gohorsejobs/backend/internal/router/router.go
Tiago Yamamoto 6fbd1f5ffc feat: implement full auth system with HTTPOnly cookies + JWT, fix migrations to UUID v7, remove mock data from frontend
Backend:
- Fix migrations 037-041 to use UUID v7 (uuid_generate_v7)
- Fix CORS defaults to include localhost:8963
- Fix FRONTEND_URL default to localhost:8963
- Update superadmin password hash with pepper
- Add PASSWORD_PEPPER environment variable

Frontend:
- Replace mockJobs with real API calls in home page
- Replace mockNotifications with notificationsApi in context
- Replace mockApplications with applicationsApi in dashboard
- Fix register/user page to call real registerCandidate API
- Fix hardcoded values in backoffice and messages pages

Auth:
- Support both HTTPOnly cookie and Bearer token authentication
- Login returns token + sets HTTPOnly cookie
- Logout clears HTTPOnly cookie
- Token valid for 24h
2026-02-16 05:20:46 -06:00

377 lines
22 KiB
Go
Executable file

package router
import (
"encoding/json"
"net/http"
"os"
"time"
// Added this import
"github.com/rede5/gohorsejobs/backend/internal/api/middleware"
"github.com/rede5/gohorsejobs/backend/internal/database"
"github.com/rede5/gohorsejobs/backend/internal/handlers"
"github.com/rede5/gohorsejobs/backend/internal/infrastructure/persistence/postgres"
"github.com/rede5/gohorsejobs/backend/internal/services"
// Core Imports
apiHandlers "github.com/rede5/gohorsejobs/backend/internal/api/handlers"
authUC "github.com/rede5/gohorsejobs/backend/internal/core/usecases/auth"
tenantUC "github.com/rede5/gohorsejobs/backend/internal/core/usecases/tenant"
userUC "github.com/rede5/gohorsejobs/backend/internal/core/usecases/user"
authInfra "github.com/rede5/gohorsejobs/backend/internal/infrastructure/auth"
legacyMiddleware "github.com/rede5/gohorsejobs/backend/internal/middleware"
// Admin Imports
_ "github.com/rede5/gohorsejobs/backend/docs" // Import generated docs
httpSwagger "github.com/swaggo/http-swagger/v2"
)
func NewRouter() http.Handler {
mux := http.NewServeMux()
// Initialize Services
// --- CORE ARCHITECTURE INITIALIZATION ---
// Infrastructure
userRepo := postgres.NewUserRepository(database.DB)
companyRepo := postgres.NewCompanyRepository(database.DB)
locationRepo := postgres.NewLocationRepository(database.DB)
// Utils Services (Moved up for dependency injection)
credentialsService := services.NewCredentialsService(database.DB)
settingsService := services.NewSettingsService(database.DB)
storageService := services.NewStorageService(credentialsService)
fcmService := services.NewFCMService(credentialsService)
cloudflareService := services.NewCloudflareService(credentialsService)
emailService := services.NewEmailService(database.DB, credentialsService)
locationService := services.NewLocationService(locationRepo)
adminService := services.NewAdminService(database.DB)
jobService := services.NewJobService(database.DB)
applicationService := services.NewApplicationService(database.DB, emailService)
jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
// Fallback for dev, but really should be in env
jwtSecret = "default-dev-secret-do-not-use-in-prod"
}
authService := authInfra.NewJWTService(jwtSecret, "todai-jobs")
// Token Repository for Password Reset
tokenRepo := postgres.NewPasswordResetTokenRepository(database.DB)
// Frontend URL for reset link
frontendURL := os.Getenv("FRONTEND_URL")
if frontendURL == "" {
frontendURL = "http://localhost:8963"
}
// UseCases
loginUC := authUC.NewLoginUseCase(userRepo, authService)
registerCandidateUC := authUC.NewRegisterCandidateUseCase(userRepo, companyRepo, authService, emailService)
createCompanyUC := tenantUC.NewCreateCompanyUseCase(companyRepo, userRepo, authService)
listCompaniesUC := tenantUC.NewListCompaniesUseCase(companyRepo)
createUserUC := userUC.NewCreateUserUseCase(userRepo, authService)
listUsersUC := userUC.NewListUsersUseCase(userRepo)
deleteUserUC := userUC.NewDeleteUserUseCase(userRepo)
updateUserUC := userUC.NewUpdateUserUseCase(userRepo)
updatePasswordUC := userUC.NewUpdatePasswordUseCase(userRepo, authService)
forgotPasswordUC := authUC.NewForgotPasswordUseCase(userRepo, tokenRepo, emailService, frontendURL)
resetPasswordUC := authUC.NewResetPasswordUseCase(userRepo, tokenRepo, authService)
// Admin Logic Services
auditService := services.NewAuditService(database.DB)
notificationService := services.NewNotificationService(database.DB, fcmService)
ticketService := services.NewTicketService(database.DB)
// Handlers & Middleware
coreHandlers := apiHandlers.NewCoreHandlers(
loginUC,
registerCandidateUC,
createCompanyUC,
createUserUC,
listUsersUC,
deleteUserUC,
updateUserUC,
updatePasswordUC,
listCompaniesUC,
forgotPasswordUC,
resetPasswordUC,
auditService,
notificationService,
ticketService,
adminService,
credentialsService,
)
authMiddleware := middleware.NewMiddleware(authService)
// Chat Services
appwriteService := services.NewAppwriteService(credentialsService)
chatService := services.NewChatService(database.DB, appwriteService)
chatHandlers := apiHandlers.NewChatHandlers(chatService)
settingsHandler := apiHandlers.NewSettingsHandler(settingsService)
credentialsHandler := apiHandlers.NewCredentialsHandler(credentialsService) // Added
storageHandler := apiHandlers.NewStorageHandler(storageService)
adminHandlers := apiHandlers.NewAdminHandlers(adminService, auditService, jobService, cloudflareService)
locationHandlers := apiHandlers.NewLocationHandlers(locationService)
seederService := services.NewSeederService(database.DB)
seederHandlers := apiHandlers.NewSeederHandlers(seederService)
// Initialize Legacy Handlers
jobHandler := handlers.NewJobHandler(jobService)
applicationHandler := handlers.NewApplicationHandler(applicationService)
paymentHandler := handlers.NewPaymentHandler(credentialsService)
// --- HEALTH CHECK ---
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("OK"))
})
// --- ROOT ROUTE ---
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
response := map[string]interface{}{
"message": "GoHorseJobs API is running!",
"docs": "/docs",
"health": "/health",
"version": "1.0.0",
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
})
// --- CORE ROUTES ---
// Public
mux.HandleFunc("POST /api/v1/auth/login", coreHandlers.Login)
mux.HandleFunc("POST /api/v1/auth/logout", coreHandlers.Logout)
mux.HandleFunc("POST /api/v1/auth/forgot-password", coreHandlers.ForgotPassword)
mux.HandleFunc("POST /api/v1/auth/reset-password", coreHandlers.ResetPassword)
mux.HandleFunc("POST /api/v1/auth/register", coreHandlers.RegisterCandidate)
mux.HandleFunc("POST /api/v1/auth/register/candidate", coreHandlers.RegisterCandidate)
mux.HandleFunc("POST /api/v1/auth/register/company", coreHandlers.CreateCompany)
mux.HandleFunc("POST /api/v1/companies", coreHandlers.CreateCompany)
// Public/Protected with RBAC (Smart Handler)
mux.Handle("GET /api/v1/companies", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(coreHandlers.ListCompanies)))
adminOnly := authMiddleware.RequireRoles("ADMIN", "SUPERADMIN", "admin", "superadmin")
// Protected Core
mux.Handle("POST /api/v1/users", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.CreateUser)))
mux.Handle("GET /api/v1/users", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(coreHandlers.ListUsers))))
mux.Handle("PATCH /api/v1/users/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.UpdateUser)))
mux.Handle("DELETE /api/v1/users/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.DeleteUser)))
mux.Handle("PUT /api/v1/users/me", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.UpdateMe))) // New Profile Update
// Job Routes
mux.HandleFunc("GET /api/v1/jobs", jobHandler.GetJobs)
mux.Handle("POST /api/v1/jobs", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobHandler.CreateJob)))
mux.HandleFunc("GET /api/v1/jobs/{id}", jobHandler.GetJobByID)
mux.Handle("PUT /api/v1/jobs/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobHandler.UpdateJob)))
mux.Handle("DELETE /api/v1/jobs/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobHandler.DeleteJob)))
// --- ADMIN ROUTES (Consolidated to Standard Paths with RBAC) ---
// /api/v1/admin/access/roles -> /api/v1/users/roles
mux.Handle("GET /api/v1/users/roles", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.ListAccessRoles))))
// /api/v1/admin/audit/logins -> /api/v1/audit/logins
mux.Handle("GET /api/v1/audit/logins", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.ListLoginAudits))))
// Public /api/v1/users/me (Authenticated)
mux.Handle("GET /api/v1/users/me", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.Me)))
mux.Handle("PATCH /api/v1/users/me/profile", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.UpdateMyProfile)))
mux.Handle("PATCH /api/v1/users/me/password", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.UpdateMyPassword)))
// Company Management
mux.Handle("PATCH /api/v1/companies/{id}/status", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateCompanyStatus))))
mux.Handle("PATCH /api/v1/companies/{id}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateCompany))))
mux.Handle("DELETE /api/v1/companies/{id}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.DeleteCompany))))
mux.Handle("GET /api/v1/jobs/moderation", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.ListJobs))))
// /api/v1/admin/jobs/{id}/status
mux.Handle("PATCH /api/v1/jobs/{id}/status", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateJobStatus))))
// /api/v1/admin/jobs/{id}/duplicate -> /api/v1/jobs/{id}/duplicate
mux.Handle("POST /api/v1/jobs/{id}/duplicate", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.DuplicateJob))))
// /api/v1/tags (GET public/auth, POST/PATCH admin)
mux.Handle("GET /api/v1/tags", authMiddleware.HeaderAuthGuard(http.HandlerFunc(adminHandlers.ListTags)))
mux.Handle("POST /api/v1/tags", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.CreateTag))))
mux.Handle("PATCH /api/v1/tags/{id}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateTag))))
// /api/v1/admin/candidates -> /api/v1/candidates
mux.Handle("GET /api/v1/candidates", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.ListCandidates))))
// Get Company by ID (Public)
mux.HandleFunc("GET /api/v1/companies/{id}", coreHandlers.GetCompanyByID)
// Location Routes (Public)
mux.HandleFunc("GET /api/v1/locations/countries", locationHandlers.ListCountries)
mux.HandleFunc("GET /api/v1/locations/countries/{id}/states", locationHandlers.ListStatesByCountry)
mux.HandleFunc("GET /api/v1/locations/states/{id}/cities", locationHandlers.ListCitiesByState)
mux.HandleFunc("GET /api/v1/locations/search", locationHandlers.SearchLocations)
// Notifications Route
mux.Handle("GET /api/v1/notifications", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.ListNotifications)))
mux.Handle("POST /api/v1/tokens", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.SaveFCMToken)))
// Support Ticket Routes
mux.Handle("GET /api/v1/support/tickets", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.ListTickets)))
mux.Handle("POST /api/v1/support/tickets", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.CreateTicket)))
mux.Handle("GET /api/v1/support/tickets/all", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.ListAllTickets)))
mux.Handle("GET /api/v1/support/tickets/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.GetTicket)))
mux.Handle("POST /api/v1/support/tickets/{id}/messages", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.AddMessage)))
mux.Handle("PATCH /api/v1/support/tickets/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.UpdateTicket)))
mux.Handle("PATCH /api/v1/support/tickets/{id}/close", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.CloseTicket)))
mux.Handle("DELETE /api/v1/support/tickets/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(coreHandlers.DeleteTicket)))
// System Settings
mux.Handle("GET /api/v1/system/settings/{key}", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(settingsHandler.GetSettings)))
mux.Handle("POST /api/v1/system/settings/{key}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(settingsHandler.SaveSettings))))
// System Credentials
mux.Handle("GET /api/v1/system/credentials", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(credentialsHandler.ListCredentials))))
mux.Handle("POST /api/v1/system/credentials", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(credentialsHandler.SaveCredential))))
mux.Handle("DELETE /api/v1/system/credentials/{service}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(credentialsHandler.DeleteCredential))))
// Storage (Presigned URL)
mux.Handle("GET /api/v1/storage/upload-url", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.GetUploadURL)))
mux.Handle("POST /api/v1/storage/upload-url", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.GetUploadURL)))
mux.Handle("POST /api/v1/storage/download-url", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.GetDownloadURL)))
mux.Handle("DELETE /api/v1/storage/files", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.DeleteFile)))
// Storage (Direct Proxy)
mux.Handle("POST /api/v1/storage/upload", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.UploadFile)))
mux.Handle("POST /api/v1/admin/storage/test-connection", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(storageHandler.TestConnection))))
mux.Handle("POST /api/v1/system/cloudflare/purge", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.PurgeCache))))
// Seeder Routes (Dev Only)
mux.HandleFunc("GET /api/v1/seeder/seed/stream", seederHandlers.HandleSeedStream)
mux.HandleFunc("POST /api/v1/seeder/reset", seederHandlers.HandleReset)
// Email Templates & Settings (Admin Only)
mux.Handle("GET /api/v1/admin/email-templates", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.ListEmailTemplates))))
mux.Handle("POST /api/v1/admin/email-templates", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.CreateEmailTemplate))))
mux.Handle("GET /api/v1/admin/email-templates/{slug}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.GetEmailTemplate))))
mux.Handle("PUT /api/v1/admin/email-templates/{slug}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateEmailTemplate))))
mux.Handle("DELETE /api/v1/admin/email-templates/{slug}", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.DeleteEmailTemplate))))
mux.Handle("GET /api/v1/admin/email-settings", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.GetEmailSettings))))
mux.Handle("PUT /api/v1/admin/email-settings", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.UpdateEmailSettings))))
// Chat Routes
mux.Handle("GET /api/v1/conversations", authMiddleware.HeaderAuthGuard(http.HandlerFunc(chatHandlers.ListConversations)))
mux.Handle("GET /api/v1/conversations/{id}/messages", authMiddleware.HeaderAuthGuard(http.HandlerFunc(chatHandlers.ListMessages)))
mux.Handle("POST /api/v1/conversations/{id}/messages", authMiddleware.HeaderAuthGuard(http.HandlerFunc(chatHandlers.SendMessage)))
// Metrics Routes
metricsService := services.NewMetricsService(database.DB)
metricsHandler := handlers.NewMetricsHandler(metricsService)
mux.HandleFunc("GET /api/v1/jobs/{id}/metrics", metricsHandler.GetJobMetrics)
mux.HandleFunc("POST /api/v1/jobs/{id}/view", metricsHandler.RecordJobView)
// Subscription Routes
subService := services.NewSubscriptionService(database.DB)
subHandler := handlers.NewSubscriptionHandler(subService)
mux.HandleFunc("POST /api/v1/subscription/checkout", subHandler.CreateCheckoutSession)
mux.HandleFunc("POST /api/v1/subscription/webhook", subHandler.HandleWebhook)
// Application Routes (merged: both OptionalAuth for create + both /me endpoints)
mux.Handle("POST /api/v1/applications", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(applicationHandler.CreateApplication)))
mux.Handle("GET /api/v1/applications/me", authMiddleware.HeaderAuthGuard(http.HandlerFunc(applicationHandler.GetMyApplications)))
mux.HandleFunc("GET /api/v1/applications", applicationHandler.GetApplications)
mux.HandleFunc("GET /api/v1/applications/{id}", applicationHandler.GetApplicationByID)
mux.HandleFunc("PUT /api/v1/applications/{id}/status", applicationHandler.UpdateApplicationStatus)
mux.HandleFunc("DELETE /api/v1/applications/{id}", applicationHandler.DeleteApplication)
// Job Alert Routes
jobAlertService := services.NewJobAlertService(database.DB, emailService)
jobAlertHandler := handlers.NewJobAlertHandler(jobAlertService)
mux.Handle("POST /api/v1/alerts", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(jobAlertHandler.CreateAlert)))
mux.Handle("GET /api/v1/alerts/me", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobAlertHandler.GetMyAlerts)))
mux.Handle("DELETE /api/v1/alerts/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobAlertHandler.DeleteAlert)))
mux.Handle("PATCH /api/v1/alerts/{id}/toggle", authMiddleware.HeaderAuthGuard(http.HandlerFunc(jobAlertHandler.ToggleAlert)))
mux.HandleFunc("GET /api/v1/alerts/confirm", jobAlertHandler.ConfirmAlert)
// Favorite Jobs Routes
favoriteJobService := services.NewFavoriteJobService(database.DB)
favoriteJobHandler := handlers.NewFavoriteJobHandler(favoriteJobService)
mux.Handle("GET /api/v1/favorites", authMiddleware.HeaderAuthGuard(http.HandlerFunc(favoriteJobHandler.GetMyFavorites)))
mux.Handle("POST /api/v1/favorites/{jobId}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(favoriteJobHandler.AddFavorite)))
mux.Handle("DELETE /api/v1/favorites/{jobId}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(favoriteJobHandler.RemoveFavorite)))
mux.Handle("GET /api/v1/favorites/{jobId}/check", authMiddleware.HeaderAuthGuard(http.HandlerFunc(favoriteJobHandler.CheckFavorite)))
// Company Followers Routes
companyFollowerService := services.NewCompanyFollowerService(database.DB)
companyFollowerHandler := handlers.NewCompanyFollowerHandler(companyFollowerService)
mux.Handle("GET /api/v1/companies/following", authMiddleware.HeaderAuthGuard(http.HandlerFunc(companyFollowerHandler.GetMyFollowing)))
mux.Handle("POST /api/v1/companies/follow/{companyId}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(companyFollowerHandler.Follow)))
mux.Handle("DELETE /api/v1/companies/follow/{companyId}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(companyFollowerHandler.Unfollow)))
mux.Handle("GET /api/v1/companies/followed/check/{companyId}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(companyFollowerHandler.CheckFollowing)))
mux.HandleFunc("GET /api/v1/companies/with-jobs", companyFollowerHandler.GetCompanies)
// Video Interview Routes
videoInterviewService := services.NewVideoInterviewService(database.DB)
videoInterviewHandler := handlers.NewVideoInterviewHandler(videoInterviewService)
mux.Handle("POST /api/v1/interviews", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.CreateInterview)))
mux.Handle("GET /api/v1/interviews", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.GetCompanyInterviews)))
mux.HandleFunc("GET /api/v1/interviews/by-application", videoInterviewHandler.GetInterviewsByApplication)
mux.Handle("GET /api/v1/interviews/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.GetInterview)))
mux.Handle("PUT /api/v1/interviews/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.UpdateInterview)))
mux.Handle("POST /api/v1/interviews/{id}/feedback", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.SubmitFeedback)))
mux.Handle("DELETE /api/v1/interviews/{id}", authMiddleware.HeaderAuthGuard(http.HandlerFunc(videoInterviewHandler.DeleteInterview)))
// Payment Routes
mux.Handle("POST /api/v1/payments/create-checkout", authMiddleware.HeaderAuthGuard(http.HandlerFunc(paymentHandler.CreateCheckout)))
mux.HandleFunc("POST /api/v1/payments/webhook", paymentHandler.HandleWebhook)
mux.HandleFunc("GET /api/v1/payments/status/{id}", paymentHandler.GetPaymentStatus)
// --- STORAGE ROUTES (Legacy Removed) ---
// --- TICKET ROUTES ---
ticketHandler := handlers.NewTicketHandler(ticketService)
mux.HandleFunc("GET /api/v1/tickets", ticketHandler.GetTickets)
mux.HandleFunc("POST /api/v1/tickets", ticketHandler.CreateTicket)
mux.HandleFunc("GET /api/v1/tickets/{id}", ticketHandler.GetTicketByID)
mux.HandleFunc("PUT /api/v1/tickets/{id}", ticketHandler.UpdateTicket)
mux.HandleFunc("POST /api/v1/tickets/{id}/messages", ticketHandler.AddTicketMessage)
// --- ACTIVITY LOG ROUTES ---
activityLogService := services.NewActivityLogService(database.DB)
activityLogHandler := handlers.NewActivityLogHandler(activityLogService)
mux.HandleFunc("GET /api/v1/activity-logs/stats", activityLogHandler.GetActivityLogStats)
mux.HandleFunc("GET /api/v1/activity-logs", activityLogHandler.GetActivityLogs)
// --- NOTIFICATION ROUTES ---
notificationHandler := handlers.NewNotificationHandler(notificationService)
mux.Handle("PUT /api/v1/notifications/read-all", authMiddleware.HeaderAuthGuard(http.HandlerFunc(notificationHandler.MarkAllAsRead)))
mux.Handle("PUT /api/v1/notifications/{id}/read", authMiddleware.HeaderAuthGuard(http.HandlerFunc(notificationHandler.MarkAsRead)))
mux.Handle("PATCH /api/v1/notifications/read-all", authMiddleware.HeaderAuthGuard(http.HandlerFunc(notificationHandler.MarkAllAsRead)))
mux.Handle("PATCH /api/v1/notifications/{id}/read", authMiddleware.HeaderAuthGuard(http.HandlerFunc(notificationHandler.MarkAsRead)))
mux.Handle("POST /api/v1/notifications/fcm-token", authMiddleware.HeaderAuthGuard(http.HandlerFunc(notificationHandler.RegisterFCMToken)))
// Swagger Route - available at /docs
mux.HandleFunc("/docs/", httpSwagger.WrapHandler)
// Apply middleware chain: Security Headers -> Rate Limiting -> CORS -> Router
// Order matters: outer middleware
var handler http.Handler = mux
handler = middleware.CORSMiddleware(handler)
handler = legacyMiddleware.SanitizeMiddleware(handler) // Sanitize XSS from JSON bodies
handler = legacyMiddleware.RateLimitMiddleware(100, time.Minute)(handler) // 100 req/min per IP
handler = legacyMiddleware.SecurityHeadersMiddleware(handler)
return handler
}