// Package middleware предоставляет middleware для HTTP-роутера. // Включает аутентификацию через JWT (AuthMiddleware), проверку ролей // (RoleMiddleware), in-memory rate limiter и Redis-based rate limiter // с настройками для auth, read, write и admin эндпоинтов. package middleware import ( "context" "encoding/json" "net/http" "strings" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/services" ) type contextKey string const ( UserIDKey contextKey = "user_id" UserRoleKey contextKey = "user_role" ) func AuthMiddleware(authSvc *services.AuthService) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { writeAuthError(w, "missing authorization header") return } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { writeAuthError(w, "invalid authorization format") return } claims, err := authSvc.ValidateAccessToken(parts[1]) if err != nil { writeAuthError(w, "invalid or expired token") return } ctx := context.WithValue(r.Context(), UserIDKey, claims.UserID) ctx = context.WithValue(ctx, UserRoleKey, claims.Role) next.ServeHTTP(w, r.WithContext(ctx)) }) } } func RoleMiddleware(roles ...string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { role, _ := r.Context().Value(UserRoleKey).(string) for _, allowed := range roles { if role == allowed { next.ServeHTTP(w, r) return } } writeAuthError(w, "insufficient permissions") }) } } func GetUserID(ctx context.Context) string { v, _ := ctx.Value(UserIDKey).(string) return v } func GetUserRole(ctx context.Context) string { v, _ := ctx.Value(UserRoleKey).(string) return v } func writeAuthError(w http.ResponseWriter, msg string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(map[string]string{"error": msg}) }