Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ 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
Expand Down Expand Up @@ -47,7 +49,6 @@ CORS_ORIGINS=*
# =============================================================================
# COMPONENTS (enable/disable container profiles)
# =============================================================================
DASHBOARD_ENABLED=true # Enable dashboard container
PROXY_ENABLED=true # Enable Traefik proxy container

# =============================================================================
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- **BREAKING — single-port dashboard: the API now serves the bundled dashboard SPA.** In production the
NestJS API serves the built dashboard from its own port (default `2785`) via `@nestjs/serve-static`, so
there is no separate dashboard container and the UI is available by default wherever the API runs. `/api`
and `/socket.io` are excluded so they keep returning real API/WebSocket responses. Opt out with
`SERVE_DASHBOARD=false`. Dev is unchanged: `npm run dev` still runs the Vite dev server on `:2886` (HMR)
proxying to the API. Split-origin hosting (dashboard on a separate origin/CDN) still works: build with
`VITE_API_URL=<api-origin>` and host `dashboard/dist` anywhere. (#275)
- 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)

### Added

- `npm run build:all` (build API + dashboard) and `npm run prod` (build then serve) for running the
production build directly without Docker. (#275)

### Migration

- 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)

## [0.3.0] - 2026-06-18

Engine pluggability and plugin extensibility. OpenWA can now run on a second, browser-free WhatsApp engine
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ RUN npm ci
# Copy source code
COPY . .

# Build the application
RUN npm run build
# Build the API (dist/) and the dashboard SPA (dashboard/dist/). The root `npm ci` above
# ran before the dashboard source was copied, so its postinstall hook skipped the dashboard
# deps - install them explicitly here (npm ci, reproducible from dashboard/package-lock.json).
RUN npm run build && npm run dashboard:ci && npm run dashboard:build

# ===== Stage 2: Production =====
FROM docker.io/node:22-slim AS production
Expand Down Expand Up @@ -71,6 +73,10 @@ RUN npm ci --omit=dev && npm cache clean --force
# Copy built application from builder stage
COPY --from=builder /app/dist ./dist

# Copy the bundled dashboard SPA; ServeStaticModule serves it from this same process/port
# (app.module.ts resolves dashboard/dist relative to dist/). Single container, single port.
COPY --from=builder /app/dashboard/dist ./dashboard/dist

# Create data directories with correct ownership
RUN mkdir -p ./data/sessions ./data/media && \
chown -R openwa:openwa /app
Expand Down
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ git clone https://github.com/rmyndharis/OpenWA.git
cd OpenWA
docker compose -f docker-compose.dev.yml up -d

# Access
# Dashboard: http://localhost:2886
# Access (the dashboard is bundled into the API image and served on the same port)
# Dashboard: http://localhost:2785
# API: http://localhost:2785/api
# Swagger: http://localhost:2785/api/docs
```
Expand Down Expand Up @@ -134,7 +134,7 @@ npm install
# Start API + Dashboard (config is auto-generated on first run)
npm run dev

# Access
# Access (in dev the dashboard runs on the Vite server with hot reload)
# Dashboard: http://localhost:2886
# API: http://localhost:2785/api
# Swagger: http://localhost:2785/api/docs
Expand Down Expand Up @@ -187,22 +187,24 @@ docker compose up -d
# With PostgreSQL database
docker compose --profile postgres up -d

# Full stack (PostgreSQL, Redis, Dashboard, Traefik)
# Full stack (PostgreSQL, Redis, Traefik)
docker compose --profile full up -d
```

