From ae24bdd0e0797b21f3b5db87b6a8d86bb851de40 Mon Sep 17 00:00:00 2001 From: vantablackTea Date: Sun, 27 Sep 2026 20:35:20 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=BE=D0=BD=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20/ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/internal/api/api.go | 14 +++++++++++++- server/internal/server/middleware.go | 19 +++++++++++++++++++ server/internal/server/server.go | 7 ++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/server/internal/api/api.go b/server/internal/api/api.go index 6b0a887..cb8cbcb 100644 --- a/server/internal/api/api.go +++ b/server/internal/api/api.go @@ -3,6 +3,7 @@ package api import ( "log/slog" "net/http" + "strings" "messenger/internal/auth" "messenger/internal/platform/config" @@ -19,7 +20,8 @@ type apiHandler struct { } // NewHandler builds the /api/v1 route tree with all middleware applied. -func NewHandler(st *store.Store, tm *auth.TokenManager, hub *realtime.Hub, log *slog.Logger, cfg *config.Config) http.Handler { +// static, when non-nil, serves the built frontend for non-API paths. +func NewHandler(st *store.Store, tm *auth.TokenManager, hub *realtime.Hub, log *slog.Logger, cfg *config.Config, static http.Handler) http.Handler { h := &apiHandler{ store: st, tokens: tm, @@ -54,6 +56,16 @@ func NewHandler(st *store.Store, tm *auth.TokenManager, hub *realtime.Hub, log * mux.Handle("GET /ws", authHandler) + if static != nil { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/api/") { + writeError(w, http.StatusNotFound, "not found") + return + } + static.ServeHTTP(w, r) + }) + } + return mux } diff --git a/server/internal/server/middleware.go b/server/internal/server/middleware.go index 330f4d6..2926f2c 100644 --- a/server/internal/server/middleware.go +++ b/server/internal/server/middleware.go @@ -1,7 +1,10 @@ package server import ( + "bufio" + "errors" "log/slog" + "net" "net/http" "runtime/debug" "slices" @@ -61,3 +64,19 @@ func (r *statusRecorder) WriteHeader(code int) { r.status = code r.ResponseWriter.WriteHeader(code) } + +// Hijack forwards the WebSocket upgrade to the underlying writer. +func (r *statusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hj, ok := r.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("response writer does not support hijacking") + } + return hj.Hijack() +} + +// Flush forwards periodic flush calls (SSE, streaming). +func (r *statusRecorder) Flush() { + if f, ok := r.ResponseWriter.(http.Flusher); ok { + f.Flush() + } +} diff --git a/server/internal/server/server.go b/server/internal/server/server.go index 3b3d2bc..8a01ae5 100644 --- a/server/internal/server/server.go +++ b/server/internal/server/server.go @@ -27,12 +27,13 @@ func New(cfg *config.Config, log *slog.Logger, st *store.Store, tm *auth.TokenMa writeJSON(w, http.StatusOK, map[string]string{"status": "ready"}) }) - mux.Handle("/api/v1/", api.NewHandler(st, tm, hub, log, cfg)) - + var static http.Handler if cfg.StaticDir != "" { - mux.Handle("/", spaHandler{staticDir: cfg.StaticDir, log: log}) + static = spaHandler{staticDir: cfg.StaticDir, log: log} } + mux.Handle("/", api.NewHandler(st, tm, hub, log, cfg, static)) + handler := logRequests(log, recoverPanics(cors(cfg.AllowedOrigins, mux))) return &http.Server{