feat: add job, runner, and DAG API handlers with routes

This commit is contained in:
Forbes
2026-02-14 13:19:02 -06:00
parent df073709ce
commit 2732554cd2
4 changed files with 763 additions and 3 deletions

View File

@@ -150,6 +150,11 @@ func NewRouter(server *Server, logger zerolog.Logger) http.Handler {
r.Get("/bom/export.csv", server.HandleExportBOMCSV)
r.Get("/bom/export.ods", server.HandleExportBOMODS)
// DAG (read: viewer, write: editor)
r.Get("/dag", server.HandleGetDAG)
r.Get("/dag/forward-cone/{nodeKey}", server.HandleGetForwardCone)
r.Get("/dag/dirty", server.HandleGetDirtySubgraph)
r.Group(func(r chi.Router) {
r.Use(server.RequireWritable)
r.Use(server.RequireRole(auth.RoleEditor))
@@ -169,6 +174,8 @@ func NewRouter(server *Server, logger zerolog.Logger) http.Handler {
r.Post("/bom/merge", server.HandleMergeBOM)
r.Put("/bom/{childPartNumber}", server.HandleUpdateBOMEntry)
r.Delete("/bom/{childPartNumber}", server.HandleDeleteBOMEntry)
r.Put("/dag", server.HandleSyncDAG)
r.Post("/dag/mark-dirty/{nodeKey}", server.HandleMarkDirty)
})
})
})
@@ -201,6 +208,39 @@ func NewRouter(server *Server, logger zerolog.Logger) http.Handler {
r.Post("/sheets/diff", server.HandleSheetDiff)
})
// Jobs (read: viewer, write: editor)
r.Route("/jobs", func(r chi.Router) {
r.Get("/", server.HandleListJobs)
r.Get("/{jobID}", server.HandleGetJob)
r.Get("/{jobID}/logs", server.HandleGetJobLogs)
r.Group(func(r chi.Router) {
r.Use(server.RequireWritable)
r.Use(server.RequireRole(auth.RoleEditor))
r.Post("/", server.HandleCreateJob)
r.Post("/{jobID}/cancel", server.HandleCancelJob)
})
})
// Job definitions (read: viewer, reload: admin)
r.Route("/job-definitions", func(r chi.Router) {
r.Get("/", server.HandleListJobDefinitions)
r.Get("/{name}", server.HandleGetJobDefinition)
r.Group(func(r chi.Router) {
r.Use(server.RequireRole(auth.RoleAdmin))
r.Post("/reload", server.HandleReloadJobDefinitions)
})
})
// Runners (admin)
r.Route("/runners", func(r chi.Router) {
r.Use(server.RequireRole(auth.RoleAdmin))
r.Get("/", server.HandleListRunners)
r.Post("/", server.HandleRegisterRunner)
r.Delete("/{runnerID}", server.HandleDeleteRunner)
})
// Part number generation (editor)
r.Group(func(r chi.Router) {
r.Use(server.RequireWritable)
@@ -209,6 +249,19 @@ func NewRouter(server *Server, logger zerolog.Logger) http.Handler {
})
})
// Runner-facing API (runner token auth, not user auth)
r.Route("/api/runner", func(r chi.Router) {
r.Use(server.RequireRunnerAuth)
r.Post("/heartbeat", server.HandleRunnerHeartbeat)
r.Post("/claim", server.HandleRunnerClaim)
r.Post("/jobs/{jobID}/start", server.HandleRunnerStartJob)
r.Put("/jobs/{jobID}/progress", server.HandleRunnerUpdateProgress)
r.Post("/jobs/{jobID}/complete", server.HandleRunnerCompleteJob)
r.Post("/jobs/{jobID}/fail", server.HandleRunnerFailJob)
r.Post("/jobs/{jobID}/log", server.HandleRunnerAppendLog)
r.Put("/jobs/{jobID}/dag", server.HandleRunnerSyncDAG)
})
// React SPA — serve from web/dist at root, fallback to index.html
if info, err := os.Stat("web/dist"); err == nil && info.IsDir() {
spa := http.FileServerFS(os.DirFS("web/dist"))