package api import ( "github.com/calendarapi/internal/api/handlers" "github.com/calendarapi/internal/api/openapi" mw "github.com/calendarapi/internal/middleware" "github.com/go-chi/chi/v5" chimw "github.com/go-chi/chi/v5/middleware" ) type Handlers struct { Auth *handlers.AuthHandler User *handlers.UserHandler Calendar *handlers.CalendarHandler Sharing *handlers.SharingHandler Event *handlers.EventHandler Reminder *handlers.ReminderHandler Attendee *handlers.AttendeeHandler Contact *handlers.ContactHandler Availability *handlers.AvailabilityHandler Booking *handlers.BookingHandler APIKey *handlers.APIKeyHandler ICS *handlers.ICSHandler } func NewRouter(h Handlers, authMW *mw.AuthMiddleware, rateLimiter *mw.RateLimiter) *chi.Mux { r := chi.NewRouter() r.Use(chimw.Logger) r.Use(chimw.Recoverer) r.Use(chimw.RealIP) r.Use(rateLimiter.Limit) // OpenAPI spec and Swagger UI r.Get("/openapi.json", openapi.SpecHandler) r.Get("/docs", openapi.DocsHandler) // Public routes (no auth) r.Group(func(r chi.Router) { r.Post("/auth/register", h.Auth.Register) r.Post("/auth/login", h.Auth.Login) r.Post("/auth/refresh", h.Auth.Refresh) r.Get("/booking/{token}/availability", h.Booking.GetAvailability) r.Post("/booking/{token}/reserve", h.Booking.Reserve) r.Get("/cal/{token}/feed.ics", h.ICS.PublicFeed) }) // Authenticated routes r.Group(func(r chi.Router) { r.Use(authMW.Authenticate) // Auth r.Post("/auth/logout", h.Auth.Logout) r.Get("/auth/me", h.Auth.Me) // Users r.Get("/users/me", h.User.GetMe) r.Put("/users/me", h.User.UpdateMe) r.Delete("/users/me", h.User.DeleteMe) // API Keys r.Post("/api-keys", h.APIKey.Create) r.Get("/api-keys", h.APIKey.List) r.Delete("/api-keys/{id}", h.APIKey.Revoke) // Calendars r.Route("/calendars", func(r chi.Router) { r.With(mw.RequireScope("calendars", "read")).Get("/", h.Calendar.List) r.With(mw.RequireScope("calendars", "write")).Post("/", h.Calendar.Create) r.With(mw.RequireScope("calendars", "write")).Post("/import", h.ICS.Import) r.With(mw.RequireScope("calendars", "write")).Post("/import-url", h.ICS.ImportURL) r.Route("/{id}", func(r chi.Router) { r.With(mw.RequireScope("calendars", "read")).Get("/", h.Calendar.Get) r.With(mw.RequireScope("calendars", "write")).Put("/", h.Calendar.Update) r.With(mw.RequireScope("calendars", "write")).Delete("/", h.Calendar.Delete) // Sharing r.With(mw.RequireScope("calendars", "write")).Post("/share", h.Sharing.Share) r.With(mw.RequireScope("calendars", "read")).Get("/members", h.Sharing.ListMembers) r.With(mw.RequireScope("calendars", "write")).Delete("/members/{userID}", h.Sharing.RemoveMember) // Booking link r.With(mw.RequireScope("booking", "write")).Post("/booking-link", h.Booking.CreateLink) // ICS r.With(mw.RequireScope("calendars", "read")).Get("/export.ics", h.ICS.Export) }) }) // Events r.Route("/events", func(r chi.Router) { r.With(mw.RequireScope("events", "read")).Get("/", h.Event.List) r.With(mw.RequireScope("events", "write")).Post("/", h.Event.Create) r.Route("/{id}", func(r chi.Router) { r.With(mw.RequireScope("events", "read")).Get("/", h.Event.Get) r.With(mw.RequireScope("events", "write")).Put("/", h.Event.Update) r.With(mw.RequireScope("events", "write")).Delete("/", h.Event.Delete) // Reminders r.With(mw.RequireScope("events", "write")).Post("/reminders", h.Reminder.Add) r.With(mw.RequireScope("events", "write")).Delete("/reminders/{reminderID}", h.Reminder.Delete) // Attendees r.With(mw.RequireScope("events", "write")).Post("/attendees", h.Attendee.Add) r.With(mw.RequireScope("events", "write")).Put("/attendees/{attendeeID}", h.Attendee.UpdateStatus) r.With(mw.RequireScope("events", "write")).Delete("/attendees/{attendeeID}", h.Attendee.Delete) }) }) // Contacts r.Route("/contacts", func(r chi.Router) { r.With(mw.RequireScope("contacts", "read")).Get("/", h.Contact.List) r.With(mw.RequireScope("contacts", "write")).Post("/", h.Contact.Create) r.Route("/{id}", func(r chi.Router) { r.With(mw.RequireScope("contacts", "read")).Get("/", h.Contact.Get) r.With(mw.RequireScope("contacts", "write")).Put("/", h.Contact.Update) r.With(mw.RequireScope("contacts", "write")).Delete("/", h.Contact.Delete) }) }) // Availability r.With(mw.RequireScope("availability", "read")).Get("/availability", h.Availability.Get) }) return r }