diff --git a/handlers/generate.go b/handlers/generate.go index da2b29c..9c30e4f 100644 --- a/handlers/generate.go +++ b/handlers/generate.go @@ -58,7 +58,10 @@ func (h *GenerateHandler) Generate(c *gin.Context) { } sendEvent := func(event string, data interface{}) { - jsonData, _ := json.Marshal(data) + jsonData, err := json.Marshal(data) + if err != nil { + jsonData = []byte(`{"error":"failed to marshal event data"}`) + } fmt.Fprintf(c.Writer, "event: %s\ndata: %s\n\n", event, jsonData) flusher.Flush() } @@ -74,7 +77,14 @@ func (h *GenerateHandler) Generate(c *gin.Context) { } // Save analysis to DB - resultJSON, _ := json.Marshal(pr) - h.db.Exec(`INSERT INTO analyses (repo_id, type, base_ref, head_ref, result) VALUES (?, 'pr_description', ?, ?, ?)`, - id, req.Base, req.Head, string(resultJSON)) + resultJSON, err := json.Marshal(pr) + if err != nil { + sendEvent("error", map[string]interface{}{"message": "marshal result: " + err.Error()}) + return + } + if _, err := h.db.Exec(`INSERT INTO analyses (repo_id, type, base_ref, head_ref, result) VALUES (?, 'pr_description', ?, ?, ?)`, + id, req.Base, req.Head, string(resultJSON)); err != nil { + sendEvent("error", map[string]interface{}{"message": "save analysis: " + err.Error()}) + return + } } diff --git a/handlers/repos.go b/handlers/repos.go index 208e53d..2f49675 100644 --- a/handlers/repos.go +++ b/handlers/repos.go @@ -43,6 +43,10 @@ func (h *ReposHandler) ListRepos(c *gin.Context) { }) } } + if err := rows.Err(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } if repos == nil { repos = []gin.H{} } @@ -61,9 +65,18 @@ func (h *ReposHandler) DeleteRepo(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - os.RemoveAll(localPath) - h.db.Exec(`DELETE FROM analyses WHERE repo_id = ?`, id) - h.db.Exec(`DELETE FROM repositories WHERE id = ?`, id) + if err := os.RemoveAll(localPath); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "remove repo dir: " + err.Error()}) + return + } + if _, err := h.db.Exec(`DELETE FROM analyses WHERE repo_id = ?`, id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "delete analyses: " + err.Error()}) + return + } + if _, err := h.db.Exec(`DELETE FROM repositories WHERE id = ?`, id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "delete repository: " + err.Error()}) + return + } c.JSON(http.StatusOK, gin.H{"ok": true}) } @@ -80,17 +93,33 @@ func (h *ReposHandler) CleanupRepos(c *gin.Context) { } defer rows.Close() var cleaned []int64 + var errs []string for rows.Next() { var id int64 var localPath string if rows.Scan(&id, &localPath) == nil { - os.RemoveAll(localPath) - h.db.Exec(`DELETE FROM analyses WHERE repo_id = ?`, id) - h.db.Exec(`DELETE FROM repositories WHERE id = ?`, id) + if err := os.RemoveAll(localPath); err != nil { + errs = append(errs, fmt.Sprintf("remove %d: %s", id, err.Error())) + continue + } + if _, err := h.db.Exec(`DELETE FROM analyses WHERE repo_id = ?`, id); err != nil { + errs = append(errs, fmt.Sprintf("delete analyses %d: %s", id, err.Error())) + } + if _, err := h.db.Exec(`DELETE FROM repositories WHERE id = ?`, id); err != nil { + errs = append(errs, fmt.Sprintf("delete repo %d: %s", id, err.Error())) + } cleaned = append(cleaned, id) } } - c.JSON(http.StatusOK, gin.H{"cleaned": cleaned}) + if err := rows.Err(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + result := gin.H{"cleaned": cleaned} + if len(errs) > 0 { + result["errors"] = errs + } + c.JSON(http.StatusOK, result) } // CloneRepo handles POST /api/repos with SSE progress events. @@ -119,7 +148,10 @@ func (h *ReposHandler) CloneRepo(c *gin.Context) { } sendEvent := func(event string, data interface{}) { - jsonData, _ := json.Marshal(data) + jsonData, err := json.Marshal(data) + if err != nil { + jsonData = []byte(`{"error":"failed to marshal event data"}`) + } fmt.Fprintf(c.Writer, "event: %s\ndata: %s\n\n", event, jsonData) flusher.Flush() } @@ -155,7 +187,11 @@ func (h *ReposHandler) CloneRepo(c *gin.Context) { sendEvent("error", map[string]interface{}{"message": "save to db: " + err.Error()}) return } - repoID, _ := res.LastInsertId() + repoID, err := res.LastInsertId() + if err != nil { + sendEvent("error", map[string]interface{}{"message": "get repo id: " + err.Error()}) + return + } sendEvent("complete", map[string]interface{}{ "repo_id": repoID, diff --git a/handlers/review.go b/handlers/review.go index 8c1a602..157403f 100644 --- a/handlers/review.go +++ b/handlers/review.go @@ -90,7 +90,10 @@ func (h *ReviewHandler) Review(c *gin.Context) { } sendEvent := func(event string, data interface{}) { - jsonData, _ := json.Marshal(data) + jsonData, err := json.Marshal(data) + if err != nil { + jsonData = []byte(`{"error":"failed to marshal event data"}`) + } fmt.Fprintf(c.Writer, "event: %s\ndata: %s\n\n", event, jsonData) flusher.Flush() } @@ -106,15 +109,25 @@ func (h *ReviewHandler) Review(c *gin.Context) { } // Save analysis to DB with full review result - resultJSON, _ := json.Marshal(reviewResult) + resultJSON, err := json.Marshal(reviewResult) + if err != nil { + sendEvent("error", map[string]interface{}{"message": "marshal result: " + err.Error()}) + return + } res, err := h.db.Exec(`INSERT INTO analyses (repo_id, type, base_ref, head_ref, result) VALUES (?, 'code_review', ?, ?, ?)`, id, req.Base, req.Head, string(resultJSON)) - if err == nil { - analysisID, _ := res.LastInsertId() - sendEvent("analysis_saved", map[string]interface{}{ - "analysis_id": analysisID, - }) + if err != nil { + sendEvent("error", map[string]interface{}{"message": "save analysis: " + err.Error()}) + return } + analysisID, err := res.LastInsertId() + if err != nil { + sendEvent("error", map[string]interface{}{"message": "get analysis id: " + err.Error()}) + return + } + sendEvent("analysis_saved", map[string]interface{}{ + "analysis_id": analysisID, + }) } // SaveNotes handles POST /api/repos/:id/review/notes — upsert a review note. @@ -202,6 +215,10 @@ func (h *ReviewHandler) ListReviews(c *gin.Context) { "created_at": createdAt, }) } + if err := rows.Err(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } if analyses == nil { analyses = []map[string]interface{}{} @@ -305,7 +322,11 @@ func (h *ReviewHandler) GeneratePDF(c *gin.Context) { } // Return PDF as download - filename := fmt.Sprintf("pr-helper-review-%s.pdf", createdAt[:10]) + dateStr := createdAt + if len(dateStr) > 10 { + dateStr = dateStr[:10] + } + filename := fmt.Sprintf("pr-helper-review-%s.pdf", dateStr) c.Header("Content-Type", "application/pdf") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) c.Data(http.StatusOK, "application/pdf", pdfBytes)