package handlers import ( "encoding/json" "log/slog" "net/http" "strings" "github.com/go-chi/chi/v5" "git.unkin.net/unkin/forgebot/internal/database" "git.unkin.net/unkin/forgebot/internal/provider/gitea" "git.unkin.net/unkin/forgebot/pkg/models" ) type TasksHandler struct { db *database.DB provider *gitea.Client } func NewTasksHandler(db *database.DB, provider *gitea.Client) *TasksHandler { return &TasksHandler{db: db, provider: provider} } func (h *TasksHandler) List(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") repository := r.URL.Query().Get("repository") tasks, err := h.db.ListTasks(r.Context(), status, repository) if err != nil { slog.Error("failed to list tasks", "error", err) http.Error(w, "internal error", http.StatusInternalServerError) return } if tasks == nil { tasks = []models.Task{} } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(tasks) } func (h *TasksHandler) Create(w http.ResponseWriter, r *http.Request) { var req models.CreateTaskRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body", http.StatusBadRequest) return } if req.Command == "" || req.Repository == "" { http.Error(w, "command and repository are required", http.StatusBadRequest) return } if !models.ValidCommands[req.Command] { http.Error(w, "invalid command", http.StatusBadRequest) return } task, err := h.db.CreateTask(r.Context(), req) if err != nil { slog.Error("failed to create task", "error", err) http.Error(w, "internal error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(task) } func (h *TasksHandler) Get(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") task, err := h.db.GetTask(r.Context(), id) if err != nil { http.Error(w, "task not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(task) } func (h *TasksHandler) UpdateStatus(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var req models.UpdateTaskRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body", http.StatusBadRequest) return } if err := h.db.UpdateTaskStatus(r.Context(), id, req); err != nil { slog.Error("failed to update task", "error", err, "id", id) http.Error(w, "internal error", http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } func (h *TasksHandler) PostComment(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var req models.CommentRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body", http.StatusBadRequest) return } task, err := h.db.GetTask(r.Context(), id) if err != nil { http.Error(w, "task not found", http.StatusNotFound) return } parts := strings.SplitN(task.Repository, "/", 2) if len(parts) != 2 { http.Error(w, "invalid repository format", http.StatusInternalServerError) return } issueNum := task.IssueNumber if task.PRNumber > 0 { issueNum = task.PRNumber } if err := h.provider.PostComment(parts[0], parts[1], issueNum, req.Body); err != nil { slog.Error("failed to post comment", "error", err, "task", id) http.Error(w, "failed to post comment", http.StatusBadGateway) return } w.WriteHeader(http.StatusNoContent) }