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"` Name string `json:"name"` Action string `json:"action"` ScheduledAt string `json:"scheduled_at"` ScheduleType string `json:"schedule_type"` ScheduleTime string `json:"schedule_time"` ScheduleWeekday *int `json:"schedule_weekday"` ScheduleMonthday *int `json:"schedule_monthday"` Reboot bool `json:"reboot"` HealthCheck bool `json:"health_check"` HealthCheckTimeout int `json:"health_check_timeout"` WebhookURL string `json:"webhook_url"` Active *bool `json:"active"` } 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 } if req.Action != "backup" && req.Action != "update" { writeError(w, http.StatusBadRequest, "Ungueltige Aktion (backup, update)") return } agent, _, err := h.db.GetAgent(req.AgentID) if err != nil || agent == nil { writeError(w, http.StatusNotFound, "Agent nicht gefunden") return } createdBy := "api-key" if claims, ok := r.Context().Value(UserContextKey).(*JWTClaims); ok { createdBy = claims.Username } active := true if req.Active != nil { active = *req.Active } task, err := h.db.CreateTask( req.AgentID, req.Action, req.ScheduledAt, req.WebhookURL, createdBy, req.Name, req.ScheduleType, req.ScheduleTime, req.ScheduleWeekday, req.ScheduleMonthday, req.Reboot, req.HealthCheck, req.HealthCheckTimeout, active, ) 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 once and scheduled_at is in the past or now, scheduler picks it up within 30s if req.ScheduledAt != "" && req.ScheduleType == "once" { if schedTime, err := time.Parse(time.RFC3339, req.ScheduledAt); err == nil && !schedTime.After(time.Now()) { // scheduler will pick it up _ = schedTime } } writeJSON(w, http.StatusCreated, task) } } // PUT /api/v1/tasks/{id}/toggle func (h *Handler) toggleTask(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.ToggleTask(id) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler beim Umschalten") return } if task == nil { writeError(w, http.StatusNotFound, "Task nicht gefunden") return } h.auditLog(r, "task.toggle", "task", idStr, "", "") writeJSON(w, http.StatusOK, 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"}) } // PUT /api/v1/tasks/{id} func (h *Handler) updateTask(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 } var req struct { Name string `json:"name"` ScheduleType string `json:"schedule_type"` ScheduleTime string `json:"schedule_time"` ScheduleWeekday *int `json:"schedule_weekday"` ScheduleMonthday *int `json:"schedule_monthday"` Reboot bool `json:"reboot"` HealthCheck bool `json:"health_check"` HealthCheckTimeout int `json:"health_check_timeout"` WebhookURL string `json:"webhook_url"` Active *bool `json:"active"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "Ungueltige Anfrage") return } active := true if req.Active != nil { active = *req.Active } if req.HealthCheckTimeout <= 0 { req.HealthCheckTimeout = 600 } task, err := h.db.UpdateTask(id, req.Name, req.ScheduleType, req.ScheduleTime, req.ScheduleWeekday, req.ScheduleMonthday, req.Reboot, req.HealthCheck, req.HealthCheckTimeout, req.WebhookURL, active) if err != nil { writeError(w, http.StatusInternalServerError, "Fehler: "+err.Error()) return } if task == nil { writeError(w, http.StatusNotFound, "Task nicht gefunden") return } h.auditLog(r, "task.update", "task", idStr, "", "") writeJSON(w, http.StatusOK, task) } // SetScheduler setzt den Scheduler im Handler (fuer Run-Now) func (h *Handler) SetScheduler(s *TaskScheduler) { h.scheduler = s } // POST /api/v1/tasks/{id}/run — Task sofort ausfuehren func (h *Handler) runTaskNow() http.HandlerFunc { return func(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 || task == nil { writeError(w, http.StatusNotFound, "Task nicht gefunden") return } if h.scheduler == nil { writeError(w, http.StatusInternalServerError, "Scheduler nicht verfuegbar") return } // Sofort im Hintergrund starten go h.scheduler.executeTask(*task) h.auditLog(r, "task.run_now", "task", idStr, "", task.Action) writeJSON(w, http.StatusOK, map[string]string{"message": "Task gestartet"}) } } // 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"}) }