diff --git a/.env.example b/.env.example index 9ee928e8..5360cf5a 100644 --- a/.env.example +++ b/.env.example @@ -16,9 +16,6 @@ AUTO_START_SESSIONS=false # Domain Configuration DOMAIN=localhost -# The dashboard is bundled into the API and served on the API port. This only sets the -# public port for the optional Traefik proxy (with-proxy profile). -DASHBOARD_PORT=2886 # Host interface the dev compose binds the API/dashboard ports to. Defaults to 127.0.0.1 # (localhost only). Set to 0.0.0.0 to reach the dev stack from another machine (e.g. a remote @@ -35,22 +32,16 @@ DASHBOARD_PORT=2886 # blocked) — set explicit origin(s) there, e.g. https://dashboard.yourdomain.com CORS_ORIGINS=* -# SSL/TLS (enable for production with real domain) -# TRAEFIK_ACME_EMAIL=admin@yourdomain.com -# TRAEFIK_ACME_STORAGE=/letsencrypt/acme.json +# SSL/TLS: terminate TLS at your own reverse proxy (nginx, Caddy, a cloud load +# balancer, or a k8s Ingress) in front of the API — see docs/12-troubleshooting-faq.md. # Trusted reverse proxies (comma-separated IPs/CIDRs) whose X-Forwarded-For # header is trusted to determine the real client IP for API-key IP whitelisting. # Leave empty (default) to ignore X-Forwarded-For and use the direct socket -# address, which prevents IP spoofing. If you run behind Traefik/nginx AND use +# address, which prevents IP spoofing. If you run behind a reverse proxy AND use # per-key allowedIps restrictions, set this to the proxy's address/subnet, e.g.: # TRUSTED_PROXIES=172.18.0.0/16 -# ============================================================================= -# COMPONENTS (enable/disable container profiles) -# ============================================================================= -PROXY_ENABLED=true # Enable Traefik proxy container - # ============================================================================= # ENGINE CONFIGURATION # ============================================================================= diff --git a/CHANGELOG.md b/CHANGELOG.md index fb052a9a..89994842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The API's Content-Security-Policy now allows `https://fonts.googleapis.com` (`style-src`) and `https://fonts.gstatic.com` (`font-src`) so the dashboard's webfonts load now that it is served under the API's CSP. (#275) +- **BREAKING — removed the bundled Traefik reverse proxy.** With the API serving both the UI and the API + on one port, the shipped Traefik service was a single-backend passthrough that added no value (it + terminated no TLS out of the box). Removed the `traefik` service, the `traefik/` configs, and the + `with-proxy` profile. For TLS / public exposure, put your own reverse proxy (nginx, Caddy, a cloud load + balancer, or a k8s Ingress) in front of the API — see `docs/12-troubleshooting-faq.md`. (#276) ### Added @@ -29,8 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The dashboard moved from `:2886` (separate nginx container) to the API port `:2785`. Update bookmarks, monitoring, and any external reverse-proxy config accordingly. (#275) -- The `with-dashboard` compose profile and the `DASHBOARD_ENABLED` env var are removed (the dashboard ships - with the API; ignored if still set). (#275) +- The `with-dashboard` and `with-proxy` compose profiles are removed, and the `DASHBOARD_PORT`, + `PROXY_ENABLED`, and `DASHBOARD_ENABLED` env vars are gone (silently ignored if still set). `--profile + full` now starts the optional datastores (postgres, redis, minio). If you relied on the bundled Traefik + for TLS, front the API with your own reverse proxy. (#275, #276) ## [0.3.0] - 2026-06-18 diff --git a/README.md b/README.md index 3ba20d17..a8939430 100644 --- a/README.md +++ b/README.md @@ -187,20 +187,21 @@ docker compose up -d # With PostgreSQL database docker compose --profile postgres up -d -# Full stack (PostgreSQL, Redis, Traefik) +# Full stack (PostgreSQL, Redis, MinIO) docker compose --profile full up -d ``` -| Profile | Services | -| ------------ | --------------------- | -| `postgres` | PostgreSQL database | -| `redis` | Redis cache | -| `minio` | S3-compatible storage | -| `with-proxy` | Traefik reverse proxy | -| `full` | All services above | +| Profile | Services | +| ---------- | --------------------- | +| `postgres` | PostgreSQL database | +| `redis` | Redis cache | +| `minio` | S3-compatible storage | +| `full` | All services above | > The dashboard is bundled into the API image and served by NestJS on the API port, so it -> needs no profile - it is always available wherever `openwa-api` runs. +> needs no profile — it is always available wherever `openwa-api` runs. For TLS/public exposure, +> put your own reverse proxy (nginx, Caddy, a cloud load balancer, or a k8s Ingress) in front; +> see the nginx example in `docs/12-troubleshooting-faq.md`. > **Development vs Production** > @@ -213,10 +214,10 @@ docker compose --profile full up -d ## 🔌 Ports -| Service | Port | Description | -| --------------- | --------------- | ----------------------------------------------- | -| API & Dashboard | `2785` | REST API + bundled web dashboard (same port) | -| Swagger | `2785/api/docs` | Interactive API docs | +| Service | Port | Description | +| --------------- | --------------- | --------------------------------------------- | +| API & Dashboard | `2785` | REST API + bundled web dashboard (same port) | +| Swagger | `2785/api/docs` | Interactive API docs | | Dashboard (dev) | `2886` | Vite dev server with hot reload (`npm run dev`) | --- diff --git a/docker-compose.yml b/docker-compose.yml index 10c3f375..02c192f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,24 +27,6 @@ services: - 'com.openwa.service=docker-proxy' - 'com.openwa.core=true' - # ===== CORE: Traefik Reverse Proxy ===== - traefik: - image: traefik:v3.0 - container_name: openwa-traefik - profiles: ['with-proxy', 'full'] - restart: unless-stopped - networks: - - openwa-network - ports: - - '127.0.0.1:${DASHBOARD_PORT:-2886}:80' - - '127.0.0.1:8080:8080' # Traefik dashboard - volumes: - - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro - - ./traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro - labels: - - 'com.openwa.service=proxy' - - 'com.openwa.core=true' - # ===== CORE: OpenWA Backend API ===== openwa-api: build: @@ -52,7 +34,7 @@ services: dockerfile: Dockerfile container_name: openwa-api restart: unless-stopped - # App network for datastores/traefik/dashboard + the isolated network to reach + # App network for datastores + the isolated network to reach # docker-proxy (DOCKER_HOST). createService() in docker.service.ts attaches # orchestrated containers to the literal `openwa-network`, so its name is fixed. networks: @@ -171,14 +153,14 @@ services: - 'com.openwa.core=true' # The dashboard SPA is now bundled into the openwa-api image and served by NestJS on the - # same port (2785) - there is no separate dashboard container. Reach it at the openwa-api - # port directly, or via Traefik (above) when the with-proxy profile is enabled. + # same port (2785) — there is no separate dashboard container. Reach it at the openwa-api + # port directly; put your own TLS reverse proxy (nginx/Caddy/cloud LB) in front if needed. # ===== OPTIONAL: Built-in PostgreSQL ===== postgres: image: postgres:16-alpine container_name: openwa-postgres - profiles: ['postgres'] + profiles: ['postgres', 'full'] restart: unless-stopped networks: - openwa-network @@ -206,7 +188,7 @@ services: redis: image: redis:7-alpine container_name: openwa-redis - profiles: ['redis'] + profiles: ['redis', 'full'] restart: unless-stopped networks: - openwa-network @@ -228,7 +210,7 @@ services: minio: image: minio/minio container_name: openwa-minio - profiles: ['minio'] + profiles: ['minio', 'full'] restart: unless-stopped networks: - openwa-network @@ -265,8 +247,8 @@ volumes: driver: local networks: - # Application network — datastores, traefik, dashboard, and openwa-api. Keep the - # name `openwa-network`: docker.service.ts attaches orchestrated containers to it by literal name. + # Application network — datastores and openwa-api. Keep the name `openwa-network`: + # docker.service.ts attaches orchestrated containers to it by literal name. openwa-network: name: openwa-network # Isolated network for the Docker socket proxy. `internal: true` means no external diff --git a/docs/14-migration-guide.md b/docs/14-migration-guide.md index 466581c3..a421d7f8 100644 --- a/docs/14-migration-guide.md +++ b/docs/14-migration-guide.md @@ -103,7 +103,7 @@ curl -s 'http://localhost:2785/api/infra/export-data' \ # POSTGRES_BUILTIN=true # Step 3: Restart with new configuration -docker compose --profile with-proxy up -d +docker compose --profile postgres up -d # Step 4: Import data to new database curl -X POST 'http://localhost:2785/api/infra/import-data' \ diff --git a/docs/DOCKER_ID.md b/docs/DOCKER_ID.md index 595165ce..c108b373 100644 --- a/docs/DOCKER_ID.md +++ b/docs/DOCKER_ID.md @@ -22,7 +22,6 @@ OpenWA menggunakan fitur **Profiles** di Docker Compose untuk mempermudah orkest * **`postgres`**: Database PostgreSQL (bawaan docker). * **`redis`**: Caching layer untuk performa tinggi. * **`minio`**: Penyimpanan berkas media yang kompatibel dengan Amazon S3. -* **`with-proxy`**: Reverse proxy Traefik otomatis. --- @@ -75,7 +74,7 @@ Cocok untuk keandalan data lebih tinggi: docker compose --profile postgres up -d ``` -#### Skenario C: Full Stack (PostgreSQL + Redis + S3 MinIO + Traefik) +#### Skenario C: Full Stack (PostgreSQL + Redis + S3 MinIO) Cocok untuk skala enterprise dengan multi-sesi aktif: ```bash docker compose --profile full up -d @@ -90,7 +89,6 @@ Berikut variabel penting yang bisa disesuaikan di `.env`: | Nama Variabel | Nilai Default | Deskripsi | |---|---|---| | `API_PORT` | `2785` | Port REST API sekaligus Dashboard UI (disajikan oleh NestJS). | -| `DASHBOARD_PORT` | `2886` | Port publik untuk proxy Traefik opsional (profil `with-proxy`). | | `DATABASE_TYPE` | `sqlite` | Jenis database yang digunakan (`sqlite` atau `postgres`). | | `DATABASE_NAME` | `/app/data/openwa.sqlite` | Lokasi database SQLite atau nama database PostgreSQL. | | `ENGINE_TYPE` | `whatsapp-web.js` | Driver/mesin engine WhatsApp yang digunakan. | diff --git a/docs/README.md b/docs/README.md index cfb39c69..fba27ad6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -83,7 +83,7 @@ Access: - Swagger: `http://localhost:2785/api/docs` - Health: `http://localhost:2785/api/health` -### Option B: Docker (Traefik + API, dashboard bundled into the API) +### Option B: Docker (single container: API + Dashboard) ```bash # Clone repository @@ -94,12 +94,11 @@ cd OpenWA docker compose up -d ``` -Access: +Access (the dashboard is bundled into the API and served on the same port): -- Dashboard: `http://localhost:2785` (bundled into the API, same port) +- Dashboard: `http://localhost:2785` - API: `http://localhost:2785/api` - Swagger: `http://localhost:2785/api/docs` -- Traefik (optional, with-proxy profile): `http://localhost:2886` ### API Key @@ -173,7 +172,7 @@ socket.on('message', msg => { | WebSocket Events (Socket.IO) | Ready | | Multi-session Support | Ready | | Web Dashboard | Ready | -| Docker + Traefik Deployment | Ready | +| Docker Deployment | Ready | | Webhooks with HMAC Signature | Ready | | SQLite / PostgreSQL Storage | Ready | | API Key Authentication & Roles | Ready | @@ -204,7 +203,7 @@ socket.on('message', msg => { OpenWA/ ├── src/ # Backend source code ├── dashboard/ # Frontend dashboard -├── docker-compose.yml # Traefik (optional) + API (serves bundled dashboard) +├── docker-compose.yml # API (serves bundled dashboard) + optional datastores ├── docker-compose.dev.yml # Dev-only compose ├── docs/ # Project documentation └── data/ # Local runtime data (sessions, media, api key) diff --git a/scripts/openwa.sh b/scripts/openwa.sh index 0a8c7ce3..d0b617ce 100755 --- a/scripts/openwa.sh +++ b/scripts/openwa.sh @@ -37,14 +37,8 @@ load_env() { get_profiles() { local profiles="" - # Dashboard is bundled into the openwa-api image and served by NestJS on the API port - - # no separate container/profile needed. - - # Proxy (default: enabled) - if [ "${PROXY_ENABLED:-true}" = "true" ]; then - profiles="$profiles --profile with-proxy" - log_info "Proxy (Traefik): enabled" - fi + # Dashboard is bundled into the openwa-api image and served by NestJS on the API port — + # no separate container/profile needed. Put your own TLS proxy in front if required. # PostgreSQL (built-in) if [ "${DATABASE_TYPE:-sqlite}" = "postgres" ] && [ "${POSTGRES_BUILTIN:-false}" = "true" ]; then @@ -117,16 +111,13 @@ cmd_start() { log_success "OpenWA started successfully!" echo "" log_info "Dashboard & API: http://localhost:${API_PORT:-2785}" - if [ "${PROXY_ENABLED:-true}" = "true" ]; then - log_info "Public (Traefik): http://localhost:${DASHBOARD_PORT:-2886}" - fi } # Stop OpenWA cmd_stop() { log_info "Stopping OpenWA..." cd "$PROJECT_DIR" - docker compose --profile postgres --profile redis --profile minio --profile with-proxy down + docker compose --profile full down log_success "OpenWA stopped" } diff --git a/src/modules/docker/traefik-config.spec.ts b/src/modules/docker/traefik-config.spec.ts deleted file mode 100644 index 02a7a19a..00000000 --- a/src/modules/docker/traefik-config.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; -// js-yaml has no bundled types here; require + cast (matches the repo's plugin-loader pattern). -// eslint-disable-next-line @typescript-eslint/no-require-imports -const yaml = require('js-yaml') as { load: (src: string) => unknown }; - -interface TraefikConfig { - api?: { insecure?: boolean }; - entryPoints?: Record; -} - -/** Regression lock: the Traefik dashboard must not be served insecurely + TLS exists. */ -describe('traefik static config', () => { - const cfg = yaml.load(readFileSync(join(__dirname, '../../../traefik/traefik.yml'), 'utf8')) as TraefikConfig; - - it('does not enable the insecure API dashboard', () => { - expect(cfg.api?.insecure).toBe(false); - }); - - it('defines a TLS (websecure) entrypoint', () => { - expect(cfg.entryPoints?.websecure?.address).toBe(':443'); - }); -}); diff --git a/traefik/dynamic.yml b/traefik/dynamic.yml deleted file mode 100644 index f6184ce1..00000000 --- a/traefik/dynamic.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Traefik Dynamic Configuration -# -# The dashboard SPA is bundled into and served by openwa-api (NestJS) on the same port, -# so every route - UI, /api, and /socket.io - goes to the single api service. Traefik -# stays as the optional public entry point (TLS termination / single front-door). -http: - routers: - openwa: - rule: 'PathPrefix(`/`)' - entryPoints: - - web - service: api - - services: - api: - loadBalancer: - servers: - - url: 'http://openwa-api:2785' diff --git a/traefik/traefik.yml b/traefik/traefik.yml deleted file mode 100644 index 43c837d3..00000000 --- a/traefik/traefik.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Traefik Static Configuration -api: - dashboard: true - # M8: the dashboard is NOT served unauthenticated on :8080. Expose it, if needed, via a - # secured router (api@internal) behind auth + TLS — never with insecure mode in production. - insecure: false - -entryPoints: - web: - address: ':80' - # In production, redirect plain HTTP to HTTPS (enable once TLS is configured below): - # http: - # redirections: - # entryPoint: - # to: websecure - # scheme: https - websecure: - address: ':443' - -# TLS is deployment-specific (M8): configure a certificatesResolver (e.g. Let's Encrypt) or -# mount certificates via the file provider, OR terminate TLS at an external reverse proxy and -# keep Traefik on an internal network. Do not expose the API over plain :80 in production. - -providers: - file: - filename: /etc/traefik/dynamic.yml - watch: true - -log: - level: INFO