package api import ( "encoding/json" "log" "net/http" "strconv" "time" "github.com/cynfo/rmm-backend/ws" ) // GET /api/v1/tasks func (h *Handler) listTasks(w http.ResponseWriter, r *http.Request) { limit := 50 offset := 0 agentID := r.URL.Query().Get("agent_id") status := r.URL.Query().Get("status") action := r.URL.Query().Get("action") if l := r.URL.Query().Get("limit"); l != "" { if n, err := strconv.Atoi(l); err == nil && n > 0 { limit = n } } if o := r.URL.Query().Get("offset"); o != "" { if n, err := strconv.Atoi(o); err == nil && n >= 0 { offset = n } } tasks, total, err := h.db.GetTasks(limit, offset, agentID, status, action) if err != nil { log.Printf("Tasks laden fehlgeschlagen: %v", err) writeError(w, http.StatusInternalServerError, "Fehler beim Laden") return } writeJSON(w, http.StatusOK, map[string]interface{}{ "data": tasks, "total": total, }) } // GET /api/v1/tasks/{id} func (h *Handler) getTask(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { writeError(w, http.StatusBadRequest, "Ungueltige Task-ID") return } task, err := h.db.GetTask(id) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Laden") return } if task == nil { writeError(w, http.StatusNotFound, "Task nicht gefunden") return } writeJSON(w, http.StatusOK, task) } // POST /api/v1/tasks func (h *Handler) createTask(hub *ws.Hub) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req struct { AgentID string `json:"agent_id"` Action string `json:"action"` ScheduledAt string `json:"scheduled_at"` WebhookURL string `json:"webhook_url"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "Ungueltige Anfrage") return } if req.AgentID == "" || req.Action == "" { writeError(w, http.StatusBadRequest, "agent_id und action sind erforderlich") return } // Validate action if req.Action != "backup" && req.Action != "update" { writeError(w, http.StatusBadRequest, "Ungueltige Aktion (backup, update)") return } // Check agent exists agent, _, err := h.db.GetAgent(req.AgentID) if err != nil || agent == nil { writeError(w, http.StatusNotFound, "Agent nicht gefunden") return } // Get user from context createdBy := "api-key" if claims, ok := r.Context().Value(UserContextKey).(*JWTClaims); ok { createdBy = claims.Username } task, err := h.db.CreateTask(req.AgentID, req.Action, req.ScheduledAt, req.WebhookURL, createdBy) if err != nil { log.Printf("Task erstellen fehlgeschlagen: %v", err) writeError(w, http.StatusInternalServerError, "Task erstellen fehlgeschlagen: "+err.Error()) return } h.auditLog(r, "task.create", "task", strconv.Itoa(task.ID), agent.Name, req.Action) // If scheduled_at is in the past or now, trigger immediate execution if req.ScheduledAt == "" { // Already scheduled for now, scheduler will pick it up within 30s } else { schedTime, err := time.Parse(time.RFC3339, req.ScheduledAt) if err == nil && !schedTime.After(time.Now()) { // In the past — scheduler picks it up } } writeJSON(w, http.StatusCreated, task) } } // POST /api/v1/tasks/{id}/cancel func (h *Handler) cancelTask(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { writeError(w, http.StatusBadRequest, "Ungueltige Task-ID") return } if err := h.db.CancelTask(id); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } h.auditLog(r, "task.cancel", "task", idStr, "", "") writeJSON(w, http.StatusOK, map[string]string{"message": "Task abgebrochen"}) } // DELETE /api/v1/tasks/{id} func (h *Handler) deleteTask(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { writeError(w, http.StatusBadRequest, "Ungueltige Task-ID") return } if err := h.db.DeleteTask(id); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } h.auditLog(r, "task.delete", "task", idStr, "", "") writeJSON(w, http.StatusOK, map[string]string{"message": "Task geloescht"}) }