package server import ( "errors" "net/http" "galaxy/backend/internal/auth" "galaxy/backend/internal/server/handlers" "galaxy/backend/internal/server/httperr" "galaxy/backend/internal/telemetry" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" ) // InternalSessionsHandlers groups the gateway-only session handlers // under `/api/v1/internal/sessions/*`. The internal surface only // carries the per-request session lookup gateway needs to verify // signed envelopes; revocation is driven through the user surface // (self-driven) or through admin operations that call auth in-process, // not through this listener. nil *auth.Service falls back to the // Stage-3 placeholder so the contract test continues to validate the // OpenAPI envelope without booting a database. type InternalSessionsHandlers struct { svc *auth.Service logger *zap.Logger } // NewInternalSessionsHandlers constructs the handler set. svc may be // nil — in that case every handler returns 501 not_implemented, matching // the pre-Stage-5.1 placeholder. logger may also be nil; zap.NewNop is // used in that case. func NewInternalSessionsHandlers(svc *auth.Service, logger *zap.Logger) *InternalSessionsHandlers { if logger == nil { logger = zap.NewNop() } return &InternalSessionsHandlers{svc: svc, logger: logger.Named("http.internal.sessions")} } // Get handles GET /api/v1/internal/sessions/{device_session_id}. func (h *InternalSessionsHandlers) Get() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("internalSessionsGet") } return func(c *gin.Context) { deviceSessionID, err := uuid.Parse(c.Param("device_session_id")) if err != nil { httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "device_session_id must be a valid UUID") return } ctx := c.Request.Context() sess, err := h.svc.GetSession(ctx, deviceSessionID) if err != nil { if errors.Is(err, auth.ErrSessionNotFound) { httperr.Abort(c, http.StatusNotFound, httperr.CodeNotFound, "device session not found") return } h.logger.Error("internal sessions get failed", append(telemetry.TraceFieldsFromContext(ctx), zap.Error(err))..., ) httperr.Abort(c, http.StatusInternalServerError, httperr.CodeInternalError, "service error") return } c.JSON(http.StatusOK, deviceSessionToWire(sess)) } }