60 lines
1.7 KiB
Go
60 lines
1.7 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"io/fs"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
|
|
"solopm.com/solopm-server/internal/config"
|
|
)
|
|
|
|
// Options bundles the dependencies the router needs to wire routes.
|
|
type Options struct {
|
|
Config *config.Config
|
|
Logger *slog.Logger
|
|
DB DBPinger
|
|
Version string
|
|
Dev bool // permissive CORS in dev; same-origin default otherwise
|
|
SPA fs.FS // web/dist, rooted so index.html is at the top level; nil disables SPA serving
|
|
}
|
|
|
|
// New builds the chi router with the full middleware chain (minus the auth
|
|
// resolver, which is added in Phase 1) and registers Phase 0 routes.
|
|
func New(opts Options) chi.Router {
|
|
r := chi.NewRouter()
|
|
|
|
r.Use(middleware.RequestID)
|
|
r.Use(middleware.RealIP) //nolint:staticcheck // middleware order is spec-mandated (01-architecture.md); deploy behind a trusted reverse proxy
|
|
r.Use(RequestLogger(opts.Logger))
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(corsMiddleware(opts.Dev))
|
|
|
|
r.Get("/healthz", healthzHandler(opts.Config, opts.DB, opts.Version))
|
|
|
|
if opts.SPA != nil {
|
|
r.Get("/*", spaHandler(opts.SPA))
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
func corsMiddleware(dev bool) func(http.Handler) http.Handler {
|
|
if dev {
|
|
return cors.Handler(cors.Options{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Content-Type", "Authorization"},
|
|
AllowCredentials: false,
|
|
})
|
|
}
|
|
// Same-origin default: no CORS headers granted, browser same-origin
|
|
// requests work regardless since no cross-origin request needs them.
|
|
return cors.Handler(cors.Options{
|
|
AllowedOrigins: []string{},
|
|
})
|
|
}
|