package api import ( "database/sql" "net/http" "strconv" "time" "github.com/go-chi/chi/v5" "github.com/yeniklas/terdut-server/internal/models" ) func handleListComments(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { alertID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { respond(w, http.StatusBadRequest, errResp("invalid alert id")) return } // Verify the alert exists. var exists int if err := db.QueryRowContext(r.Context(), "SELECT 1 FROM alerts WHERE id = ?", alertID).Scan(&exists); err != nil { respond(w, http.StatusNotFound, errResp("alert not found")) return } rows, err := db.QueryContext(r.Context(), ` SELECT c.id, c.alert_id, c.user_id, u.username, c.content, c.created_at FROM alert_comments c JOIN users u ON u.id = c.user_id WHERE c.alert_id = ? ORDER BY c.created_at ASC`, alertID) if err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } defer rows.Close() comments := []models.Comment{} for rows.Next() { var c models.Comment var ts int64 if err := rows.Scan(&c.ID, &c.AlertID, &c.UserID, &c.Username, &c.Content, &ts); err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } c.CreatedAt = time.Unix(ts, 0).UTC() comments = append(comments, c) } respond(w, http.StatusOK, comments) } } func handleCreateComment(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { alertID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { respond(w, http.StatusBadRequest, errResp("invalid alert id")) return } var req struct { Content string `json:"content"` } if err := decodeJSON(r, &req); err != nil { respond(w, http.StatusBadRequest, errResp("invalid request body")) return } if req.Content == "" { respond(w, http.StatusBadRequest, errResp("content is required")) return } // Verify the alert exists. var exists int if err := db.QueryRowContext(r.Context(), "SELECT 1 FROM alerts WHERE id = ?", alertID).Scan(&exists); err != nil { respond(w, http.StatusNotFound, errResp("alert not found")) return } user, _ := userFromContext(r.Context()) res, err := db.ExecContext(r.Context(), "INSERT INTO alert_comments (alert_id, user_id, content) VALUES (?, ?, ?)", alertID, user.ID, req.Content) if err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } commentID, _ := res.LastInsertId() comment := models.Comment{ ID: commentID, AlertID: alertID, UserID: user.ID, Username: user.Username, Content: req.Content, CreatedAt: time.Now().UTC(), } respond(w, http.StatusCreated, comment) } } func handleDeleteComment(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { alertID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { respond(w, http.StatusBadRequest, errResp("invalid alert id")) return } commentID, err := strconv.ParseInt(chi.URLParam(r, "commentID"), 10, 64) if err != nil { respond(w, http.StatusBadRequest, errResp("invalid comment id")) return } user, _ := userFromContext(r.Context()) res, err := db.ExecContext(r.Context(), "DELETE FROM alert_comments WHERE id = ? AND alert_id = ? AND user_id = ?", commentID, alertID, user.ID) if err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } if n, _ := res.RowsAffected(); n == 0 { respond(w, http.StatusNotFound, errResp("comment not found")) return } w.WriteHeader(http.StatusNoContent) } }