# ============================================================================ # Rastro — Compose "INTERNET" (servidor com acesso externo, ex.: Coolify repr-externo). # Espelho do que RODA no Coolify (stack rastro-br) — copiar/colar no Edit Compose File. # • Infra (db/rabbitmq/minio/gotenberg): direto do Docker Hub. # • App (rastro-node/analytics): do Gitea EXTERNO (pacotes públicos, pull anônimo). # • Roteamento do /api via LABELS Traefik no backend (NÃO usar o campo Domains do # backend no Coolify — o Domains-com-path corta o /api; o backend registra as # rotas COM /api, então precisa chegar sem strip). web/admin ficam no Domains do Coolify. # • Volume do banco: dbdata2 (o schema já subiu nele; renomear zera o banco). # • Teradata SEFAZ (processor, backend, analytics): nós COP por IP — o DNS da UTIC não # resolve trdtdbcopN. TERADATA_ENABLED/USER/PASSWORD e EVIDENCE_KEYS em Environment # Variables do Coolify. EVIDENCE_KEYS: "1:" (guarde cópia!). # • Certificado A1 (XML sob demanda no backend): DFE_CERT_PFX_BASE64 + DFE_CERT_PASSPHRASE # como variáveis secretas no Coolify — sem montar arquivo no servidor. # ============================================================================ x-app-env: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar services: db: image: 'timescale/timescaledb-ha:pg16' restart: unless-stopped environment: POSTGRES_USER: '${POSTGRES_USER}' POSTGRES_PASSWORD: '${POSTGRES_PASSWORD}' POSTGRES_DB: '${POSTGRES_DB}' shm_size: 1gb volumes: - 'dbdata2:/home/postgres/pgdata' healthcheck: test: - CMD-SHELL - 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}' interval: 10s timeout: 5s retries: 15 rabbitmq: image: 'rabbitmq:3-management' restart: unless-stopped environment: RABBITMQ_DEFAULT_USER: '${RABBITMQ_DEFAULT_USER}' RABBITMQ_DEFAULT_PASS: '${RABBITMQ_DEFAULT_PASS}' volumes: - 'rabbitmq_data:/var/lib/rabbitmq' healthcheck: test: - CMD - rabbitmq-diagnostics - '-q' - ping interval: 15s timeout: 5s retries: 10 minio: image: 'minio/minio:latest' restart: unless-stopped command: 'server /data --console-address ":9001"' environment: MINIO_ROOT_USER: '${MINIO_ROOT_USER}' MINIO_ROOT_PASSWORD: '${MINIO_ROOT_PASSWORD}' volumes: - 'minio_data:/data' gotenberg: image: 'gotenberg/gotenberg:8' restart: unless-stopped command: - gotenberg - '--chromium-disable-javascript=false' - '--api-timeout=60s' migrate: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v3.1' restart: 'no' depends_on: db: condition: service_healthy environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar NODE_ENV: production DB_CONNECTION_LIMIT: '5' command: 'sh -c "wait-for-db && pnpm --filter @rastro/db migrate && pnpm --filter @rastro/db postgis && pnpm --filter @rastro/db timescale && pnpm --filter @rastro/db intel && pnpm --filter @rastro/db analyze && pnpm --filter @rastro/db restore-users"' processor: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v3.1' restart: unless-stopped depends_on: db: condition: service_healthy environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar NODE_ENV: production DB_CONNECTION_LIMIT: '8' OSRM_URL: 'http://osrm:5000' REROUTE_STALE_INTERVAL_MIN: '15' TERADATA_ENABLED: '${TERADATA_ENABLED:-false}' TERADATA_HOSTS: '10.14.203.21,10.14.203.22,10.14.203.23' TERADATA_USER: '${TERADATA_USER}' TERADATA_PASSWORD: '${TERADATA_PASSWORD}' TERADATA_DATABASE: P_ACCDB DFE_HOT_DAYS: '7' DFE_TD_GRACE_DAYS: '10' DFE_OPEN_MAX_DAYS: '60' DFE_ROUTE_RETENTION_DAYS: '90' DFE_META_RETENTION_DAYS: '400' DFE_LIFECYCLE_ENABLED: 'true' command: 'sh -c "wait-for-db schema rabbitmq && pnpm --filter @rastro/processor start"' analytics: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-analytics:v4' restart: unless-stopped depends_on: db: condition: service_healthy environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar INTEL_TOP_COMPANIES: '200' INTEL_TOP_COMMODITIES: '50' INTEL_HORIZON_WEEKS: '8' INTEL_CYCLE_HOURS: '24' TERADATA_ENABLED: '${TERADATA_ENABLED:-false}' TERADATA_HOSTS: '10.14.203.21,10.14.203.22,10.14.203.23' TERADATA_USER: '${TERADATA_USER}' TERADATA_PASSWORD: '${TERADATA_PASSWORD}' backend: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v3.1' restart: unless-stopped depends_on: db: condition: service_healthy environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar NODE_ENV: production DB_CONNECTION_LIMIT: '12' BACKEND_PORT: '39010' JWT_SECRET: '${JWT_SECRET}' SESSION_COOKIE_NAME: rastro_session CORS_ORIGINS: 'https://${WEB_DOMAIN},https://${ADMIN_DOMAIN}' RATE_LIMIT_MAX: '1000' GOTENBERG_URL: 'http://gotenberg:3000' MINIO_ENDPOINT: minio MINIO_PORT: '9000' MINIO_ROOT_USER: '${MINIO_ROOT_USER}' MINIO_ROOT_PASSWORD: '${MINIO_ROOT_PASSWORD}' MINIO_BUCKET: rastro-photos TERADATA_ENABLED: '${TERADATA_ENABLED:-false}' TERADATA_HOSTS: '10.14.203.21,10.14.203.22,10.14.203.23' TERADATA_USER: '${TERADATA_USER}' TERADATA_PASSWORD: '${TERADATA_PASSWORD}' TERADATA_DATABASE: P_ACCDB TD_DOC_CACHE_HOURS: '24' EVIDENCE_KEYS: '${EVIDENCE_KEYS}' EVIDENCE_BUCKET: rastro-evidence # Certificado A1 para o XML sob demanda: o .pfx em base64 numa variável secreta # (base64 -i certificado.pfx | tr -d '\n'). Vazio = XML sob demanda desligado. DFE_CERT_PFX_BASE64: '${DFE_CERT_PFX_BASE64}' DFE_CERT_PASSPHRASE: '${DFE_CERT_PASSPHRASE}' DFE_TLS_REJECT_UNAUTHORIZED: 'false' command: 'sh -c "wait-for-db schema && pnpm --filter @rastro/backend start"' expose: - '39010' labels: - traefik.enable=true - 'traefik.http.routers.rastroapi.rule=(Host(`rastrobr.externo.utic.app.br`) || Host(`admin.rastrobr.externo.utic.app.br`)) && PathPrefix(`/api`)' - traefik.http.routers.rastroapi.entryPoints=https - traefik.http.routers.rastroapi.tls=true - traefik.http.routers.rastroapi.priority=1000 - traefik.http.services.rastroapi.loadbalancer.server.port=39010 web: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v3.1' restart: unless-stopped environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar VITE_DEV_HELPERS: 'false' PREVIEW_ALLOWED_HOSTS: '${WEB_DOMAIN}' command: 'sh -c "pnpm --filter web preview --host 0.0.0.0 --port 39000"' expose: - '39000' admin: image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v3.1' restart: unless-stopped environment: DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public' RABBITMQ_URL: 'amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672' RASTRO_EXCHANGE: rastro.passagens RASTRO_QUEUE: rastro.passagens.processar VITE_DEV_HELPERS: 'false' PREVIEW_ALLOWED_HOSTS: '${ADMIN_DOMAIN}' command: 'sh -c "pnpm --filter admin preview --host 0.0.0.0 --port 39020"' expose: - '39020' volumes: dbdata2: null rabbitmq_data: null minio_data: null