chore: initial scaffold with running local stack and portal auth
Brings up Dezky's local development environment end-to-end: Infrastructure (docker-compose): - Traefik v3.7 reverse proxy with mkcert TLS (v3.2 couldn't speak Docker API 1.54) - Postgres + Mongo + Redis with healthchecks and init script for per-service users - Authentik 2025.10 (server + worker) as OIDC IdP - Stalwart v0.16 mail server (image renamed from stalwartlabs/mail-server) - OCIS 7.0 with PROXY_TLS=false and OCIS_CONFIG_DIR=/etc/ocis so init writes where the server reads - Collabora office, plus the portal + provisioning service stubs - Docker network aliases on Traefik so containers resolve auth.dezky.local etc. through the network (not host /etc/hosts) - Docker socket mount parameterized for macOS Docker Desktop symlink path Authentik provisioning (done via API after stack boot): - ocis-provider (public client) + OCIS Files application - dezky-portal provider (confidential) + Dezky Portal application - Admin API token bound to akadmin manually since 2025.10's AUTHENTIK_BOOTSTRAP_TOKEN env var doesn't auto-materialize a token row Portal (apps/portal): - Nuxt 3 with nuxt-oidc-auth 1.0.0-beta.11 against generic 'oidc' preset - Global auth middleware; login at /auth/oidc/login redirects to Authentik - Visual implementation of Claude Design 'Auth' canvas: AuthShell, NodeMark, Auth* sub-components, design tokens as CSS custom properties - Pages: auth/login, auth/expired, auth/disabled, index (post-login landing) - mkcert root CA mounted into the portal so Node fetch trusts Authentik's self-signed cert (NODE_EXTRA_CA_CERTS) — dev only Docs: - AUTHENTIK-SETUP.md updated with manual token bind + portal provider scripted alternative - NEXT-STEPS.md: Phase 1 and Phase 2 marked done with file locations and dev-mode caveats Dev-mode shortcuts that need to be revisited before prod: - skipAccessTokenParsing on the OIDC config - NODE_EXTRA_CA_CERTS mkcert mount - Bootstrap password still the generated value in .env - Authentik admin token (dezky-bootstrap-token) is non-expiring
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Dezky PostgreSQL initialization
|
||||
# Creates databases and users for Authentik and OCIS.
|
||||
# Passwords come from env vars set in docker-compose.yml.
|
||||
|
||||
set -e
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
-- Authentik
|
||||
CREATE USER authentik WITH PASSWORD '${AUTHENTIK_DB_PASSWORD}';
|
||||
CREATE DATABASE authentik WITH OWNER authentik ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;
|
||||
GRANT ALL PRIVILEGES ON DATABASE authentik TO authentik;
|
||||
|
||||
-- OCIS (reserved for future use; OCIS uses internal storage in dev)
|
||||
CREATE USER ocis WITH PASSWORD '${OCIS_DB_PASSWORD}';
|
||||
CREATE DATABASE ocis WITH OWNER ocis ENCODING 'UTF8' TEMPLATE template0;
|
||||
GRANT ALL PRIVILEGES ON DATABASE ocis TO ocis;
|
||||
EOSQL
|
||||
|
||||
# Grant schema permissions inside each newly created DB
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname authentik <<-EOSQL
|
||||
GRANT ALL ON SCHEMA public TO authentik;
|
||||
EOSQL
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname ocis <<-EOSQL
|
||||
GRANT ALL ON SCHEMA public TO ocis;
|
||||
EOSQL
|
||||
|
||||
echo "Dezky PostgreSQL initialization complete."
|
||||
@@ -0,0 +1,91 @@
|
||||
# Stalwart Mail Server — Local Development Configuration
|
||||
#
|
||||
# This is a minimal config for local dev. Production config will have:
|
||||
# - Real TLS certs (Let's Encrypt)
|
||||
# - DKIM signing with real keys
|
||||
# - SPF/DMARC enforcement
|
||||
# - Rspamd integration
|
||||
# - Hetzner Object Storage for blob storage
|
||||
#
|
||||
# Reference: https://stalw.art/docs
|
||||
|
||||
[server]
|
||||
hostname = "mail.dezky.local"
|
||||
|
||||
[server.listener]
|
||||
"smtp" = { bind = "[::]:25", protocol = "smtp" }
|
||||
"submission" = { bind = "[::]:587", protocol = "smtp", tls.implicit = false }
|
||||
"submissions" = { bind = "[::]:465", protocol = "smtp", tls.implicit = true }
|
||||
"imap" = { bind = "[::]:143", protocol = "imap", tls.implicit = false }
|
||||
"imaps" = { bind = "[::]:993", protocol = "imap", tls.implicit = true }
|
||||
"sieve" = { bind = "[::]:4190", protocol = "managesieve" }
|
||||
"http" = { bind = "[::]:8080", protocol = "http" }
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Storage — RocksDB embedded for local dev (single-binary simplicity)
|
||||
# Production will use PostgreSQL backend
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[store."rocksdb"]
|
||||
type = "rocksdb"
|
||||
path = "/opt/stalwart/data"
|
||||
compression = "lz4"
|
||||
|
||||
[storage]
|
||||
data = "rocksdb"
|
||||
fts = "rocksdb"
|
||||
blob = "rocksdb"
|
||||
lookup = "rocksdb"
|
||||
directory = "internal"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Directory — internal user store for local dev
|
||||
# Production will wire OIDC to Authentik
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[directory."internal"]
|
||||
type = "internal"
|
||||
store = "rocksdb"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# TLS — Self-signed in dev, Traefik terminates the public-facing HTTPS
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[certificate."default"]
|
||||
cert = "%{file:/opt/stalwart/etc/tls/cert.pem}%"
|
||||
private-key = "%{file:/opt/stalwart/etc/tls/key.pem}%"
|
||||
default = true
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Authentication for local development
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[authentication]
|
||||
fallback-admin.user = "admin"
|
||||
fallback-admin.secret = "$env{STALWART_ADMIN_PASSWORD}"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Resolver — use the Docker DNS for local dev
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[resolver]
|
||||
type = "system"
|
||||
preserve-intermediates = true
|
||||
concurrency = 2
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Logging
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[tracer."stdout"]
|
||||
type = "stdout"
|
||||
level = "info"
|
||||
ansi = false
|
||||
enable = true
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Spam filtering — disabled in dev (no Rspamd configured)
|
||||
# Production: integrate Rspamd via milter
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
[spam-filter]
|
||||
enable = false
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Local development hint:
|
||||
# After first boot, create your first mailbox by visiting
|
||||
# https://mail.dezky.local and using admin credentials.
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
@@ -0,0 +1,51 @@
|
||||
# Traefik dynamic configuration — TLS certificates and middleware
|
||||
#
|
||||
# Uses the wildcard mkcert certificate for all *.dezky.local hostnames.
|
||||
# This file is watched and reloaded automatically by Traefik.
|
||||
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certs/dezky.local.pem
|
||||
keyFile: /certs/dezky.local-key.pem
|
||||
stores:
|
||||
- default
|
||||
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /certs/dezky.local.pem
|
||||
keyFile: /certs/dezky.local-key.pem
|
||||
|
||||
http:
|
||||
middlewares:
|
||||
# Strong security headers for all services
|
||||
secure-headers:
|
||||
headers:
|
||||
frameDeny: false # OCIS/Collabora need iframes
|
||||
sslRedirect: true
|
||||
browserXssFilter: true
|
||||
contentTypeNosniff: true
|
||||
forceSTSHeader: true
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
stsSeconds: 15552000
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
|
||||
# CORS for API calls between portal and provisioning service
|
||||
cors:
|
||||
headers:
|
||||
accessControlAllowMethods:
|
||||
- "GET"
|
||||
- "POST"
|
||||
- "PUT"
|
||||
- "PATCH"
|
||||
- "DELETE"
|
||||
- "OPTIONS"
|
||||
accessControlAllowOriginListRegex:
|
||||
- "^https://([a-z0-9-]+\\.)?dezky\\.local$"
|
||||
accessControlAllowHeaders:
|
||||
- "Content-Type"
|
||||
- "Authorization"
|
||||
- "X-Requested-With"
|
||||
accessControlMaxAge: 86400
|
||||
addVaryHeader: true
|
||||
@@ -0,0 +1,43 @@
|
||||
# Traefik static configuration for Dezky local development
|
||||
#
|
||||
# Provides TLS termination using mkcert-generated wildcard certificate.
|
||||
# Auto-discovers services via Docker labels.
|
||||
|
||||
global:
|
||||
checkNewVersion: false
|
||||
sendAnonymousUsage: false
|
||||
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true # OK for local dev only — exposes dashboard on :8080
|
||||
|
||||
log:
|
||||
level: INFO
|
||||
|
||||
accessLog: {}
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
|
||||
websecure:
|
||||
address: ":443"
|
||||
http:
|
||||
tls: {}
|
||||
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "unix:///var/run/docker.sock"
|
||||
exposedByDefault: false
|
||||
network: dezky
|
||||
watch: true
|
||||
|
||||
file:
|
||||
filename: /etc/traefik/dynamic.yml
|
||||
watch: true
|
||||
@@ -0,0 +1,341 @@
|
||||
# Dezky — Local Development Stack
|
||||
#
|
||||
# Start: docker compose up -d
|
||||
# Logs: docker compose logs -f [service]
|
||||
# Stop: docker compose down
|
||||
# Reset: docker compose down -v (WARNING: deletes all data)
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. mkcert root CA installed (mkcert -install)
|
||||
# 2. Wildcard cert generated in ./certs/dezky.local.pem
|
||||
# 3. /etc/hosts entries added (run scripts/setup-hosts.sh)
|
||||
# 4. .env file created from .env.example
|
||||
|
||||
name: dezky
|
||||
|
||||
networks:
|
||||
dezky:
|
||||
name: dezky
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
mongo_data:
|
||||
redis_data:
|
||||
authentik_media:
|
||||
authentik_certs:
|
||||
authentik_templates:
|
||||
stalwart_data:
|
||||
ocis_config:
|
||||
ocis_data:
|
||||
portal_node_modules:
|
||||
provisioning_node_modules:
|
||||
|
||||
services:
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Traefik — Reverse proxy with TLS termination via mkcert
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
traefik:
|
||||
image: traefik:v3.7
|
||||
container_name: dezky-traefik
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8443:8080" # Dashboard
|
||||
volumes:
|
||||
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro
|
||||
- ./configs/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
|
||||
- ./configs/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
|
||||
- ./certs:/certs:ro
|
||||
networks:
|
||||
dezky:
|
||||
aliases:
|
||||
- traefik.dezky.local
|
||||
- auth.dezky.local
|
||||
- app.dezky.local
|
||||
- files.dezky.local
|
||||
- mail.dezky.local
|
||||
- office.dezky.local
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.dashboard.rule=Host(`traefik.dezky.local`)
|
||||
- traefik.http.routers.dashboard.service=api@internal
|
||||
- traefik.http.routers.dashboard.tls=true
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# PostgreSQL — Shared RDBMS (Authentik, OCIS)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: dezky-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${POSTGRES_ROOT_PASSWORD}
|
||||
POSTGRES_DB: postgres
|
||||
AUTHENTIK_DB_PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
||||
OCIS_DB_PASSWORD: ${OCIS_DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./configs/postgres/init.sh:/docker-entrypoint-initdb.d/init.sh:ro
|
||||
networks: [dezky]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# MongoDB — Portal application data
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
mongo:
|
||||
image: mongo:7
|
||||
container_name: dezky-mongo
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: root
|
||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
|
||||
MONGO_INITDB_DATABASE: dezky
|
||||
volumes:
|
||||
- mongo_data:/data/db
|
||||
networks: [dezky]
|
||||
healthcheck:
|
||||
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Redis — Cache + session store
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: dezky-redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --save 60 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks: [dezky]
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Authentik — Identity provider (OIDC/SAML SSO)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
authentik-server:
|
||||
image: ghcr.io/goauthentik/server:2025.10
|
||||
container_name: dezky-authentik
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_REDIS__PASSWORD: ${REDIS_PASSWORD}
|
||||
AUTHENTIK_POSTGRESQL__HOST: postgres
|
||||
AUTHENTIK_POSTGRESQL__USER: authentik
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
||||
AUTHENTIK_POSTGRESQL__NAME: authentik
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
|
||||
AUTHENTIK_DISABLE_UPDATE_CHECK: "true"
|
||||
AUTHENTIK_BOOTSTRAP_EMAIL: admin@dezky.local
|
||||
AUTHENTIK_BOOTSTRAP_PASSWORD: ${AUTHENTIK_BOOTSTRAP_PASSWORD}
|
||||
AUTHENTIK_BOOTSTRAP_TOKEN: ${AUTHENTIK_BOOTSTRAP_TOKEN}
|
||||
volumes:
|
||||
- authentik_media:/media
|
||||
- authentik_certs:/certs
|
||||
- authentik_templates:/templates
|
||||
- ./configs/authentik/blueprints:/blueprints/custom:ro
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.authentik.rule=Host(`auth.dezky.local`)
|
||||
- traefik.http.routers.authentik.tls=true
|
||||
- traefik.http.services.authentik.loadbalancer.server.port=9000
|
||||
|
||||
authentik-worker:
|
||||
image: ghcr.io/goauthentik/server:2025.10
|
||||
container_name: dezky-authentik-worker
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_REDIS__PASSWORD: ${REDIS_PASSWORD}
|
||||
AUTHENTIK_POSTGRESQL__HOST: postgres
|
||||
AUTHENTIK_POSTGRESQL__USER: authentik
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
||||
AUTHENTIK_POSTGRESQL__NAME: authentik
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
|
||||
volumes:
|
||||
- authentik_media:/media
|
||||
- authentik_certs:/certs
|
||||
- authentik_templates:/templates
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Stalwart Mail — Mail server (SMTP/IMAP/JMAP/CalDAV/CardDAV/ActiveSync)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
stalwart:
|
||||
image: stalwartlabs/stalwart:v0.16
|
||||
container_name: dezky-stalwart
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "25:25" # SMTP
|
||||
- "465:465" # SMTPS
|
||||
- "587:587" # Submission
|
||||
- "143:143" # IMAP
|
||||
- "993:993" # IMAPS
|
||||
- "4190:4190" # ManageSieve
|
||||
environment:
|
||||
STALWART_FQDN: mail.dezky.local
|
||||
volumes:
|
||||
- stalwart_data:/opt/stalwart
|
||||
- ./configs/stalwart/config.toml:/opt/stalwart/etc/config.toml:ro
|
||||
networks: [dezky]
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.stalwart.rule=Host(`mail.dezky.local`)
|
||||
- traefik.http.routers.stalwart.tls=true
|
||||
- traefik.http.services.stalwart.loadbalancer.server.port=8080
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# OCIS — File storage with S3-compatible backend
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
ocis:
|
||||
image: owncloud/ocis:7.0
|
||||
container_name: dezky-ocis
|
||||
restart: unless-stopped
|
||||
entrypoint: /bin/sh
|
||||
command: ["-c", "ocis init --insecure true || true && ocis server"]
|
||||
environment:
|
||||
OCIS_URL: https://files.dezky.local
|
||||
OCIS_LOG_LEVEL: warn
|
||||
OCIS_INSECURE: "true" # dev only — self-signed certs
|
||||
PROXY_HTTP_ADDR: 0.0.0.0:9200
|
||||
PROXY_TLS: "false" # Traefik terminates TLS; OCIS speaks plain HTTP internally
|
||||
OCIS_OIDC_ISSUER: https://auth.dezky.local/application/o/ocis/
|
||||
WEB_OIDC_CLIENT_ID: ocis-web
|
||||
PROXY_AUTOPROVISION_ACCOUNTS: "true"
|
||||
PROXY_USER_OIDC_CLAIM: preferred_username
|
||||
PROXY_USER_CS3_CLAIM: username
|
||||
OCIS_ADMIN_USER_ID: ""
|
||||
IDM_CREATE_DEMO_USERS: "false"
|
||||
IDM_ADMIN_PASSWORD: ${OCIS_ADMIN_PASSWORD}
|
||||
STORAGE_USERS_DRIVER: ocis # Local filesystem in dev
|
||||
STORAGE_SYSTEM_DRIVER: ocis
|
||||
OCIS_CONFIG_DIR: /etc/ocis
|
||||
OCIS_BASE_DATA_PATH: /var/lib/ocis
|
||||
volumes:
|
||||
- ocis_config:/etc/ocis
|
||||
- ocis_data:/var/lib/ocis
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
- authentik-server
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.ocis.rule=Host(`files.dezky.local`)
|
||||
- traefik.http.routers.ocis.tls=true
|
||||
- traefik.http.services.ocis.loadbalancer.server.port=9200
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Collabora — Office document editor (integrated into OCIS)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
collabora:
|
||||
image: collabora/code:latest
|
||||
container_name: dezky-collabora
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
aliasgroup1: https://files\\.dezky\\.local:443
|
||||
DONT_GEN_SSL_CERT: "true"
|
||||
extra_params: --o:ssl.enable=false --o:ssl.termination=true --o:welcome.enable=false
|
||||
username: admin
|
||||
password: ${COLLABORA_ADMIN_PASSWORD}
|
||||
networks: [dezky]
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.collabora.rule=Host(`office.dezky.local`)
|
||||
- traefik.http.routers.collabora.tls=true
|
||||
- traefik.http.services.collabora.loadbalancer.server.port=9980
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Portal — Nuxt 3 customer portal (development mode with HMR)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
portal:
|
||||
image: node:20-alpine
|
||||
container_name: dezky-portal
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
|
||||
environment:
|
||||
NODE_ENV: development
|
||||
NUXT_HOST: 0.0.0.0
|
||||
NUXT_PORT: 3000
|
||||
NUXT_PUBLIC_AUTH_URL: https://auth.dezky.local
|
||||
NUXT_PUBLIC_PORTAL_URL: https://app.dezky.local
|
||||
NUXT_API_BASE: http://provisioning:3001
|
||||
MONGODB_URI: mongodb://root:${MONGO_ROOT_PASSWORD}@mongo:27017/dezky?authSource=admin
|
||||
# OIDC (confidential client) — used by Nuxt server middleware
|
||||
NUXT_OIDC_CLIENT_ID: ${PORTAL_OIDC_CLIENT_ID}
|
||||
NUXT_OIDC_CLIENT_SECRET: ${PORTAL_OIDC_CLIENT_SECRET}
|
||||
NUXT_OIDC_ISSUER: ${PORTAL_OIDC_ISSUER}
|
||||
NUXT_OIDC_REDIRECT_URI: https://app.dezky.local/auth/oidc/callback
|
||||
# Session encryption (required by nuxt-oidc-auth)
|
||||
NUXT_OIDC_TOKEN_KEY: ${NUXT_OIDC_TOKEN_KEY}
|
||||
NUXT_OIDC_SESSION_SECRET: ${NUXT_OIDC_SESSION_SECRET}
|
||||
NUXT_OIDC_AUTH_SESSION_SECRET: ${NUXT_OIDC_AUTH_SESSION_SECRET}
|
||||
# Trust mkcert root CA for Node fetch (dev only)
|
||||
NODE_EXTRA_CA_CERTS: /etc/ssl/mkcert-root.pem
|
||||
volumes:
|
||||
- ../../apps/portal:/app
|
||||
- portal_node_modules:/app/node_modules
|
||||
- ./certs/mkcert-root.pem:/etc/ssl/mkcert-root.pem:ro
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.portal.rule=Host(`app.dezky.local`) || Host(`dezky.local`)
|
||||
- traefik.http.routers.portal.tls=true
|
||||
- traefik.http.services.portal.loadbalancer.server.port=3000
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Provisioning service — NestJS worker for tenant lifecycle
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
provisioning:
|
||||
image: node:20-alpine
|
||||
container_name: dezky-provisioning
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm start:dev"
|
||||
environment:
|
||||
NODE_ENV: development
|
||||
PORT: 3001
|
||||
MONGODB_URI: mongodb://root:${MONGO_ROOT_PASSWORD}@mongo:27017/dezky?authSource=admin
|
||||
AUTHENTIK_API_URL: http://authentik-server:9000/api/v3
|
||||
AUTHENTIK_API_TOKEN: ${AUTHENTIK_BOOTSTRAP_TOKEN}
|
||||
STALWART_API_URL: http://stalwart:8080
|
||||
STALWART_ADMIN_USER: admin
|
||||
STALWART_ADMIN_PASSWORD: ${STALWART_ADMIN_PASSWORD}
|
||||
OCIS_API_URL: http://ocis:9200
|
||||
volumes:
|
||||
- ../../services/provisioning:/app
|
||||
- provisioning_node_modules:/app/node_modules
|
||||
networks: [dezky]
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user