Files
2026-06-20 19:00:11 -05:00

68 lines
1.8 KiB
Go

package api
import (
"net/http"
"time"
)
// cors allows the configured SPA origins with credentials and answers preflight.
func (s *Server) cors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin != "" && s.origins[origin] {
h := w.Header()
h.Set("Access-Control-Allow-Origin", origin)
h.Set("Access-Control-Allow-Credentials", "true")
h.Set("Vary", "Origin")
if r.Method == http.MethodOptions {
h.Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
h.Set("Access-Control-Allow-Headers", "Content-Type")
h.Set("Access-Control-Max-Age", "600")
w.WriteHeader(http.StatusNoContent)
return
}
} else if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
type statusRecorder struct {
http.ResponseWriter
status int
}
func (sr *statusRecorder) WriteHeader(code int) {
sr.status = code
sr.ResponseWriter.WriteHeader(code)
}
func (s *Server) requestLog(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rec, r)
s.log.Info("request",
"method", r.Method,
"path", r.URL.Path,
"status", rec.status,
"ip", clientIP(r),
"dur_ms", time.Since(start).Milliseconds(),
)
})
}
func (s *Server) recoverer(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
s.log.Error("panic recovered", "err", rec, "path", r.URL.Path)
writeError(w, http.StatusInternalServerError, "something went wrong")
}
}()
next.ServeHTTP(w, r)
})
}