From 4df6c1468d1c970186ee7e373ba99e9833194174 Mon Sep 17 00:00:00 2001 From: Bohan Jiang <52446949+Bohan-J@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:43:10 +0800 Subject: [PATCH] fix: validate selfhost compose env defaults (#4138) Co-authored-by: J Co-authored-by: multica-agent --- .env.example | 25 ++++++++++++++----------- docker-compose.selfhost.yml | 10 +++++----- scripts/selfhost-config.test.sh | 14 +++++++------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/.env.example b/.env.example index 8dc31e93a..92c7ee816 100644 --- a/.env.example +++ b/.env.example @@ -21,11 +21,16 @@ APP_ENV= # 888888 and keep APP_ENV non-production. This is ignored when APP_ENV=production. MULTICA_DEV_VERIFICATION_CODE= PORT=8080 -# Optional aliases for the local/self-host backend port. If one is set, it -# takes precedence over PORT in compose, Makefile, and installer helpers. -# BACKEND_PORT=8080 +# Docker Compose consumes flat port values. Set BACKEND_PORT directly to +# override the backend host port. +BACKEND_PORT=8080 +# Optional aliases for local/self-host backend port helpers outside compose. # API_PORT=8080 # SERVER_PORT=8080 +FRONTEND_PORT=3000 +# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT. +# Set explicitly only when serving frontend on a different origin/domain. +FRONTEND_ORIGIN=http://localhost:${FRONTEND_PORT} # Prometheus metrics are disabled by default. When enabled, bind to loopback # unless you protect the listener with private networking, allowlists, or # proxy auth. Do not expose this endpoint through the public app/API ingress. @@ -35,9 +40,9 @@ JWT_SECRET=change-me-in-production # Derived by Makefile / local scripts from the backend port. # Set explicitly only when the daemon reaches the API through a different URL. # MULTICA_SERVER_URL=ws://localhost:8080/ws -# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT. +# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_ORIGIN. # Set explicitly only when the app's public URL differs from local frontend. -# MULTICA_APP_URL=http://localhost:3000 +MULTICA_APP_URL=${FRONTEND_ORIGIN} # Public URL the API is reachable at from the open internet (no trailing # slash). Used to mint absolute webhook URLs for autopilot webhook # triggers and to show correct daemon setup commands in the web UI. Leave @@ -112,9 +117,9 @@ SMTP_EHLO_NAME= # rebuild is needed. GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= -# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT. +# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_ORIGIN. # Set explicitly only when your OAuth callback URL differs from local frontend. -# GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback +GOOGLE_REDIRECT_URI=${FRONTEND_ORIGIN}/auth/callback # S3 / CloudFront # S3_BUCKET — bucket NAME only (e.g. "my-bucket"). Do NOT include the @@ -122,6 +127,8 @@ GOOGLE_CLIENT_SECRET= # from S3_BUCKET + S3_REGION. S3_REGION must match the bucket's real region. S3_BUCKET= S3_REGION=us-west-2 +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= # AWS_ENDPOINT_URL — optional S3-compatible endpoint (MinIO, RustFS, R2, etc.). # For internal Docker/VPC hosts such as http://rustfs:9000, leave # ATTACHMENT_DOWNLOAD_MODE=auto or set proxy explicitly so browsers/CLI do @@ -228,10 +235,6 @@ MULTICA_LARK_HTTP_BASE_URL= MULTICA_LARK_CALLBACK_BASE_URL= # Frontend -FRONTEND_PORT=3000 -# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT. -# Set explicitly only when serving frontend on a different origin/domain. -# FRONTEND_ORIGIN=http://localhost:3000 # Leave empty — auto-derived from page origin in browser, set by Makefile for local dev. # NEXT_PUBLIC_API_URL also feeds the Next.js SSR proxy when explicitly set. NEXT_PUBLIC_API_URL= diff --git a/docker-compose.selfhost.yml b/docker-compose.selfhost.yml index 17a747f8e..c2c516a73 100644 --- a/docker-compose.selfhost.yml +++ b/docker-compose.selfhost.yml @@ -14,7 +14,7 @@ # docker compose -f docker-compose.selfhost.yml up -d # # Frontend: http://localhost:${FRONTEND_PORT:-3000} -# Backend: http://localhost:${BACKEND_PORT:-${API_PORT:-${SERVER_PORT:-${PORT:-8080}}}} +# Backend: http://localhost:${BACKEND_PORT:-8080} name: multica @@ -44,7 +44,7 @@ services: postgres: condition: service_healthy ports: - - "127.0.0.1:${BACKEND_PORT:-${API_PORT:-${SERVER_PORT:-${PORT:-8080}}}}:8080" + - "127.0.0.1:${BACKEND_PORT:-8080}:8080" volumes: - backend_uploads:/app/data/uploads environment: @@ -52,7 +52,7 @@ services: PORT: "8080" METRICS_ADDR: ${METRICS_ADDR:-} JWT_SECRET: ${JWT_SECRET:-change-me-in-production} - FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:${FRONTEND_PORT:-3000}} + FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:3000} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-} RESEND_API_KEY: ${RESEND_API_KEY:-} RESEND_FROM_EMAIL: ${RESEND_FROM_EMAIL:-noreply@multica.ai} @@ -65,7 +65,7 @@ services: SMTP_EHLO_NAME: ${SMTP_EHLO_NAME:-} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} - GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:${FRONTEND_PORT:-3000}/auth/callback} + GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-http://localhost:3000/auth/callback} S3_BUCKET: ${S3_BUCKET:-} S3_REGION: ${S3_REGION:-us-west-2} AWS_ENDPOINT_URL: ${AWS_ENDPOINT_URL:-} @@ -79,7 +79,7 @@ services: COOKIE_DOMAIN: ${COOKIE_DOMAIN:-} APP_ENV: ${APP_ENV:-production} MULTICA_DEV_VERIFICATION_CODE: ${MULTICA_DEV_VERIFICATION_CODE:-} - MULTICA_APP_URL: ${MULTICA_APP_URL:-http://localhost:${FRONTEND_PORT:-3000}} + MULTICA_APP_URL: ${MULTICA_APP_URL:-http://localhost:3000} ALLOW_SIGNUP: ${ALLOW_SIGNUP:-true} ALLOWED_EMAILS: ${ALLOWED_EMAILS:-} ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-} diff --git a/scripts/selfhost-config.test.sh b/scripts/selfhost-config.test.sh index c1ef085ef..77323a0b9 100755 --- a/scripts/selfhost-config.test.sh +++ b/scripts/selfhost-config.test.sh @@ -28,9 +28,14 @@ require_env() { fi } +tmp_env="$(mktemp)" +trap 'rm -f "$tmp_env"' EXIT +sed 's/^FRONTEND_PORT=.*/FRONTEND_PORT=3100/' .env.example >"$tmp_env" +printf '\nBACKEND_PORT=9100\n' >>"$tmp_env" + config="$( - FRONTEND_PORT=3100 BACKEND_PORT=9100 docker compose \ - --env-file .env.example \ + docker compose \ + --env-file "$tmp_env" \ -f docker-compose.selfhost.yml \ config )" @@ -48,11 +53,6 @@ for script in scripts/dev.sh scripts/check.sh; do fi done -tmp_env="$(mktemp)" -trap 'rm -f "$tmp_env"' EXIT -sed 's/^FRONTEND_PORT=.*/FRONTEND_PORT=3100/' .env.example >"$tmp_env" -printf '\nBACKEND_PORT=9100\n' >>"$tmp_env" - local_env="$( env -i PATH="$PATH" bash -c ' set -euo pipefail