package main import ( "bytes" "encoding/json" "errors" "io" "git.ipao.vip/rogee/creator-hub/internal/phasea" "github.com/gofiber/fiber/v3" ) type accountRequest struct { ID string `json:"id"` ProfileID string `json:"profile_id"` CredentialReference struct { ID string `json:"id"` Provider string `json:"provider"` Key string `json:"key"` } `json:"credential_reference"` } func registerPhaseA(app *fiber.App, store *phasea.Store) { app.Post("/api/phase-a/accounts", func(c fiber.Ctx) error { var input accountRequest if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } err := store.CreateAccount(c.Context(), phasea.Account{ ID: input.ID, ProfileID: input.ProfileID, CredentialReferenceID: input.CredentialReference.ID, CredentialProvider: input.CredentialReference.Provider, CredentialKey: input.CredentialReference.Key, }) if err != nil { return phaseAError(c, err) } return c.Status(fiber.StatusCreated).JSON(map[string]string{"id": input.ID, "profile_id": input.ProfileID}) }) app.Post("/api/phase-a/accounts/:id/pause", func(c fiber.Ctx) error { if err := store.PauseAccount(c.Context(), c.Params("id")); err != nil { return phaseAError(c, err) } return c.SendStatus(fiber.StatusNoContent) }) app.Post("/api/phase-a/runtimes", func(c fiber.Ctx) error { var input phasea.RuntimeBinding if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } if err := store.BindRuntime(c.Context(), input); err != nil { return phaseAError(c, err) } return c.Status(fiber.StatusCreated).JSON(map[string]string{"id": input.ID}) }) app.Delete("/api/phase-a/runtimes/:id", func(c fiber.Ctx) error { if err := store.ReleaseRuntime(c.Context(), c.Params("id")); err != nil { return phaseAError(c, err) } return c.SendStatus(fiber.StatusNoContent) }) app.Post("/api/phase-a/drafts", func(c fiber.Ctx) error { var input phasea.Draft if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } if err := store.CreateDraft(c.Context(), input); err != nil { return phaseAError(c, err) } return c.Status(fiber.StatusCreated).JSON(map[string]string{"id": input.ID}) }) app.Post("/api/phase-a/confirmations", func(c fiber.Ctx) error { var input phasea.Confirmation if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } if err := store.Confirm(c.Context(), input); err != nil { return phaseAError(c, err) } return c.Status(fiber.StatusCreated).JSON(map[string]string{"id": input.ID}) }) app.Post("/api/phase-a/tasks", func(c fiber.Ctx) error { var input phasea.Task if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } task, inserted, err := store.Enqueue(c.Context(), input) if err != nil { return phaseAError(c, err) } status := fiber.StatusOK if inserted { status = fiber.StatusCreated } return c.Status(status).JSON(task) }) app.Post("/api/phase-a/tasks/:id/cancel", func(c fiber.Ctx) error { if err := store.CancelTask(c.Context(), c.Params("id")); err != nil { return phaseAError(c, err) } return c.SendStatus(fiber.StatusNoContent) }) app.Post("/api/phase-a/mock/execute", func(c fiber.Ctx) error { var input struct { WorkerID string `json:"worker_id"` Outcome string `json:"outcome"` } if err := decodePhaseA(c, &input); err != nil { return phaseAError(c, err) } execution, err := store.ExecuteMock(c.Context(), input.WorkerID, input.Outcome) if err != nil { return phaseAError(c, err) } return c.JSON(execution) }) app.Get("/api/phase-a/audit", func(c fiber.Ctx) error { events, err := store.Audit(c.Context()) if err != nil { return phaseAError(c, err) } return c.JSON(events) }) } func decodePhaseA(c fiber.Ctx, destination any) error { decoder := json.NewDecoder(bytes.NewReader(c.Body())) decoder.DisallowUnknownFields() if err := decoder.Decode(destination); err != nil { return phasea.ErrInvalid } if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) { return phasea.ErrInvalid } return nil } func phaseAError(c fiber.Ctx, err error) error { status := fiber.StatusInternalServerError message := "phase A operation failed" switch { case errors.Is(err, phasea.ErrInvalid): status, message = fiber.StatusBadRequest, phasea.ErrInvalid.Error() case errors.Is(err, phasea.ErrConflict): status, message = fiber.StatusConflict, phasea.ErrConflict.Error() case errors.Is(err, phasea.ErrNotFound): status, message = fiber.StatusNotFound, phasea.ErrNotFound.Error() } return c.Status(status).JSON(map[string]string{"error": message}) }