monorepo/cloud/maplefile-backend/internal/interface/http/middleware/utils.go

111 lines
4.3 KiB
Go

// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/http/middleware/utils.go
package middleware
import (
"regexp"
"go.uber.org/zap"
)
type protectedRoute struct {
pattern string
regex *regexp.Regexp
}
var (
exactPaths = make(map[string]bool)
patternRoutes []protectedRoute
)
func init() {
// Exact matches
exactPaths = map[string]bool{
"/api/v1/me": true,
"/api/v1/me/delete": true,
"/api/v1/me/blocked-emails": true,
"/api/v1/dashboard": true,
"/api/v1/collections": true,
"/api/v1/collections/filtered": true,
"/api/v1/collections/root": true,
"/api/v1/collections/shared": true,
"/api/v1/collections/sync": true, // Sync collections endpoint
"/api/v1/files": true,
"/api/v1/files/pending": true, // Three-step workflow file-create endpoint: Start
"/api/v1/files/recent": true,
"/api/v1/files/sync": true, // Sync files endpoint
"/api/v1/files/delete-multiple": true, // Delete multiple files endpoint
"/api/v1/invites/send-email": true, // Send invitation email to non-registered user
"/api/v1/tags": true, // List and create tags
"/api/v1/tags/search": true, // Search by tags
"/iam/api/v1/users/lookup": true, // User public key lookup (requires auth)
}
// Pattern matches
patterns := []string{
// Blocked Email patterns
"^/api/v1/me/blocked-emails/[^/]+$", // Delete specific blocked email
// Collection patterns (plural routes)
"^/api/v1/collections/[a-zA-Z0-9-]+$", // Individual collection operations
"^/api/v1/collections/[a-zA-Z0-9-]+/move$", // Move collection
"^/api/v1/collections/[a-zA-Z0-9-]+/share$", // Share collection
"^/api/v1/collections/[a-zA-Z0-9-]+/members$", // Collection members
"^/api/v1/collections/[a-zA-Z0-9-]+/members/[a-zA-Z0-9-]+$", // Remove specific member
"^/api/v1/collections/[a-zA-Z0-9-]+/archive$", // Archive collection
"^/api/v1/collections/[a-zA-Z0-9-]+/restore$", // Restore collection
"^/api/v1/collections-by-parent/[a-zA-Z0-9-]+$", // Collections by parent
// Collection patterns (singular routes for files)
"^/api/v1/collection/[a-zA-Z0-9-]+/files$", // Collection files (singular)
// File patterns (singular routes)
"^/api/v1/file/[a-zA-Z0-9-]+$", // Individual file operations
"^/api/v1/file/[a-zA-Z0-9-]+/data$", // File data
"^/api/v1/file/[a-zA-Z0-9-]+/upload-url$", // File upload URL
"^/api/v1/file/[a-zA-Z0-9-]+/download-url$", // File download URL
"^/api/v1/file/[a-zA-Z0-9-]+/complete$", // Complete file upload
"^/api/v1/file/[a-zA-Z0-9-]+/archive$", // Archive file
"^/api/v1/file/[a-zA-Z0-9-]+/restore$", // Restore file
// Tag patterns
"^/api/v1/tags/[a-zA-Z0-9-]+$", // Individual tag operations (GET, PUT, DELETE)
"^/api/v1/tags/[a-zA-Z0-9-]+/assign$", // Assign tag to entity
"^/api/v1/tags/[a-zA-Z0-9-]+/entities/[a-zA-Z0-9-]+$", // Unassign tag from entity
"^/api/v1/tags/for/collection/[a-zA-Z0-9-]+$", // Get tags for collection
"^/api/v1/tags/for/file/[a-zA-Z0-9-]+$", // Get tags for file
"^/api/v1/tags/collections$", // List collections by tag
"^/api/v1/tags/files$", // List files by tag
}
// Precompile patterns
patternRoutes = make([]protectedRoute, len(patterns))
for i, pattern := range patterns {
patternRoutes[i] = protectedRoute{
pattern: pattern,
regex: regexp.MustCompile(pattern),
}
}
}
func isProtectedPath(logger *zap.Logger, path string) bool {
// Check exact matches first (O(1) lookup)
if exactPaths[path] {
logger.Debug("✅ found via map - url is protected",
zap.String("path", path))
return true
}
// Check patterns
for _, route := range patternRoutes {
if route.regex.MatchString(path) {
logger.Debug("✅ found via regex - url is protected",
zap.String("path", path))
return true
}
}
logger.Debug("❌ not found",
zap.String("path", path))
return false
}