mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-23 18:17:44 +02:00
* fix(email): wire SMTP_EHLO_NAME through self-host config + docs Follow-up to #3679, which added SMTP_EHLO_NAME in code but never exposed it to operators. - docker-compose.selfhost.yml: pass SMTP_EHLO_NAME through to the backend container. The compose env block is an explicit allowlist, so without this the override set in .env was silently dropped and never reached the process — making the escape hatch unusable on the docker path. - Document the var alongside its SMTP_* siblings: .env.example, SELF_HOSTING_ADVANCED.md, environment-variables.mdx, auth-setup.mdx, and self-host-quickstart.mdx (the last two with a strict-relay example). - email.go: log when os.Hostname() fails instead of silently falling back to net/smtp's lazy "localhost" — the exact greeting strict relays reject. - Add TestNewEmailService_EHLOName covering the env override, trimming, and the hostname fallback. MUL-2984 Co-authored-by: multica-agent <github@multica.ai> * fix(email): gate EHLO resolution to SMTP mode + sync docs to zh/ja/ko Addresses review nits on this PR: - email.go: resolve smtpEHLOName only when SMTP_HOST is set, so the Resend / DEV-stdout paths never call os.Hostname() or emit its failure log. The EHLO name is only ever used on the SMTP send path. - docs: add SMTP_EHLO_NAME to the zh/ja/ko variants of environment-variables, self-host-quickstart, and auth-setup, in sync with the English docs updated earlier in this PR. Note: the ja/ko self-host-quickstart and auth-setup pages were already missing the port-465 implicit-TLS example (pre-existing i18n drift from an earlier SMTP_TLS change, unrelated to this PR); the new EHLO block is inserted at the correct logical anchor regardless. A full ja/ko re-sync is left as a separate follow-up. MUL-2984 Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: J <j@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
108 lines
4.5 KiB
YAML
108 lines
4.5 KiB
YAML
# Self-hosting Docker Compose — starts PostgreSQL, backend, and frontend.
|
|
#
|
|
# Services bind to 127.0.0.1 only. For cross-machine or public access, front
|
|
# them with a reverse proxy (Caddy / nginx / Cloudflare Tunnel) that terminates
|
|
# TLS and forwards to 127.0.0.1:8080 (backend) and 127.0.0.1:3000 (frontend).
|
|
# Do NOT change these bindings to 0.0.0.0 — Docker bypasses host firewalls
|
|
# (UFW/iptables) by default, so the raw ports would be exposed to the internet
|
|
# with the default JWT_SECRET and Postgres credentials. See:
|
|
# apps/docs/content/docs/self-host-quickstart.mdx
|
|
#
|
|
# Usage:
|
|
# cp .env.example .env
|
|
# # Edit .env — change JWT_SECRET at minimum
|
|
# 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}}}}
|
|
|
|
name: multica
|
|
|
|
services:
|
|
postgres:
|
|
image: pgvector/pgvector:pg17
|
|
environment:
|
|
POSTGRES_DB: ${POSTGRES_DB:-multica}
|
|
POSTGRES_USER: ${POSTGRES_USER:-multica}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-multica}
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-multica} -d ${POSTGRES_DB:-multica}"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
backend:
|
|
image: ${MULTICA_BACKEND_IMAGE:-ghcr.io/multica-ai/multica-backend}:${MULTICA_IMAGE_TAG:-latest}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "127.0.0.1:${BACKEND_PORT:-${API_PORT:-${SERVER_PORT:-${PORT:-8080}}}}:8080"
|
|
volumes:
|
|
- backend_uploads:/app/data/uploads
|
|
environment:
|
|
DATABASE_URL: postgres://${POSTGRES_USER:-multica}:${POSTGRES_PASSWORD:-multica}@postgres:5432/${POSTGRES_DB:-multica}?sslmode=disable
|
|
PORT: "8080"
|
|
METRICS_ADDR: ${METRICS_ADDR:-}
|
|
JWT_SECRET: ${JWT_SECRET:-change-me-in-production}
|
|
FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:${FRONTEND_PORT:-3000}}
|
|
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
|
|
RESEND_API_KEY: ${RESEND_API_KEY:-}
|
|
RESEND_FROM_EMAIL: ${RESEND_FROM_EMAIL:-noreply@multica.ai}
|
|
SMTP_HOST: ${SMTP_HOST:-}
|
|
SMTP_PORT: ${SMTP_PORT:-25}
|
|
SMTP_USERNAME: ${SMTP_USERNAME:-}
|
|
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
|
|
SMTP_TLS: ${SMTP_TLS:-}
|
|
SMTP_TLS_INSECURE: ${SMTP_TLS_INSECURE:-false}
|
|
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}
|
|
S3_BUCKET: ${S3_BUCKET:-}
|
|
S3_REGION: ${S3_REGION:-us-west-2}
|
|
CLOUDFRONT_DOMAIN: ${CLOUDFRONT_DOMAIN:-}
|
|
CLOUDFRONT_KEY_PAIR_ID: ${CLOUDFRONT_KEY_PAIR_ID:-}
|
|
CLOUDFRONT_PRIVATE_KEY: ${CLOUDFRONT_PRIVATE_KEY:-}
|
|
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}}
|
|
ALLOW_SIGNUP: ${ALLOW_SIGNUP:-true}
|
|
ALLOWED_EMAILS: ${ALLOWED_EMAILS:-}
|
|
ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-}
|
|
DISABLE_WORKSPACE_CREATION: ${DISABLE_WORKSPACE_CREATION:-}
|
|
GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
|
|
GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-}
|
|
# Public URL the API is reachable at from the open internet, no
|
|
# trailing slash. Used to mint absolute webhook URLs for autopilot
|
|
# webhook triggers. Leave unset behind a same-origin reverse proxy
|
|
# (e.g. plain localhost dev); the frontend will compose the URL
|
|
# from window.origin + webhook_path in that case. Headers are
|
|
# intentionally NOT used to derive this value, to avoid Host /
|
|
# X-Forwarded-Host spoofing on misconfigured proxies.
|
|
MULTICA_PUBLIC_URL: ${MULTICA_PUBLIC_URL:-}
|
|
# Comma-separated CIDRs whose source IP is allowed to set
|
|
# X-Forwarded-For / X-Real-IP for the webhook per-IP rate limiter.
|
|
# Empty default = headers ignored, RemoteAddr used. Set e.g.
|
|
# "127.0.0.1/32" when running behind a same-host reverse proxy.
|
|
MULTICA_TRUSTED_PROXIES: ${MULTICA_TRUSTED_PROXIES:-}
|
|
restart: unless-stopped
|
|
|
|
frontend:
|
|
image: ${MULTICA_WEB_IMAGE:-ghcr.io/multica-ai/multica-web}:${MULTICA_IMAGE_TAG:-latest}
|
|
depends_on:
|
|
- backend
|
|
ports:
|
|
- "127.0.0.1:${FRONTEND_PORT:-3000}:3000"
|
|
environment:
|
|
HOSTNAME: "0.0.0.0"
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
pgdata:
|
|
backend_uploads:
|