| Profile | Services |
| ---------------- | --------------------- |
| `postgres` | PostgreSQL database |
| `redis` | Redis cache |
| `minio` | S3-compatible storage |
| `with-dashboard` | Web dashboard |
| `with-proxy` | Traefik reverse proxy |
| `full` | All services above |
| Profile | Services |
| ------------ | --------------------- |
| `postgres` | PostgreSQL database |
| `redis` | Redis cache |
| `minio` | S3-compatible storage |
| `with-proxy` | Traefik reverse proxy |
| `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.

> **Development vs Production**
>
> - Development (`docker-compose.dev.yml`): SQLite, local storage, both API & Dashboard included
> - Development (`docker-compose.dev.yml`): SQLite, local storage, API serves the bundled dashboard
> - Production (`docker-compose.yml`): Configurable database, profiles for optional services
>
> Official GHCR images are published as multi-arch manifests for:
Expand All @@ -211,11 +213,11 @@ docker compose --profile full up -d

## 🔌 Ports

| Service | Port | Description |
| --------- | --------------- | ------------------------ |
| API | `2785` | REST API endpoints |
| Dashboard | `2886` | Web management interface |
| 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`) |

---

Expand Down
28 changes: 0 additions & 28 deletions dashboard/Dockerfile

This file was deleted.

50 changes: 0 additions & 50 deletions dashboard/Dockerfile.traefik

This file was deleted.

6 changes: 5 additions & 1 deletion dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ npm install
npm run dev
```

Dashboard will be available at `http://localhost:2886`
Dashboard will be available at `http://localhost:2886` (Vite dev server with hot reload; it
proxies `/api` + `/socket.io` to the NestJS API on `:2785`).

In production the build (`npm run build` → `dist/`) is served by the NestJS API itself on the
same port via `@nestjs/serve-static`, so there is no separate dashboard container.

### Production Build

Expand Down
35 changes: 0 additions & 35 deletions dashboard/nginx.conf

This file was deleted.

16 changes: 2 additions & 14 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,8 @@ services:
retries: 3
start_period: 30s

# Dashboard Frontend
dashboard:
build:
context: ./dashboard
dockerfile: Dockerfile
container_name: openwa-dashboard
ports:
- '${BIND_HOST:-127.0.0.1}:2886:80'
volumes:
- ./dashboard/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
openwa:
condition: service_healthy
restart: unless-stopped
# The dashboard SPA is bundled into the image and served by NestJS on the same port:
# open http://localhost:2785 — there is no separate dashboard container.

networks:
default:
Expand Down
21 changes: 3 additions & 18 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,9 @@ services:
- 'com.openwa.service=api'
- 'com.openwa.core=true'

# ===== CORE: Dashboard Frontend =====
dashboard:
build:
context: ./dashboard
dockerfile: Dockerfile.traefik
container_name: openwa-dashboard
profiles: ['with-dashboard', 'full']
restart: unless-stopped
# App network only — the dashboard must NOT be able to reach docker-proxy:2375 (H6).
networks:
- openwa-network
expose:
- '80'
depends_on:
- openwa-api
labels:
- 'com.openwa.service=dashboard'
- '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.

# ===== OPTIONAL: Built-in PostgreSQL =====
postgres:
Expand Down
11 changes: 2 additions & 9 deletions docs/10-devops-infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,8 @@ services:
ports:
- "6379:6379"

dashboard:
build:
context: ./dashboard
ports:
- "2886:2886"
environment:
- VITE_API_URL=http://localhost:2785
depends_on:
- app
# No separate dashboard service: the `app` image bundles the dashboard SPA and serves it
# from the same port (2785) via NestJS. Open http://localhost:2785 for the UI.

volumes:
postgres-data:
Expand Down
3 changes: 1 addition & 2 deletions docs/12-troubleshooting-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,10 @@ and no unqualified-search registries are defined

**Cause:** Podman rootless mode does not fall back to Docker Hub for unqualified image names.

**Fix:** All `FROM` directives in `Dockerfile` and `dashboard/Dockerfile` must use fully-qualified names:
**Fix:** All `FROM` directives in the `Dockerfile` must use fully-qualified names:

```dockerfile
FROM docker.io/node:22-slim
FROM docker.io/nginx:alpine
```

---
Expand Down
2 changes: 1 addition & 1 deletion docs/14-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-dashboard --profile with-proxy up -d
docker compose --profile with-proxy up -d

# Step 4: Import data to new database
curl -X POST 'http://localhost:2785/api/infra/import-data' \
Expand Down
Loading
Loading