package middleware import ( "context" "net/http" "strings" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/yuxingu/digital-psychology/apps/api/pkg/response" ) const AdminIDKey ctxKey = "admin_id" const AdminTokenHeader = "Authorization" // AdminSessionResolver looks up a valid admin session by token. type AdminSessionResolver interface { ResolveAdminID(ctx context.Context, token string) (uuid.UUID, error) } // AdminAuth requires Bearer token for /admin routes. func AdminAuth(resolver AdminSessionResolver) gin.HandlerFunc { return func(c *gin.Context) { token := BearerToken(c.GetHeader(AdminTokenHeader)) if token == "" { response.Fail(c, http.StatusUnauthorized, 40101, "admin auth required") c.Abort() return } adminID, err := resolver.ResolveAdminID(c.Request.Context(), token) if err != nil || adminID == uuid.Nil { response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid") c.Abort() return } c.Set(string(AdminIDKey), adminID.String()) c.Next() } } // AdminIDFromContext returns the authenticated admin id. func AdminIDFromContext(c *gin.Context) (uuid.UUID, bool) { v, ok := c.Get(string(AdminIDKey)) if !ok { return uuid.Nil, false } id, err := uuid.Parse(v.(string)) return id, err == nil } // BearerToken extracts an opaque token from Authorization (Bearer / bearer). func BearerToken(h string) string { h = strings.TrimSpace(h) if h == "" { return "" } lower := strings.ToLower(h) if strings.HasPrefix(lower, "bearer ") { return strings.TrimSpace(h[len("bearer "):]) } return h }