Add Go rewrite with Postgres 17 and Dokploy Docker Compose
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// registerRoutes is set by internal/web/handlers's init() (via RegisterRoutes),
|
||||
// avoiding an import cycle: handlers imports web for *App, so web cannot import
|
||||
// handlers back — it calls into this hook instead.
|
||||
var registerRoutes func(*App, *http.ServeMux)
|
||||
|
||||
// RegisterRoutes installs the application's route-registration callback. Called
|
||||
// once from the handlers package's init().
|
||||
func RegisterRoutes(fn func(*App, *http.ServeMux)) {
|
||||
registerRoutes = fn
|
||||
}
|
||||
|
||||
// Handler builds the full HTTP handler: routing, static assets, session
|
||||
// load/save, panic recovery, request-id tagging and base-URL prefix handling.
|
||||
func (a *App) Handler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
if registerRoutes != nil {
|
||||
registerRoutes(a, mux)
|
||||
}
|
||||
|
||||
staticDir := filepath.Join(a.Cfg.WebDir, "static")
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
|
||||
|
||||
var h http.Handler = mux
|
||||
h = a.recoverer(h)
|
||||
h = a.requestID(h)
|
||||
h = a.Sessions.LoadAndSave(h)
|
||||
h = a.stripBasePrefix(h)
|
||||
return h
|
||||
}
|
||||
|
||||
// recoverer converts panics in handlers into a 500 response instead of crashing
|
||||
// the server.
|
||||
func (a *App) recoverer(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
log.Printf("panic: %v\n%s", rec, debug.Stack())
|
||||
http.Error(w, "Внутренняя ошибка сервера.", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// requestID tags every response with a short correlation id (X-Request-Id),
|
||||
// useful for grepping logs; purely optional/best-effort.
|
||||
func (a *App) requestID(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.Header.Get("X-Request-Id")
|
||||
if id == "" {
|
||||
b := make([]byte, 8)
|
||||
_, _ = rand.Read(b)
|
||||
id = hex.EncodeToString(b)
|
||||
}
|
||||
w.Header().Set("X-Request-Id", id)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// stripBasePrefix strips the configured APP_BASE_URL path prefix (if any) from
|
||||
// incoming requests before they reach the router, so route patterns stay
|
||||
// prefix-free. Requests that arrive without the prefix (e.g. an internal
|
||||
// container health check hitting 127.0.0.1 directly) are passed through
|
||||
// unchanged rather than 404ing.
|
||||
func (a *App) stripBasePrefix(next http.Handler) http.Handler {
|
||||
prefix := strings.Trim(a.Cfg.BaseURL, "/")
|
||||
if prefix == "" {
|
||||
return next
|
||||
}
|
||||
p := "/" + prefix
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == p || strings.HasPrefix(r.URL.Path, p+"/") {
|
||||
trimmed := strings.TrimPrefix(r.URL.Path, p)
|
||||
if trimmed == "" {
|
||||
trimmed = "/"
|
||||
}
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
u2 := new(url.URL)
|
||||
*u2 = *r.URL
|
||||
u2.Path = trimmed
|
||||
r2.URL = u2
|
||||
next.ServeHTTP(w, r2)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user