190 lines
5.7 KiB
Go
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})
|
|
}
|