This commit introduces the following changes:
- Added new API endpoints for email change requests and
verification.
- Updated the backend code to support email change workflow,
including validation, code generation, and email sending.
- Updated the frontend to include components for initiating and
verifying email changes.
- Added new dependencies to support email change functionality.
- Updated the existing components to include email change
functionality.
https://codeberg.org/mapleopentech/monorepo/issues/1
113 lines
4.4 KiB
Go
113 lines
4.4 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/me/email/change-request": true, // Email change step 1: request
|
|
"/api/v1/me/email/change-verify": true, // Email change step 2: verify
|
|
"/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
|
|
}
|