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

- 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:
poignatov
2026-01-01 19:13:37 +03:00
parent 6f77f0643c
commit d96bb2ce8d
3 changed files with 44 additions and 8 deletions

View File

@@ -1 +1 @@
2.0.5
2.0.6

View File

@@ -861,12 +861,16 @@ func (a *App) addWordsHandler(w http.ResponseWriter, r *http.Request) {
var req WordsRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
log.Printf("Error decoding addWords request: %v", err)
sendErrorWithCORS(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("addWords: user_id=%d, words_count=%d", userID, len(req.Words))
tx, err := a.DB.Begin()
if err != nil {
log.Printf("Error beginning transaction: %v", err)
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -875,52 +879,84 @@ func (a *App) addWordsHandler(w http.ResponseWriter, r *http.Request) {
// Create default dictionary for user if needed
var defaultDictID int
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
`, userID).Scan(&defaultDictID)
if err == sql.ErrNoRows {
// Create default dictionary for user
log.Printf("Creating default dictionary for user_id=%d", userID)
err = tx.QueryRow(`
INSERT INTO dictionaries (name, user_id) VALUES ('Все слова', $1) RETURNING id
`, userID).Scan(&defaultDictID)
if err != nil {
log.Printf("Error creating default dictionary: %v", err)
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
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(`
INSERT INTO words (name, translation, description, dictionary_id)
VALUES ($1, $2, $3, COALESCE($4, $5))
INSERT INTO words (name, translation, description, dictionary_id, user_id)
VALUES ($1, $2, $3, COALESCE($4, $5), $6)
RETURNING id
`)
if err != nil {
log.Printf("Error preparing insert statement: %v", err)
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
return
}
defer stmt.Close()
var addedCount int
for _, wordReq := range req.Words {
for i, wordReq := range req.Words {
var id int
dictionaryID := defaultDictID
if wordReq.DictionaryID != nil {
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
}
err := stmt.QueryRow(wordReq.Name, wordReq.Translation, wordReq.Description, wordReq.DictionaryID, dictionaryID).Scan(&id)
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, userID).Scan(&id)
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)
return
}
addedCount++
log.Printf("Successfully added word id=%d: name='%s', dict_id=%d", id, wordReq.Name, dictionaryID)
}
if err := tx.Commit(); err != nil {
log.Printf("Error committing transaction: %v", err)
sendErrorWithCORS(w, err.Error(), http.StatusInternalServerError)
return
}
log.Printf("Successfully added %d words for user_id=%d", addedCount, userID)
setCORSHeaders(w)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{

View File

@@ -1,6 +1,6 @@
{
"name": "play-life-web",
"version": "2.0.5",
"version": "2.0.6",
"type": "module",
"scripts": {
"dev": "vite",