Files
digital-psychology/apps/api/internal/middleware/admin_auth.go
T
jackyu66gitandCursor 879bf70cb7
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s
feat(ECR-006): 落地运营后台 Phase A(admin API + admin-h5)
新增独立鉴权的 /api/v1/admin 与 Vue 控制台;会员授予与审计同事务,并补集成/单测。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 18:35:53 +08:00

64 lines
1.6 KiB
Go

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
}