Fix timeout middleware panic on proxy/WebSocket routes and worker healthcheck

The TimeoutMiddleware wraps the response writer in *http.timeoutWriter which
doesn't implement http.Flusher. When the admin reverse proxy or WebSocket
upgrader tries to flush, it panics and crashes the container (502 Bad Gateway).
Skip timeout for /admin, /_next, and /ws routes.

Also fix the Dockerfile HEALTHCHECK to detect the worker process — the worker
has no HTTP server so the curl-based check always failed, marking it unhealthy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-02 19:56:12 -06:00
parent 7690f07a2b
commit 7438dfd9b1
8 changed files with 12 additions and 1 deletions

View File

@@ -145,6 +145,6 @@ RUN mkdir -p /app/uploads
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:${PORT:-5000}/api/health/ || exit 1
CMD pgrep -x worker > /dev/null && exit 0 || curl -f http://localhost:${PORT:-5000}/api/health/ || exit 1
CMD ["/app/start.sh"]

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-playground/validator/v10"
@@ -60,6 +61,16 @@ func SetupRouter(deps *Dependencies) *echo.Echo {
e.Use(middleware.BodyLimit("1M")) // 1MB default for JSON payloads
e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
Timeout: 30 * time.Second,
Skipper: func(c echo.Context) bool {
path := c.Request().URL.Path
// Skip timeout for reverse proxy and WebSocket routes — the
// timeout middleware wraps the response writer in *http.timeoutWriter
// which does not implement http.Flusher, causing a panic when
// httputil.ReverseProxy or WebSocket upgraders try to flush.
return strings.HasPrefix(path, "/admin") ||
strings.HasPrefix(path, "/_next") ||
strings.HasSuffix(path, "/ws")
},
}))
e.Use(corsMiddleware(cfg))
e.Use(i18n.Middleware())