Add Jobs API for background task management

- Add JobsHandler with CreateJob and GetJobStatus endpoints
- Add jobs router with POST /api/jobs and GET /api/jobs/:jobId routes
- Integrate JobsHandler into main server and router config
This commit is contained in:
2026-03-05 16:28:24 -05:00
parent 605aff104b
commit 5e97f14008
4 changed files with 102 additions and 0 deletions
+3
View File
@@ -106,6 +106,7 @@ func main() {
// Create worker for background tasks // Create worker for background tasks
worker := services.NewWorker(3) worker := services.NewWorker(3)
services.WorkerInstance = worker
koreaderHandler := handlers.NewKOReaderHandler(queries, connManager, queueProcessor) koreaderHandler := handlers.NewKOReaderHandler(queries, connManager, queueProcessor)
wsHandler := handlers.NewWSHandler(queries, connManager, cfg.JWTSecret, deviceAuthMiddleware) wsHandler := handlers.NewWSHandler(queries, connManager, cfg.JWTSecret, deviceAuthMiddleware)
@@ -123,6 +124,7 @@ func main() {
dashboardHandler := handlers.NewDashboardHandler(queries) dashboardHandler := handlers.NewDashboardHandler(queries)
mediaHandler := handlers.NewMediaHandler(queries, libraryService, worker) mediaHandler := handlers.NewMediaHandler(queries, libraryService, worker)
matchingHandler := handlers.NewMatchingHandler(queries, connManager) matchingHandler := handlers.NewMatchingHandler(queries, connManager)
jobsHandler := handlers.NewJobsHandler(queries, worker)
e := echo.New() e := echo.New()
@@ -177,6 +179,7 @@ func main() {
ConnManager: connManager, ConnManager: connManager,
QueueProcessor: queueProcessor, QueueProcessor: queueProcessor,
DeviceAuthMiddleware: deviceAuthMiddleware, DeviceAuthMiddleware: deviceAuthMiddleware,
JobsHandler: jobsHandler,
LoginTracker: loginAttemptTracker, LoginTracker: loginAttemptTracker,
} }
+87
View File
@@ -0,0 +1,87 @@
package handlers
import (
"net/http"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"bookhoard/internal/database"
"bookhoard/internal/services"
)
type JobsHandler struct {
db *database.Queries
worker *services.Worker
}
func NewJobsHandler(db *database.Queries, worker *services.Worker) *JobsHandler {
return &JobsHandler{
db: db,
worker: worker,
}
}
// CreateJob creates a new job based on type
func (h *JobsHandler) CreateJob(c echo.Context) error {
var req struct {
Type string `json:"type"`
Params map[string]interface{} `json:"params"`
}
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid request body",
})
}
// Validate job type
var jobType services.JobType
switch req.Type {
case "import", "convert", "thumbnails", "backup", "analytics", "sync":
jobType = services.JobType(req.Type)
default:
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid job type",
})
}
// Add database to params
req.Params["db"] = h.db
// Create job
job := &services.Job{
ID: uuid.New().String(),
Type: jobType,
Params: req.Params,
Status: services.JobStatusPending,
}
// Enqueue job
if err := h.worker.EnqueueJob(job); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": "Failed to enqueue job",
})
}
return c.JSON(http.StatusAccepted, map[string]interface{}{
"message": "Job created",
"job_id": job.ID,
"type": req.Type,
"status": "pending",
})
}
// GetJobStatus returns the status of a specific job
func (h *JobsHandler) GetJobStatus(c echo.Context) error {
jobID := c.Param("jobId")
result, exists := h.worker.GetJobStatus(jobID)
if !exists {
return c.JSON(http.StatusNotFound, map[string]string{
"error": "Job not found",
})
}
return c.JSON(http.StatusOK, result)
}
+10
View File
@@ -0,0 +1,10 @@
package router
func registerJobRoutes(cfg *Config) {
e := cfg.Echo
jwtMiddleware := createJWTMiddleware(cfg)
protected := e.Group("/api", jwtMiddleware)
jobs := protected.Group("/jobs")
jobs.POST("", cfg.JobsHandler.CreateJob)
jobs.GET("/:jobId", cfg.JobsHandler.GetJobStatus)
}
+2
View File
@@ -61,6 +61,7 @@ type Config struct {
DeviceAuthMiddleware *middleware.DeviceAuthMiddleware DeviceAuthMiddleware *middleware.DeviceAuthMiddleware
LoginTracker *ratelimit.LoginAttemptTracker LoginTracker *ratelimit.LoginAttemptTracker
ScannerHandler *handlers.Handler ScannerHandler *handlers.Handler
JobsHandler *handlers.JobsHandler
} }
// createJWTMiddleware creates a JWT middleware with proper user context setup // createJWTMiddleware creates a JWT middleware with proper user context setup
@@ -217,6 +218,7 @@ func RegisterRoutes(cfg *Config) *handlers.Handler {
registerConflictRoutes(cfg) registerConflictRoutes(cfg)
registerAnalyticsRoutes(cfg) registerAnalyticsRoutes(cfg)
registerQueueRoutes(cfg) registerQueueRoutes(cfg)
registerJobRoutes(cfg)
registerOPDSRoutes(cfg) registerOPDSRoutes(cfg)
registerWebSocketRoutes(cfg) registerWebSocketRoutes(cfg)
registerFrontendRoutes(cfg) registerFrontendRoutes(cfg)