package api import ( "context" "net/http" "strings" "github.com/imc-vibe/backend/internal/api/handlers" "github.com/imc-vibe/backend/internal/auth" "github.com/imc-vibe/backend/internal/config" "github.com/imc-vibe/backend/internal/db" "github.com/imc-vibe/backend/internal/mail" ) type Router struct { authHandler *handlers.AuthHandler domainHandler *handlers.DomainHandler userHandler *handlers.UserHandler aliasHandler *handlers.AliasHandler statsHandler *handlers.StatsHandler queueHandler *handlers.QueueHandler logsHandler *handlers.LogsHandler jwtManager *auth.JWTManager db *db.DB } func New(database *db.DB, cfg *config.Config) *Router { jwtManager := auth.NewJWTManager(cfg.JWTSecret, "imc-vibe") emailService := mail.NewEmailService(cfg) return &Router{ authHandler: handlers.NewAuthHandler(database, jwtManager, emailService), domainHandler: handlers.NewDomainHandler(database), userHandler: handlers.NewUserHandler(database), aliasHandler: handlers.NewAliasHandler(database), statsHandler: handlers.NewStatsHandler(database), queueHandler: handlers.NewQueueHandler(), logsHandler: handlers.NewLogsHandler(), jwtManager: jwtManager, db: database, } } func (r *Router) Handler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { path := req.URL.Path // Only handle /api/* routes if !strings.HasPrefix(path, "/api/") { http.NotFound(w, req) return } // Public routes - no auth required switch path { case "/api/auth/login": r.authHandler.Login(w, req) return case "/api/auth/forgot": r.authHandler.ForgotPassword(w, req) return case "/api/auth/logout": r.authHandler.Logout(w, req) return } // Protected routes - auth required req = r.validateAuth(req) if req == nil { http.Error(w, `{"error":"not authenticated"}`, http.StatusUnauthorized) return } switch { case path == "/api/auth/me": r.authHandler.Me(w, req) case path == "/api/auth/change-password": r.authHandler.ChangePassword(w, req) case path == "/api/stats": r.statsHandler.Get(w, req) case strings.HasPrefix(path, "/api/domains/"): if strings.Contains(strings.TrimPrefix(path, "/api/domains/"), "/users") { r.handleDomainUsers(w, req) } else if strings.Contains(strings.TrimPrefix(path, "/api/domains/"), "/aliases") { r.handleDomainAliases(w, req) } else { r.handleDomains(w, req) } case strings.HasPrefix(path, "/api/domains"): r.handleDomains(w, req) case strings.HasPrefix(path, "/api/queue"): r.handleQueue(w, req) case path == "/api/logs": r.logsHandler.List(w, req) default: http.NotFound(w, req) } }) } func (r *Router) validateAuth(req *http.Request) *http.Request { authHeader := req.Header.Get("Authorization") if authHeader == "" { return nil } parts := strings.SplitN(authHeader, " ", 2) if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" { return nil } claims, err := r.jwtManager.ValidateToken(parts[1]) if err != nil { return nil } authCtx := &auth.Context{ UserID: claims.UserID, Username: claims.Username, Role: auth.Role(claims.Role), } ctx := context.WithValue(req.Context(), handlers.AuthContextKey, authCtx) return req.WithContext(ctx) } func (r *Router) handleDomains(w http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodGet: if len(req.URL.Path) > len("/api/domains/") { r.domainHandler.Get(w, req) } else { r.domainHandler.List(w, req) } case http.MethodPost: r.domainHandler.Create(w, req) case http.MethodDelete: r.domainHandler.Delete(w, req) default: http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } func (r *Router) handleDomainUsers(w http.ResponseWriter, req *http.Request) { path := req.URL.Path parts := strings.Split(strings.TrimPrefix(path, "/api/domains/"), "/") if len(parts) < 2 || parts[1] != "users" { http.NotFound(w, req) return } domainName := parts[0] var idStr string if len(parts) >= 3 { idStr = parts[2] } switch req.Method { case http.MethodGet: if idStr != "" { req.URL.Path = "/api/domains/" + domainName + "/users/" + idStr r.userHandler.Get(w, req) } else { req.URL.Path = "/api/domains/" + domainName + "/users" r.userHandler.List(w, req) } case http.MethodPost: req.URL.Path = "/api/domains/" + domainName + "/users" r.userHandler.Create(w, req) case http.MethodPut: if idStr != "" { req.URL.Path = "/api/domains/" + domainName + "/users/" + idStr r.userHandler.Update(w, req) } else { http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } case http.MethodDelete: if idStr != "" { req.URL.Path = "/api/domains/" + domainName + "/users/" + idStr r.userHandler.Delete(w, req) } else { http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } default: http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } func (r *Router) handleDomainAliases(w http.ResponseWriter, req *http.Request) { path := req.URL.Path parts := strings.Split(strings.TrimPrefix(path, "/api/domains/"), "/") if len(parts) < 2 || parts[1] != "aliases" { http.NotFound(w, req) return } domainName := parts[0] var idStr string if len(parts) >= 3 { idStr = parts[2] } switch req.Method { case http.MethodGet: req.URL.Path = "/api/domains/" + domainName + "/aliases" r.aliasHandler.List(w, req) case http.MethodPost: req.URL.Path = "/api/domains/" + domainName + "/aliases" r.aliasHandler.Create(w, req) case http.MethodDelete: if idStr != "" { req.URL.Path = "/api/domains/" + domainName + "/aliases/" + idStr r.aliasHandler.Delete(w, req) } else { http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } default: http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } } func (r *Router) handleQueue(w http.ResponseWriter, req *http.Request) { path := req.URL.Path id := "" if len(path) > len("/api/queue/") { id = path[len("/api/queue/"):] } switch req.Method { case http.MethodGet: r.queueHandler.List(w, req) case http.MethodPost: if id != "" { req.URL.Path = "/api/queue/" + id r.queueHandler.Requeue(w, req) } else { http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } case http.MethodDelete: if id != "" { req.URL.Path = "/api/queue/" + id r.queueHandler.Delete(w, req) } else { http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } default: http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) } }