package main import ( "context" "crypto/subtle" "strconv" "sync" "github.com/gofiber/fiber/v3" ) type service struct { app *fiber.App mu sync.RWMutex data snapshots eventMu sync.Mutex requests map[string]requestRecord feedback []feedbackRecord feedbackDir string publish func(context.Context, string, []byte) (string, error) } func newService(data snapshots, secret string) *service { s := &service{data: data, app: fiber.New(fiber.Config{Immutable: true}), requests: make(map[string]requestRecord)} const prefix = "/internal/v1/dispatcher" s.app.Use(prefix, func(c fiber.Ctx) error { if subtle.ConstantTimeCompare([]byte(c.Get("X-DISPATCHER-id")), []byte(data.dispatcherID)) != 1 || subtle.ConstantTimeCompare([]byte(c.Get("X-DISPATCHER-SECRET-KEY")), []byte(secret)) != 1 { return c.Status(403).JSON(fiber.Map{"error": "unknown dispatcher"}) } return c.Next() }) serve := func(resource string) fiber.Handler { return func(c fiber.Ctx) error { s.mu.RLock() defer s.mu.RUnlock() var raw []byte switch resource { case "sip": raw = s.data.sip case "providers": raw = s.data.providers case "quota": id, err := strconv.ParseInt(c.Params("tenant_id"), 10, 64) if err != nil || id != s.data.tenantID { return c.Status(404).JSON(fiber.Map{"error": "unknown tenant"}) } raw = s.data.quota case "task": var ok bool raw, ok = s.data.tasks[c.Params("task_id")] if !ok { return c.Status(404).JSON(fiber.Map{"error": "unknown task"}) } case "tasks": var err error raw, err = s.data.page(c.Query("after")) if err != nil { return c.Status(400).JSON(fiber.Map{"error": err.Error()}) } } c.Set("Content-Type", "application/json") return c.Send(raw) } } s.app.Get(prefix+"/sip", serve("sip")) s.app.Get(prefix+"/ai-providers", serve("providers")) s.app.Get(prefix+"/tenant/:tenant_id/quota", serve("quota")) s.app.Get(prefix+"/task/:task_id", serve("task")) s.app.Get(prefix+"/tasks", serve("tasks")) s.routes() return s }