diff --git a/Dockerfile b/Dockerfile index 9034defd85..6a34858efe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -118,4 +118,4 @@ EXPOSE 8080 # tini as PID 1 for signal handling; entrypoint handles privilege drop ENTRYPOINT ["tini", "--", "docker-entrypoint.sh"] -CMD ["kandev", "start", "--backend-port", "8080", "--web-port", "3000", "--verbose"] +CMD ["kandev", "start", "--backend-port", "8080", "--web-port", "37429", "--verbose"] diff --git a/Makefile b/Makefile index 4b275c4eb1..87454826cc 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ help: @echo "Development Commands:" @echo " dev Run backend + web via CLI (auto ports)" @echo " dev-backend Run backend in development mode (port 8080)" - @echo " dev-web Run web app in development mode (port 3000)" + @echo " dev-web Run web app in development mode (port 37429)" @echo " dev Note: Uses apps/cli launcher (auto ports)" @echo "" @echo "Production Commands:" @@ -121,8 +121,8 @@ dev-backend: .PHONY: dev-web dev-web: - @echo "Starting web app on http://localhost:3000" - @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web dev + @echo "Starting web app on http://localhost:37429" + @cd $(APPS_DIR) && PORT=37429 $(PNPM) --filter @kandev/web dev # # Build diff --git a/README.md b/README.md index 585b6575b9..5a1aba8dd6 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ make dev # Or run separately make dev-backend # Backend on :8080 -make dev-web # Frontend on :3000 +make dev-web # Frontend on :37429 ``` ### Testing & Linting diff --git a/apps/backend/cmd/kandev/helpers.go b/apps/backend/cmd/kandev/helpers.go index b3e2b0a90c..8f2514023d 100644 --- a/apps/backend/cmd/kandev/helpers.go +++ b/apps/backend/cmd/kandev/helpers.go @@ -468,10 +468,9 @@ func registerRoutes(p routeParams) { } else { proxy := httputil.NewSingleHostReverseProxy(target) proxy.FlushInterval = -1 - // Custom error handler to prevent the default behavior of panicking - // with http.ErrAbortHandler when the upstream connection fails or the - // client disconnects. Without this, Gin's recovery middleware catches - // the panic and logs a full (harmless but noisy) stack trace. + // ErrorHandler catches upstream failures (e.g., connection refused) + // that occur before any response headers are written, returning a + // clean 502 instead of letting the default handler log an error. proxy.ErrorHandler = func(w http.ResponseWriter, _ *http.Request, err error) { p.log.Debug("Web proxy error", zap.Error(err)) w.WriteHeader(http.StatusBadGateway) @@ -482,6 +481,19 @@ func registerRoutes(p routeParams) { c.AbortWithStatus(http.StatusNotFound) return } + // httputil.ReverseProxy panics with http.ErrAbortHandler as an + // intentional stdlib signal when a streaming response is aborted + // (e.g., the client disconnects mid-body, or the upstream dies + // after response headers were already written). net/http.Server + // understands this sentinel panic and closes the connection + // quietly, but Gin's recovery middleware catches it first and + // logs a noisy stack trace. Swallow that specific panic here + // while letting any other panic bubble up to Gin's recovery. + defer func() { + if r := recover(); r != nil && r != http.ErrAbortHandler { + panic(r) + } + }() proxy.ServeHTTP(c.Writer, c.Request) }) p.log.Info("Web reverse proxy enabled", zap.String("target", p.webInternalURL)) diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index ca57f05bad..b4e95e4729 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -3,7 +3,7 @@ import path from "node:path"; // Default service ports (will auto-fallback if busy). export const DEFAULT_BACKEND_PORT = 8080; -export const DEFAULT_WEB_PORT = 3000; +export const DEFAULT_WEB_PORT = 37429; export const DEFAULT_AGENTCTL_PORT = 9999; export const DEFAULT_MCP_PORT = 9090; diff --git a/apps/cli/src/run.test.ts b/apps/cli/src/run.test.ts index a97ba71f03..5e22956b5e 100644 --- a/apps/cli/src/run.test.ts +++ b/apps/cli/src/run.test.ts @@ -14,7 +14,7 @@ vi.mock("./constants", () => ({ }, DATA_DIR: "/tmp/kandev-test-data", DEFAULT_BACKEND_PORT: 8080, - DEFAULT_WEB_PORT: 3000, + DEFAULT_WEB_PORT: 37429, DEFAULT_AGENTCTL_PORT: 9999, HEALTH_TIMEOUT_MS_RELEASE: 15000, RANDOM_PORT_MIN: 10000, diff --git a/apps/cli/src/shared.ts b/apps/cli/src/shared.ts index 26ef3b9d66..e19121ebe6 100644 --- a/apps/cli/src/shared.ts +++ b/apps/cli/src/shared.ts @@ -96,7 +96,7 @@ export function buildWebEnv(options: WebEnvOptions): NodeJS.ProcessEnv { ...process.env, // Server-side: full localhost URL for SSR fetches (Next.js SSR → Go backend) KANDEV_API_BASE_URL: ports.backendUrl, - // Client-side: port injection for dev mode (browser on :3000, API on :8080). + // Client-side: port injection for dev mode (browser on web port, API on backend port). // In production, the backend reverse-proxies Next.js so the client uses same-origin. NEXT_PUBLIC_KANDEV_API_PORT: String(ports.backendPort), PORT: String(ports.webPort), diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 615b54099e..f05130dd6f 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -2,7 +2,7 @@ import type { NextConfig } from "next"; const defaultDevOrigins = [ "localhost", - "localhost:3000", + "localhost:37429", "192.168.1.116", // Maintainer dev server "100.105.155.17", // Maintainer dev server (Tailscale) ]; diff --git a/docs/cli.md b/docs/cli.md index 3d82cd7a19..c7e72f6697 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -50,7 +50,7 @@ npm install -g kandev # Run the latest release (recommended for users) npx kandev -# Opens the app at http://localhost:3000 (or next available port) +# Opens the app at http://localhost:37429 (or next available port) ``` ## Commands @@ -113,7 +113,7 @@ npx kandev start |--------|-------------|---------| | `--version ` | Use a specific release version | `--version v0.1.0` | | `--backend-port ` | Override backend port | `--backend-port 8080` | -| `--web-port ` | Override web port | `--web-port 3000` | +| `--web-port ` | Override web port | `--web-port 37429` | | `--help`, `-h` | Show help | `--help` | ### Examples @@ -126,7 +126,7 @@ npx kandev --version v0.1.0 npx kandev --backend-port 18080 --web-port 13000 # Dev mode with custom ports -npx kandev dev --backend-port 8080 --web-port 3000 +npx kandev dev --backend-port 8080 --web-port 13000 ``` ## Port Selection @@ -136,9 +136,9 @@ By default, the CLI automatically finds available ports: | Service | Default Port | Fallback | |---------|--------------|----------| | Backend | 8080 | Auto-selects from 10000-60000 | -| Web | 3000 | Auto-selects from 10000-60000 | -| AgentCtl | 8081 | Auto-selects from 10000-60000 | -| MCP Server | 8082 | Auto-selects from 10000-60000 | +| Web | 37429 | Auto-selects from 10000-60000 | +| AgentCtl | 9999 | Auto-selects from 10000-60000 | +| MCP Server | 9090 | Auto-selects from 10000-60000 | If the default port is in use, the CLI finds the next available port automatically. @@ -186,7 +186,7 @@ See `make help` for all available commands. The CLI automatically finds available ports. If you need a specific port: ```bash -npx kandev --backend-port 8080 --web-port 3000 +npx kandev --backend-port 8080 --web-port 37429 ``` ### Backend Takes Too Long to Start