refactor(deploy): externalize all secrets to .env, migrate Brevo SMTP → REST API
- docker-compose.yml: replace 43 hardcoded env values with ${VAR} references.
Operators must provide /opt/architools/.env (chmod 600, gitignored) with the
matching keys. Removes the historical leak surface where every edit risked
echoing secrets.
- email-service.ts: drop nodemailer SMTP transport; use Brevo REST API
(POST https://api.brevo.com/v3/smtp/email) with BREVO_API_KEY header.
Brevo SMTP relay credentials have been deleted upstream.
- package.json: remove nodemailer + @types/nodemailer.
NOTE: legacy hardcoded credentials present in git history must still be
rotated separately (DB password, Authentik client secret, ENCRYPTION_SECRET,
ANCPI password, etc.).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+146
-149
@@ -1,149 +1,146 @@
|
||||
services:
|
||||
architools:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- NEXT_PUBLIC_STORAGE_ADAPTER=${NEXT_PUBLIC_STORAGE_ADAPTER:-database}
|
||||
- NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME:-ArchiTools}
|
||||
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-https://tools.beletage.ro}
|
||||
- NEXT_PUBLIC_MARTIN_URL=https://tools.beletage.ro/tiles
|
||||
- NEXT_PUBLIC_PMTILES_URL=/tiles/pmtiles/overview.pmtiles
|
||||
container_name: architools
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
# Database
|
||||
- DATABASE_URL=postgresql://architools_user:stictMyFon34!_gonY@10.10.10.166:5432/architools_db?schema=public
|
||||
# MinIO
|
||||
- MINIO_ENDPOINT=10.10.10.166
|
||||
- MINIO_PORT=9002
|
||||
- MINIO_USE_SSL=false
|
||||
- MINIO_ACCESS_KEY=admin
|
||||
- MINIO_SECRET_KEY=MinioStrongPass123
|
||||
- MINIO_BUCKET_NAME=tools
|
||||
# Authentication (Authentik OIDC)
|
||||
- NEXTAUTH_URL=https://tools.beletage.ro
|
||||
- NEXTAUTH_SECRET=8IL9Kpipj0EZwZPNvekbNRPhV6a2/UY4cGVzE3n0pUY=
|
||||
- AUTHENTIK_CLIENT_ID=V59GMiYle87yd9VZOgUmdSmzYQALqNsKVAUR6QMi
|
||||
- AUTHENTIK_CLIENT_SECRET=TMeewkusUro0hQ2DMwS0Z5lNpNMdmziO9WXywNAGlK3Y6Y8HYULZBEtMtm53lioIkszWbpPRQcv1cxHMtwftMvsaSnbliDsL1f707wmUJhMFKjeZ0ypIFKFG4dJkp7Jr
|
||||
- AUTHENTIK_ISSUER=https://auth.beletage.ro/application/o/architools/
|
||||
# Vault encryption
|
||||
- ENCRYPTION_SECRET=ArchiTools-Vault-2025!SecureKey@AES256
|
||||
# ManicTime Tags.txt sync (SMB mount path)
|
||||
- MANICTIME_TAGS_PATH=/mnt/manictime/Tags.txt
|
||||
# AI Chat (set AI_PROVIDER to openai/anthropic/ollama; demo if no key)
|
||||
- AI_PROVIDER=${AI_PROVIDER:-demo}
|
||||
- AI_API_KEY=${AI_API_KEY:-}
|
||||
- AI_MODEL=${AI_MODEL:-}
|
||||
- AI_BASE_URL=${AI_BASE_URL:-}
|
||||
- AI_MAX_TOKENS=${AI_MAX_TOKENS:-2048}
|
||||
# Visual CoPilot (at-vim)
|
||||
- VIM_URL=${VIM_URL:-}
|
||||
# eTerra ANCPI (parcel-sync module)
|
||||
- ETERRA_USERNAME=${ETERRA_USERNAME:-}
|
||||
- ETERRA_PASSWORD=${ETERRA_PASSWORD:-}
|
||||
# ANCPI ePay (CF extract ordering)
|
||||
- ANCPI_USERNAME=m.tarau@beletage.ro
|
||||
- ANCPI_PASSWORD=Beletage@112
|
||||
- ANCPI_BASE_URL=https://epay.ancpi.ro/epay
|
||||
- ANCPI_LOGIN_URL=https://oassl.ancpi.ro/openam/UI/Login
|
||||
- ANCPI_DEFAULT_SOLICITANT_ID=14452
|
||||
- MINIO_BUCKET_ANCPI=ancpi-documente
|
||||
# Stirling PDF (local PDF tools)
|
||||
- STIRLING_PDF_URL=http://10.10.10.166:8087
|
||||
- STIRLING_PDF_API_KEY=cd829f62-6eef-43eb-a64d-c91af727b53a
|
||||
# iLovePDF cloud compression (free: 250 files/month)
|
||||
- ILOVEPDF_PUBLIC_KEY=${ILOVEPDF_PUBLIC_KEY:-}
|
||||
# Martin vector tile server (geoportal)
|
||||
- NEXT_PUBLIC_MARTIN_URL=https://tools.beletage.ro/tiles
|
||||
# PMTiles overview tiles — proxied through tile-cache nginx (HTTPS, no mixed-content)
|
||||
- NEXT_PUBLIC_PMTILES_URL=/tiles/pmtiles/overview.pmtiles
|
||||
# DWG-to-DXF sidecar
|
||||
- DWG2DXF_URL=http://dwg2dxf:5001
|
||||
# Email notifications (Brevo SMTP)
|
||||
- BREVO_SMTP_HOST=smtp-relay.brevo.com
|
||||
- BREVO_SMTP_PORT=587
|
||||
- BREVO_SMTP_USER=a2d94b001@smtp-brevo.com
|
||||
- BREVO_SMTP_PASS=xsmtpsib-c2f5dfe1a7809c962d8907afafdc9edc1ff7e74340518539de8f8eccfd1dcc90-ipkNHpvN9RByv1V6
|
||||
- NOTIFICATION_FROM_EMAIL=noreply@beletage.ro
|
||||
- NOTIFICATION_FROM_NAME=Alerte Termene
|
||||
- NOTIFICATION_CRON_SECRET=1547a198feca43af6c05622588c6d3b820bad5163b8c20175b2b5bbf8fc1a987
|
||||
# Weekend Deep Sync email reports (comma-separated for multiple recipients)
|
||||
- WEEKEND_SYNC_EMAIL=${WEEKEND_SYNC_EMAIL:-}
|
||||
# PMTiles rebuild webhook (pmtiles-webhook systemd service on host)
|
||||
- N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL:-http://10.10.10.166:9876}
|
||||
# Portal-only users (comma-separated, redirected to /portal)
|
||||
- PORTAL_ONLY_USERS=dtiurbe,d.tiurbe
|
||||
# Address Book API (inter-service auth for external tools)
|
||||
- ADDRESSBOOK_API_KEY=abook-7f3e9a2b4c1d8e5f6a0b3c7d9e2f4a1b5c8d0e3f6a9b2c5d8e1f4a7b0c3d6e
|
||||
depends_on:
|
||||
dwg2dxf:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
# SMB share for ManicTime Tags.txt (mount on host: //time/tags → /mnt/manictime)
|
||||
- /mnt/manictime:/mnt/manictime
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
dwg2dxf:
|
||||
build:
|
||||
context: ./dwg2dxf-api
|
||||
container_name: dwg2dxf
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"python3",
|
||||
"-c",
|
||||
"import urllib.request; urllib.request.urlopen('http://localhost:5001/health')",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
martin:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: martin.Dockerfile
|
||||
container_name: martin
|
||||
restart: unless-stopped
|
||||
# No host port — only accessible via tile-cache nginx proxy
|
||||
command: ["--config", "/config/martin.yaml"]
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://architools_user:stictMyFon34!_gonY@10.10.10.166:5432/architools_db
|
||||
|
||||
tile-cache:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: tile-cache.Dockerfile
|
||||
container_name: tile-cache
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3010:80"
|
||||
depends_on:
|
||||
- martin
|
||||
volumes:
|
||||
- tile-cache-data:/var/cache/nginx/tiles
|
||||
|
||||
tippecanoe:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: tippecanoe.Dockerfile
|
||||
container_name: tippecanoe
|
||||
profiles: ["tools"]
|
||||
environment:
|
||||
- DB_HOST=10.10.10.166
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=architools_db
|
||||
- DB_USER=architools_user
|
||||
- DB_PASS=stictMyFon34!_gonY
|
||||
- MINIO_ENDPOINT=http://10.10.10.166:9002
|
||||
- MINIO_ACCESS_KEY=admin
|
||||
- MINIO_SECRET_KEY=MinioStrongPass123
|
||||
|
||||
volumes:
|
||||
tile-cache-data:
|
||||
services:
|
||||
architools:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- NEXT_PUBLIC_STORAGE_ADAPTER=${NEXT_PUBLIC_STORAGE_ADAPTER:-database}
|
||||
- NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME:-ArchiTools}
|
||||
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-https://tools.beletage.ro}
|
||||
- NEXT_PUBLIC_MARTIN_URL=${NEXT_PUBLIC_MARTIN_URL}
|
||||
- NEXT_PUBLIC_PMTILES_URL=${NEXT_PUBLIC_PMTILES_URL}
|
||||
container_name: architools
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=${NODE_ENV}
|
||||
# Database
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
# MinIO
|
||||
- MINIO_ENDPOINT=${MINIO_ENDPOINT}
|
||||
- MINIO_PORT=${MINIO_PORT}
|
||||
- MINIO_USE_SSL=${MINIO_USE_SSL}
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
- MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME}
|
||||
# Authentication (Authentik OIDC)
|
||||
- NEXTAUTH_URL=${NEXTAUTH_URL}
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||
- AUTHENTIK_CLIENT_ID=${AUTHENTIK_CLIENT_ID}
|
||||
- AUTHENTIK_CLIENT_SECRET=${AUTHENTIK_CLIENT_SECRET}
|
||||
- AUTHENTIK_ISSUER=${AUTHENTIK_ISSUER}
|
||||
# Vault encryption
|
||||
- ENCRYPTION_SECRET=${ENCRYPTION_SECRET}
|
||||
# ManicTime Tags.txt sync (SMB mount path)
|
||||
- MANICTIME_TAGS_PATH=${MANICTIME_TAGS_PATH}
|
||||
# AI Chat (set AI_PROVIDER to openai/anthropic/ollama; demo if no key)
|
||||
- AI_PROVIDER=${AI_PROVIDER:-demo}
|
||||
- AI_API_KEY=${AI_API_KEY:-}
|
||||
- AI_MODEL=${AI_MODEL:-}
|
||||
- AI_BASE_URL=${AI_BASE_URL:-}
|
||||
- AI_MAX_TOKENS=${AI_MAX_TOKENS:-2048}
|
||||
# Visual CoPilot (at-vim)
|
||||
- VIM_URL=${VIM_URL:-}
|
||||
# eTerra ANCPI (parcel-sync module)
|
||||
- ETERRA_USERNAME=${ETERRA_USERNAME:-}
|
||||
- ETERRA_PASSWORD=${ETERRA_PASSWORD:-}
|
||||
# ANCPI ePay (CF extract ordering)
|
||||
- ANCPI_USERNAME=${ANCPI_USERNAME}
|
||||
- ANCPI_PASSWORD=${ANCPI_PASSWORD}
|
||||
- ANCPI_BASE_URL=${ANCPI_BASE_URL}
|
||||
- ANCPI_LOGIN_URL=${ANCPI_LOGIN_URL}
|
||||
- ANCPI_DEFAULT_SOLICITANT_ID=${ANCPI_DEFAULT_SOLICITANT_ID}
|
||||
- MINIO_BUCKET_ANCPI=${MINIO_BUCKET_ANCPI}
|
||||
# Stirling PDF (local PDF tools)
|
||||
- STIRLING_PDF_URL=${STIRLING_PDF_URL}
|
||||
- STIRLING_PDF_API_KEY=${STIRLING_PDF_API_KEY}
|
||||
# iLovePDF cloud compression (free: 250 files/month)
|
||||
- ILOVEPDF_PUBLIC_KEY=${ILOVEPDF_PUBLIC_KEY:-}
|
||||
# Martin vector tile server (geoportal)
|
||||
- NEXT_PUBLIC_MARTIN_URL=${NEXT_PUBLIC_MARTIN_URL}
|
||||
# PMTiles overview tiles — proxied through tile-cache nginx (HTTPS, no mixed-content)
|
||||
- NEXT_PUBLIC_PMTILES_URL=${NEXT_PUBLIC_PMTILES_URL}
|
||||
# DWG-to-DXF sidecar
|
||||
- DWG2DXF_URL=${DWG2DXF_URL}
|
||||
# Email notifications (Brevo REST API)
|
||||
- BREVO_API_KEY=${BREVO_API_KEY}
|
||||
- NOTIFICATION_FROM_EMAIL=${NOTIFICATION_FROM_EMAIL}
|
||||
- NOTIFICATION_FROM_NAME=${NOTIFICATION_FROM_NAME}
|
||||
- NOTIFICATION_CRON_SECRET=${NOTIFICATION_CRON_SECRET}
|
||||
# Weekend Deep Sync email reports (comma-separated for multiple recipients)
|
||||
- WEEKEND_SYNC_EMAIL=${WEEKEND_SYNC_EMAIL:-}
|
||||
# PMTiles rebuild webhook (pmtiles-webhook systemd service on host)
|
||||
- N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL:-http://10.10.10.166:9876}
|
||||
# Portal-only users (comma-separated, redirected to /portal)
|
||||
- PORTAL_ONLY_USERS=${PORTAL_ONLY_USERS}
|
||||
# Address Book API (inter-service auth for external tools)
|
||||
- ADDRESSBOOK_API_KEY=${ADDRESSBOOK_API_KEY}
|
||||
depends_on:
|
||||
dwg2dxf:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
# SMB share for ManicTime Tags.txt (mount on host: //time/tags → /mnt/manictime)
|
||||
- /mnt/manictime:/mnt/manictime
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
dwg2dxf:
|
||||
build:
|
||||
context: ./dwg2dxf-api
|
||||
container_name: dwg2dxf
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"python3",
|
||||
"-c",
|
||||
"import urllib.request; urllib.request.urlopen('http://localhost:5001/health')",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
martin:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: martin.Dockerfile
|
||||
container_name: martin
|
||||
restart: unless-stopped
|
||||
# No host port — only accessible via tile-cache nginx proxy
|
||||
command: ["--config", "/config/martin.yaml"]
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
|
||||
tile-cache:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: tile-cache.Dockerfile
|
||||
container_name: tile-cache
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3010:80"
|
||||
depends_on:
|
||||
- martin
|
||||
volumes:
|
||||
- tile-cache-data:/var/cache/nginx/tiles
|
||||
|
||||
tippecanoe:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: tippecanoe.Dockerfile
|
||||
container_name: tippecanoe
|
||||
profiles: ["tools"]
|
||||
environment:
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_NAME=${DB_NAME}
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASS=${DB_PASS}
|
||||
- MINIO_ENDPOINT=${MINIO_ENDPOINT}
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
|
||||
volumes:
|
||||
tile-cache-data:
|
||||
|
||||
Reference in New Issue
Block a user