feat(backend): add cron job REST API (list, add, delete, enable, disable)
This commit is contained in:
parent
8dd22efe65
commit
9ae9583be6
2 changed files with 158 additions and 0 deletions
152
web/backend/api/cron.go
Normal file
152
web/backend/api/cron.go
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/sipeed/picoclaw/pkg/config"
|
||||
"github.com/sipeed/picoclaw/pkg/cron"
|
||||
)
|
||||
|
||||
// registerCronRoutes registers cron job API routes on the ServeMux.
|
||||
func (h *Handler) registerCronRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /api/cron/jobs", h.handleListCronJobs)
|
||||
mux.HandleFunc("POST /api/cron/jobs", h.handleAddCronJob)
|
||||
mux.HandleFunc("DELETE /api/cron/jobs/{id}", h.handleDeleteCronJob)
|
||||
mux.HandleFunc("POST /api/cron/jobs/{id}/enable", h.handleEnableCronJob)
|
||||
mux.HandleFunc("POST /api/cron/jobs/{id}/disable", h.handleDisableCronJob)
|
||||
}
|
||||
|
||||
type cronJobResponse struct {
|
||||
Jobs []cron.CronJob `json:"jobs"`
|
||||
}
|
||||
|
||||
type cronAddRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
EveryMS *int64 `json:"every_ms,omitempty"`
|
||||
CronExpr string `json:"cron_expr,omitempty"`
|
||||
Message string `json:"message" binding:"required"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
}
|
||||
|
||||
// handleListCronJobs lists all cron jobs from the cron service.
|
||||
func (h *Handler) handleListCronJobs(w http.ResponseWriter, r *http.Request) {
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
storePath := cfg.WorkspacePath() + "/cron/jobs.json"
|
||||
cs := cron.NewCronService(storePath, nil)
|
||||
if err := cs.Load(); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load cron store: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jobs := cs.ListJobs(true)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(cronJobResponse{Jobs: jobs})
|
||||
}
|
||||
|
||||
// handleAddCronJob adds a new cron job.
|
||||
func (h *Handler) handleAddCronJob(w http.ResponseWriter, r *http.Request) {
|
||||
var req cronAddRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Name == "" || req.Message == "" {
|
||||
http.Error(w, "name and message are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var schedule cron.CronSchedule
|
||||
if req.EveryMS != nil {
|
||||
schedule = cron.CronSchedule{Kind: "every", EveryMS: req.EveryMS}
|
||||
} else if req.CronExpr != "" {
|
||||
schedule = cron.CronSchedule{Kind: "cron", Expr: req.CronExpr}
|
||||
} else {
|
||||
http.Error(w, "either every_ms or cron_expr must be specified", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
storePath := cfg.WorkspacePath() + "/cron/jobs.json"
|
||||
cs := cron.NewCronService(storePath, nil)
|
||||
job, err := cs.AddJob(req.Name, schedule, req.Message, req.Channel, req.To)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to add job: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{"job": job, "status": "ok"})
|
||||
}
|
||||
|
||||
// handleDeleteCronJob deletes a cron job by ID.
|
||||
func (h *Handler) handleDeleteCronJob(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
http.Error(w, "job id is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
storePath := cfg.WorkspacePath() + "/cron/jobs.json"
|
||||
cs := cron.NewCronService(storePath, nil)
|
||||
if removed := cs.RemoveJob(id); !removed {
|
||||
http.Error(w, "job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
}
|
||||
|
||||
// handleEnableCronJob enables a cron job by ID.
|
||||
func (h *Handler) handleEnableCronJob(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
http.Error(w, "job id is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
storePath := cfg.WorkspacePath() + "/cron/jobs.json"
|
||||
cs := cron.NewCronService(storePath, nil)
|
||||
if job := cs.EnableJob(id, true); job == nil {
|
||||
http.Error(w, "job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
}
|
||||
|
||||
// handleDisableCronJob disables a cron job by ID.
|
||||
func (h *Handler) handleDisableCronJob(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
http.Error(w, "job id is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
storePath := cfg.WorkspacePath() + "/cron/jobs.json"
|
||||
cs := cron.NewCronService(storePath, nil)
|
||||
if job := cs.EnableJob(id, false); job == nil {
|
||||
http.Error(w, "job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
}
|
||||
|
|
@ -107,6 +107,12 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
|||
|
||||
// WeCom QR login flow
|
||||
h.registerWecomRoutes(mux)
|
||||
|
||||
// Agent management
|
||||
h.registerAgentRoutes(mux)
|
||||
|
||||
// Cron job management
|
||||
h.registerCronRoutes(mux)
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the handler, stopping the gateway if it was started by this handler.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue