358 lines
16 KiB
YAML
358 lines
16 KiB
YAML
# ============================================================================
|
|
# 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): ESPELHADA no Gitea — pull do Docker Hub neste servidor
|
|
# corrompia camadas grandes ("magic number mismatch") e a minio/minio saiu 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:<openssl rand -base64 32>" (guarde cópia!).
|
|
# • Certificado A1 (ingestor, dfe-worker, backend): DFE_CERT_PFX_BASE64 + DFE_CERT_PASSPHRASE
|
|
# como variáveis secretas no Coolify — sem montar arquivo no servidor.
|
|
# • Drenagem: ingestor (ONE) + dfe-worker (cadeia) + osrm (grafo no volume osrm_data);
|
|
# processor/dfe-worker roteiam por OSRM_URL = http://osrm:5000.
|
|
# ============================================================================
|
|
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: 'gitea.externo.utic.app.br/ericoalmeida/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: 'gitea.externo.utic.app.br/ericoalmeida/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:
|
|
# Espelho da imagem oficial (quay.io; o Docker Hub não publica mais a MinIO), versão fixa.
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/minio:RELEASE.2025-09-07T16-13-09Z'
|
|
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: 'gitea.externo.utic.app.br/ericoalmeida/gotenberg:8'
|
|
restart: unless-stopped
|
|
command:
|
|
- gotenberg
|
|
- '--chromium-disable-javascript=false'
|
|
- '--api-timeout=60s'
|
|
osrm:
|
|
# Rotas pelas vias (grafo do Brasil, MLD, v5.26.0, --mmap: ~70-700 MB de RAM).
|
|
# O grafo (15 GB) vive no volume osrm_data e é baixado UMA vez (marca .pronto).
|
|
# Máquina nova (ou volume perdido) = defina OSRM_DATA_URL nas variáveis do Coolify,
|
|
# com ~25 GB livres: o serviço baixa de lá, retoma queda e confere sha256. Sem a
|
|
# variável e sem .pronto o container para e diz isso no log — de propósito.
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-osrm:v1'
|
|
restart: unless-stopped
|
|
environment:
|
|
OSRM_DATA_URL: '${OSRM_DATA_URL:-}'
|
|
volumes:
|
|
- 'osrm_data:/data'
|
|
expose:
|
|
- '5000'
|
|
ingestor:
|
|
# DRENAGEM do ONE (passagens + fotos). Valores de cadência da produção antiga.
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
|
restart: unless-stopped
|
|
# Só ORDEM de partida, sem `condition: service_healthy`: a espera de verdade é
|
|
# dentro do container (`wait-for-db schema rabbitmq`). Com a condição aqui, o
|
|
# `compose up` do Coolify fica parado esperando o RabbitMQ ficar saudável, estoura
|
|
# o tempo do deploy e o container NUNCA é criado — foi assim que o ingestor
|
|
# desapareceu ("No such container"), do mesmo jeito que o osrm-data antes dele.
|
|
depends_on:
|
|
- db
|
|
- rabbitmq
|
|
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'
|
|
ONE_TP_AMB: '1'
|
|
ONE_VER_APLIC: rastro-1.0
|
|
ONE_HOST: one.svrs.rs.gov.br
|
|
ONE_TIMEOUT_MS: '30000'
|
|
ONE_TP_DIST: '1,4'
|
|
ONE_CUF: '41'
|
|
ONE_CNPJ_OPER: ''
|
|
ONE_POLL_INTERVAL: '30'
|
|
ONE_MAX_BATCHES: '40'
|
|
ONE_BATCH_DELAY_MS: '700'
|
|
ONE_IND_RESUMO: '0'
|
|
# Certificado A1 por variável secreta (base64 do .pfx) — sem montar arquivo.
|
|
DFE_CERT_PFX_BASE64: '${DFE_CERT_PFX_BASE64}'
|
|
DFE_CERT_PASSPHRASE: '${DFE_CERT_PASSPHRASE}'
|
|
DFE_TLS_REJECT_UNAUTHORIZED: 'false'
|
|
MINIO_ENDPOINT: minio
|
|
MINIO_PORT: '9000'
|
|
MINIO_ROOT_USER: '${MINIO_ROOT_USER}'
|
|
MINIO_ROOT_PASSWORD: '${MINIO_ROOT_PASSWORD}'
|
|
MINIO_BUCKET: rastro-photos
|
|
command: 'sh -c "wait-for-db schema rabbitmq && pnpm --filter @rastro/ingestor start"'
|
|
migrate:
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
|
restart: 'no'
|
|
depends_on:
|
|
- db
|
|
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:v4.3'
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- db
|
|
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: '${OSRM_URL}'
|
|
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"'
|
|
dfe-worker:
|
|
# Cadeia MDF-e → CT-e → NF-e (SEFAZ; Teradata para transporte além de D+1) + rota OSRM.
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- db
|
|
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'
|
|
OSRM_URL: '${OSRM_URL}'
|
|
DFE_GEN_INTERVAL: '15'
|
|
DFE_GEN_BATCH: '6'
|
|
DFE_GEN_CONCURRENCY: '6'
|
|
DFE_REFRESH_TTL_HOURS: '12'
|
|
DFE_GEN_ROUTE: 'true'
|
|
SEFAZ_WS_TP_AMB: '1'
|
|
SEFAZ_WS_MAX_CTE: '20'
|
|
SEFAZ_ARQUIVOXML_CUF: '41'
|
|
SEFAZ_ARQUIVOXML_ORGAO: SEFA-PARANA
|
|
SEFAZ_ARQUIVOXML_SETOR: AGAA
|
|
SEFAZ_ARQUIVOXML_VERAPLIC: PR_Rastro_v1
|
|
SEFAZ_ARQUIVOXML_MATR: '999999'
|
|
SEFAZ_ARQUIVOXML_MAX_NFE: '40'
|
|
# Certificado A1 por variável secreta (base64 do .pfx) — sem montar arquivo.
|
|
DFE_CERT_PFX_BASE64: '${DFE_CERT_PFX_BASE64}'
|
|
DFE_CERT_PASSPHRASE: '${DFE_CERT_PASSPHRASE}'
|
|
DFE_TLS_REJECT_UNAUTHORIZED: 'false'
|
|
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_OPEN_MAX_DAYS: '60'
|
|
TD_CHAIN_MIN_AGE_HOURS: '36'
|
|
# wait-for-db osrm: sem OSRM roteando, a rota seria gravada como RETA (fallback).
|
|
command: 'sh -c "wait-for-db schema osrm && pnpm --filter @rastro/processor start:worker"'
|
|
analytics:
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-analytics:v4'
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- db
|
|
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:v4.3'
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- db
|
|
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},https://${CAMPO_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'
|
|
# Rastro Campo (PWA volante). O registro de passagem (610500) nasce DESLIGADO e
|
|
# em HOMOLOGAÇÃO (tpAmb 2): ligar CAMPO_PASSAGEM_ENABLED só depois de ver cStat
|
|
# 135/136 em homologação; produção = SEFAZ_EVENTO_TP_AMB=1.
|
|
CAMPO_PUBLIC_URL: 'https://${CAMPO_DOMAIN}'
|
|
ALPR_URL: 'http://alpr:8000'
|
|
CAMPO_PASSAGEM_ENABLED: '${CAMPO_PASSAGEM_ENABLED:-false}'
|
|
SEFAZ_EVENTO_TP_AMB: '${SEFAZ_EVENTO_TP_AMB:-2}'
|
|
SEFAZ_EVENTO_CORGAO: '${SEFAZ_EVENTO_CORGAO:-91}'
|
|
SEFAZ_EVENTO_CORGAO_AUTOR: '41'
|
|
SEFAZ_EVENTO_CNPJ: '${SEFAZ_EVENTO_CNPJ:-}'
|
|
SEFAZ_EVENTO_ENDPOINTS: '${SEFAZ_EVENTO_ENDPOINTS:-}'
|
|
TD_DOC_CACHE_HOURS_CAMPO: '1'
|
|
command: 'sh -c "wait-for-db schema && pnpm --filter @rastro/backend start"'
|
|
expose:
|
|
- '39010'
|
|
labels:
|
|
- traefik.enable=true
|
|
# Hosts vêm de WEB_DOMAIN/ADMIN_DOMAIN (mesmas variáveis do PREVIEW_ALLOWED_HOSTS e
|
|
# do CORS): domínio hardcoded aqui deixou o /api fora do ar na troca de endereço —
|
|
# o Traefik mandava /api para o `web`, que respondia 403 do vite preview.
|
|
- 'traefik.http.routers.rastroapi.rule=(Host(`${WEB_DOMAIN}`) || Host(`${ADMIN_DOMAIN}`)) && PathPrefix(`/api`)'
|
|
- traefik.http.routers.rastroapi.entryPoints=https
|
|
- traefik.http.routers.rastroapi.tls=true
|
|
- traefik.http.routers.rastroapi.priority=1000
|
|
- traefik.http.routers.rastroapi.service=rastroapi
|
|
- traefik.http.services.rastroapi.loadbalancer.server.port=39010
|
|
# /api no domínio do Rastro Campo: roteador SEPARADO — se CAMPO_DOMAIN estiver
|
|
# vazio, só ele fica inválido; o /api do web/admin continua de pé.
|
|
- 'traefik.http.routers.rastroapicampo.rule=Host(`${CAMPO_DOMAIN}`) && PathPrefix(`/api`)'
|
|
- traefik.http.routers.rastroapicampo.entryPoints=https
|
|
- traefik.http.routers.rastroapicampo.tls=true
|
|
- traefik.http.routers.rastroapicampo.priority=1000
|
|
- traefik.http.routers.rastroapicampo.service=rastroapi
|
|
web:
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
|
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:v4.3'
|
|
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'
|
|
# Rastro Campo — PWA da fiscalização volante. O endereço sai da variável
|
|
# CAMPO_DOMAIN: basta defini-la e apontar o DNS, SEM configurar domínio na tela do
|
|
# Coolify (como o /api do backend já faz). HTTPS é obrigatório — sem ele o
|
|
# navegador não libera a câmera.
|
|
campo:
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
|
restart: unless-stopped
|
|
environment:
|
|
DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public'
|
|
PREVIEW_ALLOWED_HOSTS: '${CAMPO_DOMAIN}'
|
|
command: 'sh -c "pnpm --filter campo preview --host 0.0.0.0 --port 39030"'
|
|
expose:
|
|
- '39030'
|
|
labels:
|
|
- traefik.enable=true
|
|
# Sem PathPrefix e com prioridade baixa: este roteador pega tudo o que sobra no
|
|
# domínio do Campo. O /api do mesmo domínio tem prioridade 1000 no backend e
|
|
# ganha dele — a ordem importa, não a ordem de escrita.
|
|
- 'traefik.http.routers.rastrocampo.rule=Host(`${CAMPO_DOMAIN}`)'
|
|
- traefik.http.routers.rastrocampo.entryPoints=https
|
|
- traefik.http.routers.rastrocampo.tls=true
|
|
- traefik.http.routers.rastrocampo.priority=10
|
|
- traefik.http.routers.rastrocampo.service=rastrocampo
|
|
- traefik.http.services.rastrocampo.loadbalancer.server.port=39030
|
|
# OCR de placas do Rastro Campo (CPU, modelos embutidos na imagem). Só rede interna.
|
|
alpr:
|
|
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-alpr:v1'
|
|
restart: unless-stopped
|
|
expose:
|
|
- '8000'
|
|
volumes:
|
|
osrm_data: null
|
|
dbdata2: null
|
|
rabbitmq_data: null
|
|
minio_data: null
|