Files
creator-hub/cmd/control-plane/phasea.go
T

190 lines
5.7 KiB
Go

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"`
Platform string `json:"platform"`
PlatformAccountKey string `json:"platform_account_key"`
AuthorizationKind string `json:"authorization_kind"`
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, runtimeStore runtimeStopStore) {
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)
}
account := phasea.Account{
ID: input.ID, Platform: input.Platform, PlatformAccountKey: input.PlatformAccountKey,
AuthorizationKind: input.AuthorizationKind, CredentialKey: input.CredentialReference.Key,
CredentialReference: phasea.CredentialReference{ID: input.CredentialReference.ID, Provider: input.CredentialReference.Provider},
AuthorizationStatus: "authorized", RuntimeStatus: "paused", Version: 1,
}
if err := store.CreateAccount(c.Context(), account); err != nil {
return phaseAError(c, err)
}
return c.Status(fiber.StatusCreated).JSON(account)
})
app.Get("/api/phase-a/accounts", func(c fiber.Ctx) error {
accounts, err := store.ListAccounts(c.Context())
if err != nil {
return phaseAError(c, err)
}
return c.JSON(accounts)
})
app.Get("/api/phase-a/accounts/:id", func(c fiber.Ctx) error {
account, err := store.GetAccount(c.Context(), c.Params("id"))
if err != nil {
return phaseAError(c, err)
}
return c.JSON(account)
})
app.Post("/api/phase-a/accounts/:id/pause", func(c fiber.Ctx) error {
runtimeOperations.Lock()
defer runtimeOperations.Unlock()
if err := store.PauseAccount(c.Context(), c.Params("id")); err != nil {
return phaseAError(c, err)
}
if runtimeStore != nil {
if err := stopAccountRuntime(c.Context(), runtimeStore, c.Params("id")); err != nil {
return hubError(c, err)
}
}
return c.SendStatus(fiber.StatusNoContent)
})
app.Post("/api/phase-a/accounts/:id/resume", func(c fiber.Ctx) error {
runtimeOperations.Lock()
defer runtimeOperations.Unlock()
if err := store.ResumeAccount(c.Context(), c.Params("id")); err != nil {
return phaseAError(c, err)
}
return c.SendStatus(fiber.StatusNoContent)
})
app.Post("/api/phase-a/accounts/:id/revoke", func(c fiber.Ctx) error {
runtimeOperations.Lock()
defer runtimeOperations.Unlock()
if err := store.RevokeAccount(c.Context(), c.Params("id")); err != nil {
return phaseAError(c, err)
}
if runtimeStore != nil {
if err := stopAccountRuntime(c.Context(), runtimeStore, c.Params("id")); err != nil {
return hubError(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})
}