feat: backend service

This commit is contained in:
Ilia Denisov
2026-05-06 10:14:55 +03:00
committed by GitHub
parent 3e2622757e
commit f446c6a2ac
1486 changed files with 49720 additions and 266401 deletions
@@ -0,0 +1,174 @@
package server
import (
"context"
"errors"
"net/http"
"galaxy/backend/internal/runtime"
"galaxy/backend/internal/server/handlers"
"galaxy/backend/internal/server/httperr"
"galaxy/backend/internal/telemetry"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// AdminEngineVersionsHandlers groups the engine-version-registry
// handlers under `/api/v1/admin/engine-versions/*`. The implementation swaps
// the placeholder bodies for real `*runtime.EngineVersionService`
// calls; tests that omit the service fall back to the Stage-3 501
// envelope.
type AdminEngineVersionsHandlers struct {
svc *runtime.EngineVersionService
logger *zap.Logger
}
// NewAdminEngineVersionsHandlers constructs the handler set. svc may
// be nil — in that case every handler returns 501.
func NewAdminEngineVersionsHandlers(svc *runtime.EngineVersionService, logger *zap.Logger) *AdminEngineVersionsHandlers {
if logger == nil {
logger = zap.NewNop()
}
return &AdminEngineVersionsHandlers{svc: svc, logger: logger.Named("http.admin.engine-versions")}
}
// List handles GET /api/v1/admin/engine-versions.
func (h *AdminEngineVersionsHandlers) List() gin.HandlerFunc {
if h == nil || h.svc == nil {
return handlers.NotImplemented("adminEngineVersionsList")
}
return func(c *gin.Context) {
ctx := c.Request.Context()
items, err := h.svc.List(ctx)
if err != nil {
respondEngineVersionError(c, h.logger, "admin engine-versions list", ctx, err)
return
}
c.JSON(http.StatusOK, engineVersionListToWire(items))
}
}
// Create handles POST /api/v1/admin/engine-versions.
func (h *AdminEngineVersionsHandlers) Create() gin.HandlerFunc {
if h == nil || h.svc == nil {
return handlers.NotImplemented("adminEngineVersionsCreate")
}
return func(c *gin.Context) {
var req engineVersionCreateRequestWire
if err := c.ShouldBindJSON(&req); err != nil {
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
return
}
ctx := c.Request.Context()
input := runtime.RegisterInput{Version: req.Version, ImageRef: req.ImageRef}
if req.Enabled != nil {
input.Enabled = req.Enabled
}
v, err := h.svc.Register(ctx, input)
if err != nil {
respondEngineVersionError(c, h.logger, "admin engine-versions create", ctx, err)
return
}
c.JSON(http.StatusCreated, engineVersionToWire(v))
}
}
// Update handles PATCH /api/v1/admin/engine-versions/{id}.
func (h *AdminEngineVersionsHandlers) Update() gin.HandlerFunc {
if h == nil || h.svc == nil {
return handlers.NotImplemented("adminEngineVersionsUpdate")
}
return func(c *gin.Context) {
var req engineVersionUpdateRequestWire
if err := c.ShouldBindJSON(&req); err != nil {
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
return
}
ctx := c.Request.Context()
updated, err := h.svc.Update(ctx, c.Param("id"), runtime.UpdateInput{
ImageRef: req.ImageRef,
Enabled: req.Enabled,
})
if err != nil {
respondEngineVersionError(c, h.logger, "admin engine-versions update", ctx, err)
return
}
c.JSON(http.StatusOK, engineVersionToWire(updated))
}
}
// Disable handles POST /api/v1/admin/engine-versions/{id}/disable.
func (h *AdminEngineVersionsHandlers) Disable() gin.HandlerFunc {
if h == nil || h.svc == nil {
return handlers.NotImplemented("adminEngineVersionsDisable")
}
return func(c *gin.Context) {
ctx := c.Request.Context()
updated, err := h.svc.Disable(ctx, c.Param("id"))
if err != nil {
respondEngineVersionError(c, h.logger, "admin engine-versions disable", ctx, err)
return
}
c.JSON(http.StatusOK, engineVersionToWire(updated))
}
}
func respondEngineVersionError(c *gin.Context, logger *zap.Logger, op string, ctx context.Context, err error) {
switch {
case errors.Is(err, runtime.ErrNotFound):
httperr.Abort(c, http.StatusNotFound, httperr.CodeNotFound, "engine version not found")
case errors.Is(err, runtime.ErrEngineVersionTaken):
httperr.Abort(c, http.StatusConflict, httperr.CodeConflict, "engine version already registered")
case errors.Is(err, runtime.ErrInvalidInput):
httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, err.Error())
default:
logger.Error(op+" failed",
append(telemetry.TraceFieldsFromContext(ctx), zap.Error(err))...,
)
httperr.Abort(c, http.StatusInternalServerError, httperr.CodeInternalError, "internal error")
}
}
func engineVersionToWire(v runtime.EngineVersion) engineVersionWire {
return engineVersionWire{
Version: v.Version,
ImageRef: v.ImageRef,
Enabled: v.Enabled,
CreatedAt: v.CreatedAt.UTC().Format(timestampLayout),
}
}
func engineVersionListToWire(items []runtime.EngineVersion) engineVersionListWire {
out := engineVersionListWire{Items: make([]engineVersionWire, 0, len(items))}
for _, v := range items {
out.Items = append(out.Items, engineVersionToWire(v))
}
return out
}
// engineVersionWire mirrors `EngineVersion` from openapi.yaml.
type engineVersionWire struct {
Version string `json:"version"`
ImageRef string `json:"image_ref"`
Enabled bool `json:"enabled"`
CreatedAt string `json:"created_at"`
}
// engineVersionListWire mirrors `EngineVersionList`.
type engineVersionListWire struct {
Items []engineVersionWire `json:"items"`
}
// engineVersionCreateRequestWire mirrors `EngineVersionCreateRequest`.
type engineVersionCreateRequestWire struct {
Version string `json:"version"`
ImageRef string `json:"image_ref"`
Enabled *bool `json:"enabled,omitempty"`
}
// engineVersionUpdateRequestWire mirrors `EngineVersionUpdateRequest`.
type engineVersionUpdateRequestWire struct {
ImageRef *string `json:"image_ref,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
}