compose: rastro-node v4.3 — Rastro Campo (campo + alpr) e Glosas
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d19c6f8873
commit
11eafc29c1
@@ -15,6 +15,9 @@ TAG=latest
|
||||
# ---- Domínios (MUDAM na máquina nova) ----
|
||||
WEB_DOMAIN=rastro.SEU-NOVO-DOMINIO.app.br
|
||||
ADMIN_DOMAIN=admin.rastro.SEU-NOVO-DOMINIO.app.br
|
||||
# Rastro Campo (PWA volante, celular). Atribua este domínio ao serviço `campo`
|
||||
# (porta 39030) no Coolify. HTTPS obrigatório — a câmera exige.
|
||||
CAMPO_DOMAIN=campo.rastro.SEU-NOVO-DOMINIO.app.br
|
||||
|
||||
# ---- Banco ----
|
||||
POSTGRES_USER=rastro
|
||||
@@ -91,3 +94,16 @@ TERADATA_PASSWORD=
|
||||
# "1:<openssl rand -base64 32>". PERDER A CHAVE = PERDER AS PROVAS: guarde cópia fora do servidor.
|
||||
EVIDENCE_KEYS=
|
||||
|
||||
|
||||
# ---- Rastro Campo: registro de passagem (evento do fisco 610500) ----
|
||||
# Assinado com o certificado A1 acima (DFE_CERT_PFX_BASE64). Nasce DESLIGADO e em
|
||||
# HOMOLOGAÇÃO. Hoje o Ambiente Nacional devolve cStat 576: o CNPJ do certificado
|
||||
# (78.393.592/0001-46) ainda não está habilitado como autor do 610500. Depois da
|
||||
# habilitação: validar 135/136 em homologação, então SEFAZ_EVENTO_TP_AMB=1 e ligar.
|
||||
CAMPO_PASSAGEM_ENABLED=false
|
||||
SEFAZ_EVENTO_TP_AMB=2
|
||||
# 91 = Ambiente Nacional (único que recebe o 610500; a SEFAZ-PR responde 491).
|
||||
SEFAZ_EVENTO_CORGAO=91
|
||||
# CNPJ da SEFAZ autora; vazio = o do certificado.
|
||||
SEFAZ_EVENTO_CNPJ=
|
||||
SEFAZ_EVENTO_ENDPOINTS=
|
||||
|
||||
@@ -14,8 +14,9 @@ Não é uma imagem única. São **2 imagens custom** + **5 de infra públicas**:
|
||||
|
||||
| Imagem | Origem | Serviços |
|
||||
|---|---|---|
|
||||
| `rastro-node` | build local → **Gitea** | backend, web, admin, ingestor, processor, dfe-worker, migrate |
|
||||
| `rastro-node` | build local → **Gitea** | backend, web, admin, campo, ingestor, processor, dfe-worker, migrate |
|
||||
| `rastro-analytics` | build local → **Gitea** | analytics (Python) |
|
||||
| `rastro-alpr` | build local → **Gitea** | alpr (OCR de placas do Rastro Campo) |
|
||||
| `timescale/timescaledb-ha:pg16` | Docker Hub | db |
|
||||
| `rabbitmq:3-management` | Docker Hub | rabbitmq |
|
||||
| `minio/minio:latest` | Docker Hub | minio |
|
||||
@@ -81,6 +82,7 @@ aparecem.
|
||||
(`/api` e `/health` no MESMO domínio). Configure no Coolify:
|
||||
- `web` → `https://${WEB_DOMAIN}` (porta 39000)
|
||||
- `admin`→ `https://${ADMIN_DOMAIN}` (porta 39020)
|
||||
- `campo`→ `https://${CAMPO_DOMAIN}` (porta 39030) — o /api desse domínio já vai ao backend pela label do Traefik
|
||||
- `backend` → responder `${WEB_DOMAIN}/api`, `${WEB_DOMAIN}/health`,
|
||||
`${ADMIN_DOMAIN}/api`, `${ADMIN_DOMAIN}/health` (porta 39010) — roteamento por PATH.
|
||||
Se o roteamento por path do Coolify complicar, a alternativa é dar ao backend um domínio
|
||||
|
||||
+55
-12
@@ -75,18 +75,21 @@ services:
|
||||
- '--api-timeout=60s'
|
||||
osrm:
|
||||
# Rotas pelas vias (grafo do Brasil, MLD, v5.26.0, --mmap: ~70-700 MB de RAM).
|
||||
# O grafo (15 GB) JÁ ESTÁ no volume osrm_data (baixado em 2026-09-17, marcado .pronto).
|
||||
# Só se o volume for perdido: adicione `environment: { OSRM_DATA_URL: '<endereço>' }`
|
||||
# aqui, com ~25 GB livres, e o serviço baixa de novo (retoma e confere sha256).
|
||||
# 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.2'
|
||||
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
|
||||
@@ -125,7 +128,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: 'no'
|
||||
depends_on:
|
||||
- db
|
||||
@@ -138,7 +141,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -165,7 +168,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -223,7 +226,7 @@ services:
|
||||
TERADATA_USER: '${TERADATA_USER}'
|
||||
TERADATA_PASSWORD: '${TERADATA_PASSWORD}'
|
||||
backend:
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -237,7 +240,7 @@ services:
|
||||
BACKEND_PORT: '39010'
|
||||
JWT_SECRET: '${JWT_SECRET}'
|
||||
SESSION_COOKIE_NAME: rastro_session
|
||||
CORS_ORIGINS: 'https://${WEB_DOMAIN},https://${ADMIN_DOMAIN}'
|
||||
CORS_ORIGINS: 'https://${WEB_DOMAIN},https://${ADMIN_DOMAIN},https://${CAMPO_DOMAIN}'
|
||||
RATE_LIMIT_MAX: '1000'
|
||||
GOTENBERG_URL: 'http://gotenberg:3000'
|
||||
MINIO_ENDPOINT: minio
|
||||
@@ -258,18 +261,41 @@ services:
|
||||
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
|
||||
- 'traefik.http.routers.rastroapi.rule=(Host(`rastrobr.externo.utic.app.br`) || Host(`admin.rastrobr.externo.utic.app.br`)) && PathPrefix(`/api`)'
|
||||
# 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.2'
|
||||
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'
|
||||
@@ -282,7 +308,7 @@ services:
|
||||
expose:
|
||||
- '39000'
|
||||
admin:
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.2'
|
||||
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'
|
||||
@@ -294,6 +320,23 @@ services:
|
||||
command: 'sh -c "pnpm --filter admin preview --host 0.0.0.0 --port 39020"'
|
||||
expose:
|
||||
- '39020'
|
||||
# Rastro Campo — PWA da fiscalização volante. No Coolify, atribua o domínio
|
||||
# (CAMPO_DOMAIN) a este serviço, porta 39030. HTTPS obrigatório (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'
|
||||
# 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
|
||||
|
||||
+55
-12
@@ -75,18 +75,21 @@ services:
|
||||
- '--api-timeout=60s'
|
||||
osrm:
|
||||
# Rotas pelas vias (grafo do Brasil, MLD, v5.26.0, --mmap: ~70-700 MB de RAM).
|
||||
# O grafo (15 GB) JÁ ESTÁ no volume osrm_data (baixado em 2026-09-17, marcado .pronto).
|
||||
# Só se o volume for perdido: adicione `environment: { OSRM_DATA_URL: '<endereço>' }`
|
||||
# aqui, com ~25 GB livres, e o serviço baixa de novo (retoma e confere sha256).
|
||||
# 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.2'
|
||||
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
|
||||
@@ -125,7 +128,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: 'no'
|
||||
depends_on:
|
||||
- db
|
||||
@@ -138,7 +141,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -165,7 +168,7 @@ services:
|
||||
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.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -223,7 +226,7 @@ services:
|
||||
TERADATA_USER: '${TERADATA_USER}'
|
||||
TERADATA_PASSWORD: '${TERADATA_PASSWORD}'
|
||||
backend:
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.2'
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.3'
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -237,7 +240,7 @@ services:
|
||||
BACKEND_PORT: '39010'
|
||||
JWT_SECRET: '${JWT_SECRET}'
|
||||
SESSION_COOKIE_NAME: rastro_session
|
||||
CORS_ORIGINS: 'https://${WEB_DOMAIN},https://${ADMIN_DOMAIN}'
|
||||
CORS_ORIGINS: 'https://${WEB_DOMAIN},https://${ADMIN_DOMAIN},https://${CAMPO_DOMAIN}'
|
||||
RATE_LIMIT_MAX: '1000'
|
||||
GOTENBERG_URL: 'http://gotenberg:3000'
|
||||
MINIO_ENDPOINT: minio
|
||||
@@ -258,18 +261,41 @@ services:
|
||||
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
|
||||
- 'traefik.http.routers.rastroapi.rule=(Host(`rastrobr.externo.utic.app.br`) || Host(`admin.rastrobr.externo.utic.app.br`)) && PathPrefix(`/api`)'
|
||||
# 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.2'
|
||||
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'
|
||||
@@ -282,7 +308,7 @@ services:
|
||||
expose:
|
||||
- '39000'
|
||||
admin:
|
||||
image: 'gitea.externo.utic.app.br/ericoalmeida/rastro-node:v4.2'
|
||||
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'
|
||||
@@ -294,6 +320,23 @@ services:
|
||||
command: 'sh -c "pnpm --filter admin preview --host 0.0.0.0 --port 39020"'
|
||||
expose:
|
||||
- '39020'
|
||||
# Rastro Campo — PWA da fiscalização volante. No Coolify, atribua o domínio
|
||||
# (CAMPO_DOMAIN) a este serviço, porta 39030. HTTPS obrigatório (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'
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user