152 lines
No EOL
5.2 KiB
Go
152 lines
No EOL
5.2 KiB
Go
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"})
|
|
} |