package router import ( "encoding/json" "net/http" "github.com/jmoiron/sqlx" "mgit.msbls.de/m/KanzlAI-mGMT/internal/auth" "mgit.msbls.de/m/KanzlAI-mGMT/internal/config" "mgit.msbls.de/m/KanzlAI-mGMT/internal/handlers" "mgit.msbls.de/m/KanzlAI-mGMT/internal/services" ) func New(db *sqlx.DB, authMW *auth.Middleware, cfg *config.Config, calDAVSvc *services.CalDAVService) http.Handler { mux := http.NewServeMux() // Services tenantSvc := services.NewTenantService(db) caseSvc := services.NewCaseService(db) partySvc := services.NewPartyService(db) appointmentSvc := services.NewAppointmentService(db) holidaySvc := services.NewHolidayService(db) deadlineSvc := services.NewDeadlineService(db) deadlineRuleSvc := services.NewDeadlineRuleService(db) calculator := services.NewDeadlineCalculator(holidaySvc) storageCli := services.NewStorageClient(cfg.SupabaseURL, cfg.SupabaseServiceKey) documentSvc := services.NewDocumentService(db, storageCli) // AI service (optional — only if API key is configured) var aiH *handlers.AIHandler if cfg.AnthropicAPIKey != "" { aiSvc := services.NewAIService(cfg.AnthropicAPIKey, db) aiH = handlers.NewAIHandler(aiSvc, db) } // Middleware tenantResolver := auth.NewTenantResolver(tenantSvc) dashboardSvc := services.NewDashboardService(db) // Handlers tenantH := handlers.NewTenantHandler(tenantSvc) caseH := handlers.NewCaseHandler(caseSvc) partyH := handlers.NewPartyHandler(partySvc) apptH := handlers.NewAppointmentHandler(appointmentSvc) deadlineH := handlers.NewDeadlineHandlers(deadlineSvc, db) ruleH := handlers.NewDeadlineRuleHandlers(deadlineRuleSvc) calcH := handlers.NewCalculateHandlers(calculator, deadlineRuleSvc) dashboardH := handlers.NewDashboardHandler(dashboardSvc) docH := handlers.NewDocumentHandler(documentSvc) // Public routes mux.HandleFunc("GET /health", handleHealth(db)) // Authenticated API routes api := http.NewServeMux() // Tenant management (no tenant resolver — these operate across tenants) api.HandleFunc("POST /api/tenants", tenantH.CreateTenant) api.HandleFunc("GET /api/tenants", tenantH.ListTenants) api.HandleFunc("GET /api/tenants/{id}", tenantH.GetTenant) api.HandleFunc("POST /api/tenants/{id}/invite", tenantH.InviteUser) api.HandleFunc("DELETE /api/tenants/{id}/members/{uid}", tenantH.RemoveMember) api.HandleFunc("GET /api/tenants/{id}/members", tenantH.ListMembers) // Tenant-scoped routes (require tenant context) scoped := http.NewServeMux() // Cases scoped.HandleFunc("GET /api/cases", caseH.List) scoped.HandleFunc("POST /api/cases", caseH.Create) scoped.HandleFunc("GET /api/cases/{id}", caseH.Get) scoped.HandleFunc("PUT /api/cases/{id}", caseH.Update) scoped.HandleFunc("DELETE /api/cases/{id}", caseH.Delete) // Parties scoped.HandleFunc("GET /api/cases/{id}/parties", partyH.List) scoped.HandleFunc("POST /api/cases/{id}/parties", partyH.Create) scoped.HandleFunc("PUT /api/parties/{partyId}", partyH.Update) scoped.HandleFunc("DELETE /api/parties/{partyId}", partyH.Delete) // Deadlines scoped.HandleFunc("GET /api/deadlines", deadlineH.ListAll) scoped.HandleFunc("GET /api/cases/{caseID}/deadlines", deadlineH.ListForCase) scoped.HandleFunc("POST /api/cases/{caseID}/deadlines", deadlineH.Create) scoped.HandleFunc("PUT /api/deadlines/{deadlineID}", deadlineH.Update) scoped.HandleFunc("PATCH /api/deadlines/{deadlineID}/complete", deadlineH.Complete) scoped.HandleFunc("DELETE /api/deadlines/{deadlineID}", deadlineH.Delete) // Deadline rules (reference data) scoped.HandleFunc("GET /api/deadline-rules", ruleH.List) scoped.HandleFunc("GET /api/deadline-rules/{type}", ruleH.GetRuleTree) scoped.HandleFunc("GET /api/proceeding-types", ruleH.ListProceedingTypes) // Deadline calculator scoped.HandleFunc("POST /api/deadlines/calculate", calcH.Calculate) // Appointments scoped.HandleFunc("GET /api/appointments", apptH.List) scoped.HandleFunc("POST /api/appointments", apptH.Create) scoped.HandleFunc("PUT /api/appointments/{id}", apptH.Update) scoped.HandleFunc("DELETE /api/appointments/{id}", apptH.Delete) // Dashboard scoped.HandleFunc("GET /api/dashboard", dashboardH.Get) // Documents scoped.HandleFunc("GET /api/cases/{id}/documents", docH.ListByCase) scoped.HandleFunc("POST /api/cases/{id}/documents", docH.Upload) scoped.HandleFunc("GET /api/documents/{docId}", docH.Download) scoped.HandleFunc("GET /api/documents/{docId}/meta", docH.GetMeta) scoped.HandleFunc("DELETE /api/documents/{docId}", docH.Delete) // AI endpoints if aiH != nil { scoped.HandleFunc("POST /api/ai/extract-deadlines", aiH.ExtractDeadlines) scoped.HandleFunc("POST /api/ai/summarize-case", aiH.SummarizeCase) } // CalDAV sync endpoints if calDAVSvc != nil { calDAVH := handlers.NewCalDAVHandler(calDAVSvc) scoped.HandleFunc("POST /api/caldav/sync", calDAVH.TriggerSync) scoped.HandleFunc("GET /api/caldav/status", calDAVH.GetStatus) } // Wire: auth -> tenant routes go directly, scoped routes get tenant resolver api.Handle("/api/", tenantResolver.Resolve(scoped)) mux.Handle("/api/", authMW.RequireAuth(api)) return mux } func handleHealth(db *sqlx.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if err := db.Ping(); err != nil { w.WriteHeader(http.StatusServiceUnavailable) json.NewEncoder(w).Encode(map[string]string{"status": "error", "error": err.Error()}) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) } }