package middleware import ( "strings" "web-qudo-be/app/database/entity" "github.com/gofiber/fiber/v2" "github.com/google/uuid" "gorm.io/gorm" ) const ( ClientKeyHeader = "X-Client-Key" ClientContextKey = "client_id" ) // excludedPaths contains paths that don't require client key validation var excludedPaths = []string{ "/swagger/*", "/docs/*", "/users/login", "/health/*", "/clients", "/clients/*", "*/viewer/*", "/bookmarks/test-table", } // isPathExcluded checks if the given path should be excluded from client key validation func isPathExcluded(path string) bool { for _, excludedPath := range excludedPaths { if strings.HasPrefix(excludedPath, "*") && strings.HasSuffix(excludedPath, "*") { // Handle wildcard at both beginning and end (e.g., "*/viewer/*") pattern := excludedPath[1 : len(excludedPath)-1] // Remove * from both ends if strings.Contains(path, pattern) { return true } } else if strings.HasPrefix(excludedPath, "*") { // Handle wildcard at the beginning if strings.HasSuffix(path, excludedPath[1:]) { return true } } else if strings.HasSuffix(excludedPath, "*") { // Handle wildcard at the end prefix := excludedPath[:len(excludedPath)-1] if strings.HasPrefix(path, prefix) { return true } } else { // Exact match if path == excludedPath { return true } } } return false } // ClientMiddleware extracts and validates the Client Key from request headers func ClientMiddleware(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { // Check if path should be excluded from client key validation if isPathExcluded(c.Path()) { return c.Next() } // Extract Client Key from header clientKey := c.Get(ClientKeyHeader) if clientKey == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "code": 400, "messages": []string{"Client Key is required in header: " + ClientKeyHeader}, }) } // Parse UUID clientUUID, err := uuid.Parse(clientKey) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "code": 400, "messages": []string{"Invalid Client Key format"}, }) } // Validate client exists and is active var client entity.Clients if err := db.Where("id = ? AND is_active = ?", clientUUID, true).First(&client).Error; err != nil { if err == gorm.ErrRecordNotFound { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ "success": false, "code": 401, "messages": []string{"Invalid or inactive Client Key"}, }) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "code": 500, "messages": []string{"Error validating Client Key"}, }) } // Store client ID in context for use in handlers c.Locals(ClientContextKey, clientUUID) return c.Next() } } // GetClientID retrieves the client ID from the context func GetClientID(c *fiber.Ctx) *uuid.UUID { if clientID, ok := c.Locals(ClientContextKey).(uuid.UUID); ok { return &clientID } return nil } // AddExcludedPath adds a new path to the excluded paths list func AddExcludedPath(path string) { excludedPaths = append(excludedPaths, path) } // GetExcludedPaths returns the current list of excluded paths func GetExcludedPaths() []string { return excludedPaths }