From d96bb2ce8de8dcfc1562c00eeb905732a9daeacf Mon Sep 17 00:00:00 2001 From: poignatov Date: Thu, 1 Jan 2026 19:13:37 +0300 Subject: [PATCH] v2.0.6: Fix addWords handler - add user_id and improve error handling - 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 --- VERSION | 2 +- play-life-backend/main.go | 48 +++++++++++++++++++++++++++++++++----- play-life-web/package.json | 2 +- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index e010258..157e54f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.5 +2.0.6 diff --git a/play-life-backend/main.go b/play-life-backend/main.go index fe19fa9..889f341 100644 --- a/play-life-backend/main.go +++ b/play-life-backend/main.go @@ -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 + } + 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 { + 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{}{ diff --git a/play-life-web/package.json b/play-life-web/package.json index 29c0501..1a28663 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "2.0.5", + "version": "2.0.6", "type": "module", "scripts": { "dev": "vite",