package middleware import ( "context" "net/http" "strings" "apps/backend/internal/domain" "apps/backend/internal/module/ai" memberDomain "apps/backend/internal/module/member/domain" "apps/backend/internal/module/permission" tokenDomain "apps/backend/internal/module/token/domain" "apps/backend/internal/response" ) type ctxKey int const ( ctxUID ctxKey = iota + 1 ctxRoles ctxMember ) func UIDFrom(ctx context.Context) (int64, bool) { v, ok := ctx.Value(ctxUID).(int64) return v, ok } func RolesFrom(ctx context.Context) []string { v, _ := ctx.Value(ctxRoles).([]string) return v } // AuthMiddleware validates Bearer access JWT. func AuthMiddleware(issuer tokenDomain.Issuer, store memberDomain.Repository) func(http.HandlerFunc) http.HandlerFunc { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { h := r.Header.Get("Authorization") if h == "" || !strings.HasPrefix(strings.ToLower(h), "bearer ") { response.Fail(w, http.StatusUnauthorized, 401001, "missing authorization", nil) return } token := strings.TrimSpace(h[7:]) claims, err := issuer.ParseAccess(token) if err != nil { response.Fail(w, http.StatusUnauthorized, 401002, "invalid or expired token", nil) return } m, err := store.FindByUID(r.Context(), claims.UID) if err != nil { response.Fail(w, http.StatusUnauthorized, 401003, "member not found", nil) return } if m.IsSuspended() { response.Fail(w, http.StatusForbidden, 403001, "account suspended", nil) return } ctx := context.WithValue(r.Context(), ctxUID, m.UID) ctx = context.WithValue(ctx, ctxRoles, m.Roles) ctx = context.WithValue(ctx, ctxMember, m) ctx = context.WithValue(ctx, domain.UIDCode, m.UID) // LLM 系統 prompt 語言:會員 preferred_language > Accept-Language > 繁中 ctx = ai.WithResponseLanguage(ctx, ai.PickLanguage(m.PreferredLanguage, r.Header.Get("Accept-Language"))) next(w, r.WithContext(ctx)) } } } // AdminMiddleware requires permission.AdminAccess(admin 角色預設授予). func AdminMiddleware(next http.HandlerFunc) http.HandlerFunc { return RequirePermission(permission.AdminAccess)(next) } // RequirePermission 檢查 JWT roles 是否具備指定能力點(須在 AuthJWT 之後). func RequirePermission(code string) func(http.HandlerFunc) http.HandlerFunc { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { roles := RolesFrom(r.Context()) if !permission.Has(roles, code) { response.Fail(w, http.StatusForbidden, 403002, "permission denied: "+code, nil) return } next(w, r) } } } func MemberFrom(ctx context.Context) (*memberDomain.Member, bool) { m, ok := ctx.Value(ctxMember).(*memberDomain.Member) return m, ok }