security: harden production configuration and routes
Run Tests on Branches / Detect Changes (push) Successful in 12s
Run Tests on Branches / Frontend Tests (push) Successful in 2m12s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Successful in 2m2s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Backend Tests (push) Successful in 2m8s

This commit is contained in:
Antoni Nuñez Romeu
2026-07-22 17:24:54 +02:00
parent f60af5f6b2
commit 849763896d
21 changed files with 638 additions and 68 deletions
+38 -23
View File
@@ -2,6 +2,8 @@ services:
redis:
image: redis:alpine
restart: unless-stopped
networks:
- internal
postgres:
image: postgres:16-alpine
@@ -9,7 +11,9 @@ services:
environment:
POSTGRES_DB: farmafinder
POSTGRES_USER: farmafinder
POSTGRES_PASSWORD: ${PG_PASSWORD:-change-me-in-production}
POSTGRES_PASSWORD: ${PG_PASSWORD:?PG_PASSWORD must be set}
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U farmafinder"]
interval: 5s
@@ -26,19 +30,17 @@ services:
restart: unless-stopped
env_file:
- ./apps/backend/.env
ports:
- "3001:3001"
environment:
PORT: "3001"
NODE_ENV: production
SESSION_SECRET: ${SESSION_SECRET:-change-me-in-production}
CORS_ORIGIN: ${CORS_ORIGIN:-https://farmacias.hacecalor.net}
SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET must be set}
CORS_ORIGIN: ${CORS_ORIGIN:?CORS_ORIGIN must be set}
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
DATABASE_PATH: /app/data/database.sqlite
FARMACIAS_WEBHOOK_URL: ${FARMACIAS_WEBHOOK_URL:-}
PG_URL: postgresql://farmafinder:${PG_PASSWORD:-change-me-in-production}@postgres:5432/farmafinder
PG_URL: postgresql://farmafinder:${PG_PASSWORD:?PG_PASSWORD must be set}@postgres:5432/farmafinder
# OpenTelemetry — exported via OTLP gRPC to the shared Alloy collector
OTEL_SERVICE_NAME: farmafinder-backend
OTEL_EXPORTER_OTLP_ENDPOINT: http://host.docker.internal:4317
@@ -52,6 +54,8 @@ services:
depends_on:
- redis
- postgres
networks:
- internal
frontend:
image: git.hacecalor.net/ichitux/farmafinder-frontend:latest
@@ -67,28 +71,31 @@ services:
- "4000:80"
depends_on:
- backend
networks:
- public
- internal
# --- Observability exporters (scraped by the shared Prometheus on srv84-macos) ---
redis-exporter:
image: oliver006/redis_exporter:latest
restart: unless-stopped
ports:
- "9121:9121"
environment:
REDIS_ADDR: redis://redis:6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
depends_on:
- redis
networks:
- internal
postgres-exporter:
image: prometheuscommunity/postgres-exporter:latest
restart: unless-stopped
ports:
- "9187:9187"
environment:
DATA_SOURCE_NAME: postgresql://farmafinder:${PG_PASSWORD:-change-me-in-production}@postgres:5432/farmafinder?sslmode=disable
DATA_SOURCE_NAME: postgresql://farmafinder:${PG_PASSWORD:?PG_PASSWORD must be set}@postgres:5432/farmafinder?sslmode=disable
depends_on:
- postgres
networks:
- internal
# --- Parapharmacy API ---
parapharmacy-api:
@@ -99,13 +106,13 @@ services:
env_file:
- ./apps/parapharmacy-api/.env
restart: unless-stopped
ports:
- "3002:3002"
environment:
PORT: "3002"
NODE_ENV: production
MONGODB_URI: mongodb://mongodb:27017/parapharmacy
CORS_ORIGIN: ${CORS_ORIGIN:-https://farmacias.hacecalor.net}
CORS_ORIGIN: ${CORS_ORIGIN:?CORS_ORIGIN must be set}
INGEST_API_KEY: ${INGEST_API_KEY:?INGEST_API_KEY must be set}
ADMIN_API_KEY: ${ADMIN_API_KEY:?ADMIN_API_KEY must be set}
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3002/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
interval: 10s
@@ -114,37 +121,37 @@ services:
start_period: 15s
depends_on:
- mongodb
networks:
- internal
# --- MongoDB for Parapharmacy ---
mongodb:
image: mongo:7
restart: unless-stopped
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
networks:
- internal
# --- N8N Workflow Automation ---
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
ports:
- "5678:5678"
environment:
# Owner account (skips /setup)
N8N_USER_MANAGEMENT_DISABLED: "false"
N8N_OWNER_EMAIL: ${N8N_EMAIL:-admin@farmafinder.com}
N8N_OWNER_PASSWORD: ${N8N_PASSWORD:-change-me}
N8N_OWNER_PASSWORD: ${N8N_PASSWORD:?N8N_PASSWORD must be set}
# Auth
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: ${N8N_USER:-admin}
N8N_BASIC_AUTH_PASSWORD: ${N8N_PASSWORD:-change-me}
N8N_BASIC_AUTH_PASSWORD: ${N8N_PASSWORD:?N8N_PASSWORD must be set}
# Database
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: farmafinder
DB_POSTGRESDB_USER: farmafinder
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:-change-me-in-production}
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:?PG_PASSWORD must be set}
# Network
N8N_HOST: localhost
N8N_PORT: 5678
@@ -163,6 +170,8 @@ services:
depends_on:
postgres:
condition: service_healthy
networks:
- internal
# --- N8N Init: import + activate workflows BEFORE n8n starts, then seed DB ---
n8n-init:
@@ -173,9 +182,9 @@ services:
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: farmafinder
DB_POSTGRESDB_USER: farmafinder
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:-change-me-in-production}
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:?PG_PASSWORD must be set}
N8N_OWNER_EMAIL: ${N8N_EMAIL:-admin@farmafinder.com}
N8N_OWNER_PASSWORD: ${N8N_PASSWORD:-change-me}
N8N_OWNER_PASSWORD: ${N8N_PASSWORD:?N8N_PASSWORD must be set}
volumes:
- n8n_data:/home/node/.n8n
- ./n8n/workflows:/home/node/workflows
@@ -189,9 +198,15 @@ services:
condition: service_healthy
n8n:
condition: service_healthy
networks:
- internal
volumes:
backend_data:
postgres_data:
mongodb_data:
n8n_data:
networks:
public:
internal: