Frontend: - Implementar máscara de entrada de telefone para números BR ((XX) XXXXX-XXXX). - Atualizar formulário de cadastro para enviar dados completos do perfil do candidato (endereço, formação, habilidades, etc.). - Corrigir problemas de idioma misto na página de Detalhes da Vaga e adicionar traduções faltantes. Backend: - Atualizar modelo de Usuário, Entidade e DTOs para incluir campos de perfil (Data de Nascimento, Endereço, Formação, etc.). - Atualizar UserRepository para persistir e recuperar os dados estendidos do usuário no PostgreSQL. - Atualizar RegisterCandidateUseCase para mapear campos de entrada para a entidade Usuário.
294 lines
15 KiB
Go
Executable file
294 lines
15 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
|
|
jobService := services.NewJobService(database.DB)
|
|
applicationService := services.NewApplicationService(database.DB)
|
|
|
|
// --- CORE ARCHITECTURE INITIALIZATION ---
|
|
// Infrastructure
|
|
// 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)
|
|
|
|
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")
|
|
|
|
// 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)
|
|
auditService := services.NewAuditService(database.DB)
|
|
notificationService := services.NewNotificationService(database.DB, fcmService)
|
|
ticketService := services.NewTicketService(database.DB)
|
|
authMiddleware := middleware.NewMiddleware(authService)
|
|
|
|
// Chat Services
|
|
appwriteService := services.NewAppwriteService(credentialsService)
|
|
chatService := services.NewChatService(database.DB, appwriteService)
|
|
chatHandlers := apiHandlers.NewChatHandlers(chatService)
|
|
|
|
coreHandlers := apiHandlers.NewCoreHandlers(
|
|
loginUC,
|
|
registerCandidateUC,
|
|
createCompanyUC,
|
|
createUserUC,
|
|
listUsersUC,
|
|
deleteUserUC,
|
|
updateUserUC,
|
|
updatePasswordUC,
|
|
listCompaniesUC,
|
|
auditService,
|
|
notificationService, // Added
|
|
ticketService, // Added
|
|
adminService, // Added for RBAC support
|
|
credentialsService, // Added for Encrypted Credentials
|
|
)
|
|
|
|
settingsHandler := apiHandlers.NewSettingsHandler(settingsService)
|
|
storageHandler := apiHandlers.NewStorageHandler(storageService)
|
|
adminHandlers := apiHandlers.NewAdminHandlers(adminService, auditService, jobService, cloudflareService)
|
|
locationHandlers := apiHandlers.NewLocationHandlers(locationService)
|
|
|
|
// Initialize Legacy Handlers
|
|
jobHandler := handlers.NewJobHandler(jobService)
|
|
applicationHandler := handlers.NewApplicationHandler(applicationService)
|
|
paymentHandler := handlers.NewPaymentHandler(credentialsService)
|
|
|
|
// --- IP HELPER ---
|
|
GetClientIP := func(r *http.Request) string {
|
|
forwarded := r.Header.Get("X-Forwarded-For")
|
|
if forwarded != "" {
|
|
return forwarded
|
|
}
|
|
return r.RemoteAddr
|
|
}
|
|
|
|
// --- 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!",
|
|
"ip": GetClientIP(r),
|
|
"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/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)))
|
|
|
|
// 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)))
|
|
|
|
// /api/v1/admin/companies -> Handled by coreHandlers.ListCompanies (Smart Branching)
|
|
// Needs to be wired with Optional Auth to support both Public and Admin.
|
|
// I will create OptionalHeaderAuthGuard in middleware next.
|
|
|
|
// 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))))
|
|
|
|
// /api/v1/admin/jobs -> /api/v1/jobs?mode=admin (Need Smart Handler) or just separate path /api/v1/jobs/management?
|
|
// User said "remove admin from ALL routes".
|
|
// Maybe /api/v1/management/jobs?
|
|
// Or just /api/v1/jobs (guarded)?
|
|
// JobHandler.GetJobs is Public.
|
|
// I will leave /api/v1/admin/jobs mapped to `GET /api/v1/jobs` for now (Collision).
|
|
// OK, I will map it to `GET /api/v1/jobs/moderation` for clearer distinction without "admin" prefix?
|
|
// Or simply `GET /api/v1/jobs` handle it?
|
|
// Given safe constraints, `GET /api/v1/jobs/moderation` is safer than breaking public `GET /api/v1/jobs`.
|
|
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))))
|
|
|
|
// Storage (Presigned URL)
|
|
mux.Handle("GET /api/v1/storage/upload-url", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.GetUploadURL)))
|
|
// Storage (Direct Proxy)
|
|
mux.Handle("POST /api/v1/storage/upload", authMiddleware.OptionalHeaderAuthGuard(http.HandlerFunc(storageHandler.UploadFile)))
|
|
|
|
mux.Handle("POST /api/v1/system/cloudflare/purge", authMiddleware.HeaderAuthGuard(adminOnly(http.HandlerFunc(adminHandlers.PurgeCache))))
|
|
|
|
// 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)))
|
|
|
|
// Application Routes
|
|
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)
|
|
|
|
// 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) ---
|
|
|
|
// 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
|
|
}
|