package main import ( "crussell/auth" "crussell/internal/dav" "fmt" "log" "net/http" "os" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "crussell/db" "crussell/mw" authHandlers "crussell/handlers/auth" "crussell/handlers/bookings" "crussell/handlers/notifications" "crussell/handlers/scheduling" "crussell/handlers/services" "crussell/handlers/today" "crussell/handlers/user" ) func init() { jwtSecret := os.Getenv("JWT_SECRET_KEY") if jwtSecret == "" { log.Fatal("FATAL: JWT_SECRET_KEY environment variable not set. Application cannot start.") } auth.InitJWT(jwtSecret) } func initDB() { if err := db.Connect(); err != nil { log.Fatal("Failed to connect to DB:", err) } fmt.Println("Connected to DB successfully") } func initDav() { if dav.Service == nil { log.Fatal("Failed to initialize DAV service") } fmt.Println("DAV Service connected successfully") } func main() { initDB() initDav() r := chi.NewRouter() // --- Global Middleware --- r.Use(middleware.RequestID) r.Use(middleware.RealIP) r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(middleware.Timeout(15 * time.Second)) r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("X-XSS-Protection", "1; mode=block") next.ServeHTTP(w, r) }) }) // All API routes grouped under /api for clarity r.Route("/api", func(r chi.Router) { // --- Public Routes --- r.Get("/services", services.ServicesHandler) r.Post("/register", authHandlers.RegisterHandler) r.Post("/login", authHandlers.LoginHandler) // --- Scheduling Routes --- r.Route("/scheduling", func(r chi.Router) { // Public GET routes r.Get("/default-hours", scheduling.GetDefaultHours) r.Get("/exceptional-groups", scheduling.ListExceptionalGroups) r.Get("/working-hours", scheduling.GetWorkingHours) r.Get("/available-hours", scheduling.GetAvailableHours) // Admin-only scheduling modifications r.Group(func(r chi.Router) { r.Use(mw.RequireAuth) r.Use(mw.RequireAdmin) r.Put("/default-hours", scheduling.UpdateDefaultHours) r.Post("/exceptional-groups", scheduling.CreateExceptionalGroup) r.Delete("/exceptional-groups", scheduling.DeleteExceptionalGroup) r.Put("/exceptional-applications", scheduling.UpdateExceptionalApplications) }) }) // --- Protected routes (any authenticated user) --- r.Group(func(r chi.Router) { r.Use(mw.RequireAuth) r.Get("/user/profile", user.GetProfileHandler) r.Put("/user/profile", user.UpdateProfileHandler) r.Delete("/user/account", user.DeleteAccountHandler) r.Get("/user/loyalty", user.GetLoyaltyHandler) // Booking routes for authenticated users r.Route("/bookings", func(r chi.Router) { r.Get("/", bookings.GetAllUserBookingsHandler) r.Post("/", bookings.CreateBookingHandler) r.Get("/{id}", bookings.GetBookingHandler) r.Put("/{id}", bookings.EditBookingHandler) r.Delete("/{id}", bookings.DeleteBookingHandler) }) }) // --- Admin-only routes --- r.Group(func(r chi.Router) { r.Use(mw.RequireAuth) r.Use(mw.RequireAdmin) r.Route("/admin/services", func(r chi.Router) { r.Post("/", services.CreateServiceHandler) r.Delete("/{id}", services.DeleteServiceHandler) r.Get("/", services.AllServicesHandler) r.Put("/{id}/toggle", services.ToggleService) }) r.Route("/admin/bookings", func(r chi.Router) { r.Get("/", bookings.GetAllAdminBookingsHandler) r.Get("/search", bookings.SearchAdminBookingsHandler) r.Get("/user/{user_id}", bookings.GetAllBookingsByUserHandler) r.Get("/{id}", bookings.GetAdminBookingHandler) r.Put("/{id}/progress", bookings.ProgressBookingHandler) r.Post("/{id}/confirm", bookings.ConfirmBookingHandler) r.Post("/{id}/cancel", bookings.ConfirmBookingHandler) }) r.Route("/admin/users", func(r chi.Router) { r.Get("/", user.ListAdminUsersHandler) r.Get("/{id}", user.GetAdminUserHandler) }) r.Route("/admin/today", func(r chi.Router) { r.Get("/current-next", today.GetCurrentAndNextHandler) r.Get("/appointments", today.GetTodayAppointmentsHandler) r.Get("/pending-approvals", today.GetPendingApprovalsHandler) }) // --- Admin Notifications --- r.Route("/admin/notifications", func(r chi.Router) { // List all unacknowledged notifications r.Get("/", notifications.GetNotifications) // Acknowledge a single notification r.Post("/{id}/acknowledge", notifications.AcknowledgeNotification) }) }) }) fmt.Println("Server is listening on :8080") http.ListenAndServe(":8080", r) }