110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/model"
|
|
"git.cloudinit.dev/coreci/orca/internal/store"
|
|
)
|
|
|
|
// handleJobsCollection handles /v1/jobs.
|
|
// - GET → list all jobs
|
|
// - POST → not yet supported (job submission is CLI-only in v0.1)
|
|
func (s *Server) handleJobsCollection(w http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
jobs, err := store.NewJobRepo(s.db).List(ctx)
|
|
if err != nil {
|
|
s.log.Error("list jobs",
|
|
slog.String("component", "daemon"),
|
|
slog.String("error", err.Error()))
|
|
writeError(w, http.StatusInternalServerError, "failed to list jobs")
|
|
return
|
|
}
|
|
if jobs == nil {
|
|
jobs = []*model.Job{}
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"jobs": jobs, "count": len(jobs)})
|
|
|
|
case http.MethodPost:
|
|
// Job submission via HTTP is intentionally not exposed in v0.1.
|
|
// The CLI submits jobs to the local store directly; the daemon
|
|
// exists for observability and lifecycle control.
|
|
writeError(w, http.StatusNotImplemented, "job submission via API is not supported in v0.1; use 'orca job run'")
|
|
|
|
default:
|
|
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
}
|
|
}
|
|
|
|
// handleJobsItem handles /v1/jobs/{id} and /v1/jobs/{id}/tasks.
|
|
// - GET /v1/jobs/{id} → job details
|
|
// - GET /v1/jobs/{id}/tasks → tasks for a job
|
|
func (s *Server) handleJobsItem(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Path is /v1/jobs/{id} or /v1/jobs/{id}/tasks
|
|
path := strings.TrimPrefix(r.URL.Path, "/v1/jobs/")
|
|
parts := strings.Split(path, "/")
|
|
if len(parts) == 0 || parts[0] == "" {
|
|
writeError(w, http.StatusBadRequest, "job id required")
|
|
return
|
|
}
|
|
id := parts[0]
|
|
if err := validateID(id); err != nil {
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
// /v1/jobs/{id}/tasks
|
|
if len(parts) == 2 && parts[1] == "tasks" {
|
|
tasks, err := store.NewTaskRepo(s.db).ListByJob(ctx, id)
|
|
if err != nil {
|
|
s.log.Error("list tasks for job",
|
|
slog.String("component", "daemon"),
|
|
slog.String("job_id", id),
|
|
slog.String("error", err.Error()))
|
|
writeError(w, http.StatusInternalServerError, "failed to list tasks")
|
|
return
|
|
}
|
|
if tasks == nil {
|
|
tasks = []*model.Task{}
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"tasks": tasks, "count": len(tasks), "job_id": id})
|
|
return
|
|
}
|
|
|
|
// /v1/jobs/{id} (with no further path)
|
|
if len(parts) != 1 {
|
|
writeError(w, http.StatusNotFound, "not found")
|
|
return
|
|
}
|
|
job, err := store.NewJobRepo(s.db).Get(ctx, id)
|
|
if err != nil {
|
|
if errors.Is(err, store.ErrNotFound) {
|
|
writeError(w, http.StatusNotFound, "job not found")
|
|
return
|
|
}
|
|
s.log.Error("get job",
|
|
slog.String("component", "daemon"),
|
|
slog.String("job_id", id),
|
|
slog.String("error", err.Error()))
|
|
writeError(w, http.StatusInternalServerError, "failed to get job")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, job)
|
|
}
|