package middleware import ( "fmt" "sno/internal/service" "sno/internal/types" "github.com/gofiber/fiber/v2" ) // AdminConfig holds configuration for admin middleware type AdminConfig struct { AdminService service.AdminService } // RequireRole middleware to check user roles func RequireRole(roles ...types.UserRole) fiber.Handler { return func(c *fiber.Ctx) error { // Skip role checking for OPTIONS preflight requests if c.Method() == "OPTIONS" { return c.Next() } userData := GetTelegramUser(c) if userData == nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ "success": false, "message": "User not authenticated", }) } // Check if user has any of the required roles userRole, err := getUserRole(c, userData.ID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "Failed to verify user role", }) } for _, role := range roles { if userRole == role { return c.Next() } } return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ "success": false, "message": fmt.Sprintf("Insufficient permissions. Required role: one of %v", roles), }) } } // RequireAdmin middleware to check if user is admin func RequireAdmin(config AdminConfig) fiber.Handler { return RequireRole(types.RoleAdmin) } // RequireOperator middleware to check if user is operator or admin func RequireOperator(config AdminConfig) fiber.Handler { return RequireRole(types.RoleAdmin, types.RoleOperator) } // getUserRole retrieves user role from database or cache func getUserRole(c *fiber.Ctx, userID int64) (types.UserRole, error) { // For now, we'll check if user exists in admins table // In production, you might want to cache this in Redis // Try to get from context first (cached) if role, ok := c.Locals("user_role").(types.UserRole); ok { return role, nil } // Get admin service from context adminService, ok := c.Locals("admin_service").(service.AdminService) if !ok { // Fallback: if no admin service, assume user role return types.RoleUser, nil } // Check if user is admin role, err := adminService.GetUserRole(c.Context(), userID) if err != nil { // If user not found in admins table, they are regular user return types.RoleUser, nil } // Cache role in context c.Locals("user_role", role) return role, nil } // AdminMiddleware middleware for admin routes func AdminMiddleware(config AdminConfig) fiber.Handler { return func(c *fiber.Ctx) error { // Store admin service in context for role checking c.Locals("admin_service", config.AdminService) // Apply auth middleware first return AuthMiddleware(AuthConfig{})(c) } } // WithAdminRoles applies both admin middleware and role checking func WithAdminRoles(config AdminConfig, roles ...types.UserRole) fiber.Handler { return func(c *fiber.Ctx) error { // First, apply admin middleware (includes auth) if err := AdminMiddleware(config)(c); err != nil { return err } // Then check roles return RequireRole(roles...)(c) } } // Convenience functions func IsAdmin(c *fiber.Ctx) bool { userData := GetTelegramUser(c) if userData == nil { return false } role, err := getUserRole(c, userData.ID) if err != nil { return false } return role == types.RoleAdmin } func IsOperator(c *fiber.Ctx) bool { userData := GetTelegramUser(c) if userData == nil { return false } role, err := getUserRole(c, userData.ID) if err != nil { return false } return role == types.RoleOperator || role == types.RoleAdmin } func GetCurrentUserRole(c *fiber.Ctx) (types.UserRole, error) { userData := GetTelegramUser(c) if userData == nil { return "", fmt.Errorf("user not authenticated") } return getUserRole(c, userData.ID) }