v2.0.6: Fix addWords handler - add user_id and improve error handling
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 40s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 40s
- Added user_id to words insertion (was missing, causing 500 errors) - Fixed default dictionary query (removed incorrect id=0 condition) - Added dictionary ownership validation before inserting words - Added comprehensive logging for debugging addWords operations
This commit is contained in:
@@ -861,12 +861,16 @@ func (a *App) addWordsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var req WordsRequest
|
var req WordsRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
log.Printf("Error decoding addWords request: %v", err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusBadRequest)
|
sendErrorWithCORS(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("addWords: user_id=%d, words_count=%d", userID, len(req.Words))
|
||||||
|
|
||||||
tx, err := a.DB.Begin()
|
tx, err := a.DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error beginning transaction: %v", err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -875,52 +879,84 @@ func (a *App) addWordsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Create default dictionary for user if needed
|
// Create default dictionary for user if needed
|
||||||
var defaultDictID int
|
var defaultDictID int
|
||||||
err = tx.QueryRow(`
|
err = tx.QueryRow(`
|
||||||
SELECT id FROM dictionaries WHERE user_id = $1 AND id = 0
|
|
||||||
UNION ALL
|
|
||||||
SELECT id FROM dictionaries WHERE user_id = $1 ORDER BY id LIMIT 1
|
SELECT id FROM dictionaries WHERE user_id = $1 ORDER BY id LIMIT 1
|
||||||
`, userID).Scan(&defaultDictID)
|
`, userID).Scan(&defaultDictID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// Create default dictionary for user
|
// Create default dictionary for user
|
||||||
|
log.Printf("Creating default dictionary for user_id=%d", userID)
|
||||||
err = tx.QueryRow(`
|
err = tx.QueryRow(`
|
||||||
INSERT INTO dictionaries (name, user_id) VALUES ('Все слова', $1) RETURNING id
|
INSERT INTO dictionaries (name, user_id) VALUES ('Все слова', $1) RETURNING id
|
||||||
`, userID).Scan(&defaultDictID)
|
`, userID).Scan(&defaultDictID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error creating default dictionary: %v", err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Printf("Created default dictionary id=%d for user_id=%d", defaultDictID, userID)
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("Error finding default dictionary: %v", err)
|
||||||
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Printf("Using default dictionary id=%d for user_id=%d", defaultDictID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := tx.Prepare(`
|
stmt, err := tx.Prepare(`
|
||||||
INSERT INTO words (name, translation, description, dictionary_id)
|
INSERT INTO words (name, translation, description, dictionary_id, user_id)
|
||||||
VALUES ($1, $2, $3, COALESCE($4, $5))
|
VALUES ($1, $2, $3, COALESCE($4, $5), $6)
|
||||||
RETURNING id
|
RETURNING id
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error preparing insert statement: %v", err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
|
|
||||||
var addedCount int
|
var addedCount int
|
||||||
for _, wordReq := range req.Words {
|
for i, wordReq := range req.Words {
|
||||||
var id int
|
var id int
|
||||||
dictionaryID := defaultDictID
|
dictionaryID := defaultDictID
|
||||||
if wordReq.DictionaryID != nil {
|
if wordReq.DictionaryID != nil {
|
||||||
dictionaryID = *wordReq.DictionaryID
|
dictionaryID = *wordReq.DictionaryID
|
||||||
|
// Проверяем, что словарь принадлежит пользователю
|
||||||
|
var dictUserID int
|
||||||
|
err := tx.QueryRow(`
|
||||||
|
SELECT user_id FROM dictionaries WHERE id = $1
|
||||||
|
`, dictionaryID).Scan(&dictUserID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
log.Printf("Dictionary %d not found for word %d", dictionaryID, i)
|
||||||
|
sendErrorWithCORS(w, fmt.Sprintf("Dictionary %d not found", dictionaryID), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("Error checking dictionary ownership: %v", err)
|
||||||
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dictUserID != userID {
|
||||||
|
log.Printf("Dictionary %d belongs to user %d, but request from user %d", dictionaryID, dictUserID, userID)
|
||||||
|
sendErrorWithCORS(w, fmt.Sprintf("Dictionary %d does not belong to user", dictionaryID), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err := stmt.QueryRow(wordReq.Name, wordReq.Translation, wordReq.Description, wordReq.DictionaryID, dictionaryID).Scan(&id)
|
err := stmt.QueryRow(wordReq.Name, wordReq.Translation, wordReq.Description, wordReq.DictionaryID, dictionaryID, userID).Scan(&id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error inserting word %d (name='%s', dict_id=%d, user_id=%d): %v", i, wordReq.Name, dictionaryID, userID, err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addedCount++
|
addedCount++
|
||||||
|
log.Printf("Successfully added word id=%d: name='%s', dict_id=%d", id, wordReq.Name, dictionaryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
|
log.Printf("Error committing transaction: %v", err)
|
||||||
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Successfully added %d words for user_id=%d", addedCount, userID)
|
||||||
|
|
||||||
setCORSHeaders(w)
|
setCORSHeaders(w)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "2.0.5",
|
"version": "2.0.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user