package router import ( "database/sql" "log/slog" "net/http" "os" "time" "person-home/internal/auth" "person-home/internal/handlers" ) func New(db *sql.DB, jwtSecret []byte, uploadDir string, embeddedFrontend http.FileSystem) http.Handler { mux := http.NewServeMux() // Public handlers projectH := &handlers.ProjectHandler{DB: db} mux.HandleFunc("GET /api/projects", projectH.List) mux.HandleFunc("GET /api/projects/{id}", projectH.Get) mux.HandleFunc("GET /api/tags", projectH.Tags) // Settings (public) settingsH := &handlers.SettingsHandler{DB: db} mux.HandleFunc("GET /api/settings", settingsH.Get) // Auth handler authH := &handlers.AuthHandler{DB: db, JWTSecret: jwtSecret} mux.HandleFunc("POST /api/auth/login", authH.Login) // Admin handlers (with auth middleware) adminH := &handlers.AdminHandler{DB: db, UploadDir: uploadDir} adminMux := http.NewServeMux() adminMux.HandleFunc("GET /api/admin/projects", adminH.List) adminMux.HandleFunc("POST /api/admin/projects", adminH.Create) adminMux.HandleFunc("PUT /api/admin/projects/{id}", adminH.Update) adminMux.HandleFunc("DELETE /api/admin/projects/{id}", adminH.Delete) adminMux.HandleFunc("POST /api/admin/upload", adminH.Upload) adminMux.HandleFunc("GET /api/admin/export", adminH.Export) adminMux.HandleFunc("PUT /api/admin/settings", settingsH.Update) mux.Handle("/api/admin/", auth.AdminOnly(jwtSecret)(adminMux)) // Uploaded images fs := http.FileServer(http.Dir(uploadDir)) mux.Handle("GET /uploads/", http.StripPrefix("/uploads/", fs)) // SPA fallback: serve embedded frontend or dev proxy if embeddedFrontend != nil { mux.Handle("/", http.FileServer(embeddedFrontend)) } return slogMiddleware(corsMiddleware(mux)) } func slogMiddleware(next http.Handler) http.Handler { logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo})) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) logger.Info("request", "method", r.Method, "path", r.URL.Path, "duration", time.Since(start)) }) } func corsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := os.Getenv("CORS_ORIGIN") if origin == "" { origin = "http://localhost:5173" } w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") w.Header().Set("Access-Control-Max-Age", "86400") if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } next.ServeHTTP(w, r) }) }