Documentação do Broker v1
Fila de integração assíncrona com múltiplos endpoints externos — PostgreSQL + pg-boss, circuit breaker, rate limiting e escalonamento horizontal de workers com atribuição por fila.
Atualizada junto com o código — ver Sobre esta documentaçãoO que é o broker
O broker é um serviço de integração assíncrona baseada em fila: eventos são enfileirados no Postgres (via pg-boss, sem Redis/RabbitMQ/Kafka) e processados por um ou mais processos worker, que chamam uma API externa por HTTP, com retry/backoff nativos, circuit breaker e rate limiting próprios.
Desde a versão atual, o broker suporta múltiplos endpoints externos ao mesmo tempo — cada integração tem sua própria fila, credencial, circuit breaker e rate limiter, isolados uns dos outros. Um dashboard web dá visão em tempo real (SSE) de todas as filas, permite criar/editar endpoints, escalar workers e atribuir filas específicas a workers específicos.
Princípios de arquitetura
- Postgres como única fonte de verdade. Sem infraestrutura extra — a fila, o estado de circuit breaker, a configuração de endpoints e os logs estruturados moram todos no mesmo Postgres.
- Retry/backoff 100% nativos do pg-boss. O código da aplicação nunca reimplementa retry — só classifica erros (permanente vs. transitório) para decidir o que conta pro circuit breaker.
- Isolamento por endpoint. Cada integração externa tem fila, client HTTP, circuit breaker e rate limiter próprios — uma API externa degradada nunca consome a capacidade de processamento de outra.
- Live-reconfig sem restart. Endpoints, parâmetros, atribuição de filas por worker — tudo é lido de tabelas do Postgres e aplicado nos próximos ciclos de poll do worker, sem precisar reiniciar nada.
- Observabilidade em primeiro lugar. Todo estado relevante (fila, circuit breaker, workers vivos) é visível no dashboard em tempo real via Server-Sent Events.
Arquitetura de ambiente
Em desenvolvimento/produção local, tudo roda via docker-compose, numa rede privada
própria (broker_default), com um Postgres dedicado (não compartilhado com outras
aplicações).
broker_default, o navegador e a API externa ficam fora dela, e o motivo pelo qual o dashboard mantém um registro próprio de instâncias de worker em vez de confiar só no WORKER_CONTROL_URL.Serviços do docker-compose.yml
| Serviço | Imagem/Build | Réplicas | Porta publicada | Volume |
|---|---|---|---|---|
postgres | postgres:16-alpine | 1 (fixo) | POSTGRES_PORT → 5432 | broker_pgdata |
worker | docker/Dockerfile.worker | 1..N (--scale worker=N) | nenhuma (control server é interno) | broker_logs |
dashboard | docker/Dockerfile.dashboard | 1 | DASHBOARD_PORT → 8080 | broker_logs |
mock-api dev only | docker/Dockerfile.mock-api | 1 | MOCK_API_PORT → 8090 | — |
mock-api só existe no overlay docker-compose.dev.yml e recusa iniciar se NODE_ENV=production — nunca sobe junto com o compose principal por acidente.Subindo o ambiente completo
# stack de producao/uso normal (postgres + worker + dashboard)
docker-compose up -d --build
# + laboratorio de testes (mock-api)
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
# escalando workers horizontalmente
docker-compose up -d --scale worker=3
Arquitetura de componentes
Fluxo de dados entre os componentes lógicos da aplicação (independente de estarem em containers separados ou não):
SKIP LOCKED, cada endpoint tem um runtime totalmente isolado dentro de cada worker.Worker por dentro (src/worker/)
| Arquivo | Responsabilidade |
|---|---|
index.js | Loop principal: sobe um runtime por endpoint habilitado, reconcilia jobs órfãos no boot, faz poll periódico de app_endpoints/atribuição de filas, heartbeat de instância, control server. |
processor.js | Processa um job: aplica rate limit, checa circuit breaker, chama a API externa, classifica erro (4xx permanente vs. 5xx/timeout transitório). |
circuitBreaker.js | Máquina de estados CLOSED/OPEN/HALF_OPEN em memória, por endpoint. |
rateLimiter.js | Janela deslizante de chamadas/segundo, por endpoint. |
concurrencyManager.js | Gerencia quantas assinaturas boss.work() ficam ativas, sem nunca ultrapassar o configurado durante uma mudança. |
instanceRegistry.js | Heartbeat da instância + atribuição de filas (app_worker_instances). |
controlServer.js | HTTP interno (pause/resume/status) usado pelo dashboard e pela console CLI. |
alerts.js | Alerta (log + webhook opcional) para backlog e jobs travados. |
Dashboard por dentro (src/dashboard/)
server.js— servidor HTTP com todas as rotas/api/*, SSE (/api/events), e serve os arquivos estáticos depublic/(incluindo esta página).queries.js— todas as consultas SQL de leitura empgboss.jobe tabelas auxiliares.public/index.html— SPA de uma página só (sem build step, sem framework), com todas as abas.
Tabelas do Postgres
| Tabela | Schema | Dono | Conteúdo |
|---|---|---|---|
job | pgboss | pg-boss | Fila real — um registro por job, uma fila (name) por endpoint. |
schedule, subscription, version | pgboss | pg-boss | Internos do pg-boss — só leitura na aba Tabelas. |
app_endpoints | público | broker | Configuração de cada integração externa (URL, credencial, limites). |
app_worker_instances | público | broker | Heartbeat + atribuição de filas de cada processo worker vivo. |
app_circuit_breaker_state | público | broker | Último estado publicado do circuit breaker de cada fila. |
app_config | público | broker | Parâmetros globais editáveis (alertas, intervalos do dashboard). |
app_logs | público | broker | Log estruturado (JSON) de worker e dashboard, fonte principal da aba Logs. |
Múltiplos endpoints
O broker integra com N endpoints externos ao mesmo tempo, cada um com sua
própria fila pg-boss (nomeada ${QUEUE_NAME}.<endpoint-id>),
credencial, circuit breaker, rate limiter e política de retry.
Limitação conhecida: tipos de credencial
O client HTTP manda um header estático por chamada — cobre API key, Bearer token e HTTP Basic (pré-codificado). Não cobre esquemas que exigem assinar o corpo por requisição (ex: HMAC) sem uma extensão pontual do client.
Escalonamento e workers
Duas dimensões de escala
- Vertical — aumentar
concurrencyde um endpoint (ao vivo, aba Admin). - Horizontal — múltiplas réplicas do processo worker (
docker-compose --scale worker=N,scripts/run-local.sh worker=N, ou o botão + na aba Arquitetura).
rateLimitMaxCalls e o circuit breaker são em memória, por processo — com N réplicas na mesma fila, o volume real contra a API externa pode chegar a rateLimitMaxCalls × N. Divida o limite pelo número de réplicas planejadas.Dedicar um worker a filas específicas
Por padrão todo worker consome todas as filas habilitadas. Pelo ícone de lápis (✎) no card de cada instância (aba Arquitetura), dá para desmarcar "Todas as filas" e escolher um subconjunto — o worker deixa de consumir as demais imediatamente (a atribuição é lida a cada ciclo de poll, sem restart), sem afetar circuit breaker/rate limiter das filas onde ele continua atuando, nem o consumo de outros workers.
A tabela "Todos os Endpoints" mostra quantos workers estão atribuídos a cada fila, com aviso se for 0 (fila parada) ou >1 (rate limit/circuit breaker multiplicado).
Instalação
Pré-requisitos
- Docker + Docker Compose (recomendado) ou Node.js 20+ para rodar nativo.
- Nenhum outro serviço já usando as portas escolhidas (por padrão 5544, 8080, 8090 — ajustável no
.env).
Passo a passo (Docker — recomendado)
git clone <repo> broker && cd broker
cp .env.example .env
# edite .env: senha do Postgres, credenciais de cada endpoint, etc.
docker-compose up -d --build
# abre o dashboard em http://localhost:8080
Rodando nativo (sem Docker)
npm install
cp .env.example .env
cp .env.example .env.local # ajuste DATABASE_URL/EXTERNAL_API_URL para "localhost"
scripts/run-local.sh # 1 worker + dashboard
scripts/run-local.sh worker=3 # 3 workers nativos
scripts/stop-local.sh # para tudo (nativo E docker-compose, se estiver rodando)
.env.example (postgres, mock-api) só resolvem dentro da rede do docker-compose. Rodando nativo, use .env.local (git-ignorado) com localhost — run-local.sh detecta e avisa se isso não estiver configurado.Criação do banco de dados
Não existe um script de migração para rodar manualmente — o schema é self-provisioning. Tanto o schema do pg-boss quanto as tabelas próprias da aplicação são criados sozinhos, automaticamente, na primeira vez que um processo sobe:
- Schema
pgboss(fila, agendamentos, versões) — criado pelo próprio pg-boss dentro deboss.start(), chamado no boot do worker, do dashboard e do console de controle. Não é código da aplicação. - Tabelas
app_*(endpoints, config, workers, circuit breaker, logs) — cada módulo chama seu próprioensure<X>Table()no boot, usando o helpercreateTableIfNotExists()desrc/config/db.js(CREATE TABLE IF NOT EXISTS, com proteção contra a corrida de duas sessões criando a mesma tabela ao mesmo tempo).
Ou seja: o "processo de criar o banco" é simplesmente subir a aplicação pela
primeira vez — não existe npm run migrate nem SQL para rodar à mão.
O que precisa existir antes disso é só a role e o database vazios:
Via Docker (automático)
A imagem oficial postgres:16-alpine já cria a role e
o database sozinha no primeiro boot do container, usando as variáveis do .env:
POSTGRES_USER=broker
POSTGRES_PASSWORD=change-me
POSTGRES_DB=broker_queue
Só acontece se o volume broker_pgdata estiver
vazio — depois da primeira vez, mudar essas variáveis não recria nada.
Postgres nativo/externo (manual)
Sem o Docker cuidando disso, crie a role e o database uma vez,
antes do primeiro npm run worker/dashboard:
psql -U postgres -c "CREATE ROLE broker WITH LOGIN PASSWORD 'change-me';"
psql -U postgres -c "CREATE DATABASE broker_queue OWNER broker;"
Aponte DATABASE_URL em .env.local
para esse database — o resto (schemas e tabelas) se cria sozinho no boot.
worker e dashboard ao mesmo tempo (ou várias
réplicas de worker) logo na primeira vez — o createTableIfNotExists() existe
justamente para resolver a corrida de duas sessões tentando criar a mesma tabela
simultaneamente (Postgres erro 23505), então não precisa subir um processo "primeiro" para
preparar o banco.Verificando a instalação
docker-compose ps— todos os serviçosUpepostgreshealthy.- Abrir
http://localhost:8080— deve carregar a tela "Todos os Endpoints". - Popular jobs de teste:
docker-compose exec worker node scripts/seed.js 10. - Confirmar no dashboard que os jobs aparecem e completam.
Manual de configuração
Duas camadas de configuração: variáveis de ambiente (infra/boot) e configuração dinâmica no Postgres, editável ao vivo pelo dashboard sem reiniciar nada.
Endpoints (por integração externa)
Cada endpoint é um registro em app_endpoints, gerenciado 100% pelo dashboard (aba
Parâmetros) ou pela API:
| Campo | Descrição |
|---|---|
id | Slug único (minúsculo, hifens) — vira o sufixo da fila. |
label | Nome amigável exibido no dashboard. |
baseUrl | URL HTTP (POST) da API externa. |
authHeaderName / authHeaderValue | Header estático de autenticação (API key, Bearer, Basic pré-codificado). O valor nunca é reexibido depois de salvo. |
timeoutMs | Timeout da chamada HTTP. |
concurrency | Assinaturas boss.work() paralelas por processo worker. |
rateLimitMaxCalls / rateLimitWindowMs | Limite de chamadas à API externa, por processo. |
circuitBreakerFailureThreshold / WindowMs / OpenMs | Limiares do circuit breaker. |
retryLimit / retryDelay / retryBackoff | Retry nativo do pg-boss — só vale para jobs enviados depois da mudança. |
enabled | Desativa o consumo sem apagar a configuração. |
GET /api/endpoints
POST /api/endpoints { id, label, baseUrl, authHeaderName, authHeaderValue, ... }
PUT /api/endpoints/:id (parcial - so os campos enviados sao atualizados)
DELETE /api/endpoints/:id (remove so a config; nao apaga jobs ja enfileirados)
Parâmetros globais
Editáveis na aba Parâmetros, aplicados ao vivo (releitura a cada workerConfigPollMs):
| Grupo | Campos |
|---|---|
| Alertas | alertPendingJobsThreshold, alertStuckActiveMinutes, alertCheckIntervalMs |
| Dashboard | dashboardRefreshMs (push SSE), workerConfigPollMs (releitura pelo worker) |
Variáveis de ambiente (.env)
Lista completa e comentada em .env.example. Principais grupos:
- Postgres:
POSTGRES_USER/PASSWORD/DB/PORT,DATABASE_URL - pg-boss:
QUEUE_NAME,PGBOSS_EXPIRE_IN_SECONDS,PGBOSS_MAINTENANCE_INTERVAL_SECONDS - Dashboard:
DASHBOARD_PORT - Worker:
WORKER_CONTROL_PORT,WORKER_CONTROL_URL - Laboratório de testes (dev):
MOCK_API_*
Como usar: enviando dados para a fila
Existem duas formas de enfileirar um evento. Para qualquer sistema externo (de qualquer linguagem), use a API HTTP de ingestão — é o caminho recomendado. Se o produtor já é um processo Node.js rodando dentro da mesma infraestrutura do broker, também é possível usar o cliente pg-boss diretamente (biblioteca), sem passar pela rede.
API HTTP de ingestão
POST /api/ingest/:endpointId, servida pelo mesmo processo do dashboard. O
corpo da requisição é exatamente o JSON que o worker vai enviar (via POST)
para o baseUrl configurado nesse endpoint — o broker não adiciona nem remove campos.
Passo a passo
- Cadastre o endpoint de destino (aba Admin ou
POST /api/endpoints) — é oiddesse registro que vira:endpointIdna URL. - Na mesma aba, clique em Gerar chave na coluna "Ingestão HTTP" desse endpoint. A chave aparece uma única vez — copie na hora.
- Chame a API mandando essa chave no header
X-Ingest-Key.
Contrato
| Item | Valor |
|---|---|
| Método/URL | POST /api/ingest/:endpointId |
| Headers obrigatórios | Content-Type: application/json, X-Ingest-Key: <chave> |
| Corpo | Objeto JSON qualquer (não pode ser array/string/número na raiz) — vira o payload enviado à API externa. |
| Sucesso | 202 Accepted — { "jobId": "...", "queueName": "..." } |
| Status | Quando | code |
|---|---|---|
| 400 | corpo não é JSON válido, ou não é um objeto na raiz | invalid_json / invalid_payload_shape |
| 401 | X-Ingest-Key ausente ou incorreta | invalid_ingest_key |
| 403 | endpoint desativado, ou sem chave de ingestão gerada ainda | endpoint_disabled / ingest_not_configured |
| 404 | endpointId não existe | endpoint_not_found |
| 413 | corpo maior que MAX_INGEST_BODY_BYTES (padrão 256KB) | payload_too_large |
| 415 | Content-Type não é application/json | unsupported_media_type |
Exemplo
curl -X POST https://seu-dominio/api/ingest/orders \
-H "Content-Type: application/json" \
-H "X-Ingest-Key: <chave gerada na aba Parametros>" \
-d '{"orderId": "A-1029", "status": "paid", "total": 149.9}'
# 202 Accepted
# {"jobId":"9f2c...","queueName":"external-api-events.orders"}
Funciona de qualquer linguagem — só precisa fazer um POST com os dois headers.
Em Python, por exemplo:
import requests
requests.post(
"https://seu-dominio/api/ingest/orders",
headers={"X-Ingest-Key": "..."},
json={"orderId": "A-1029", "status": "paid", "total": 149.9},
)
X-Ingest-Key viajaria em texto claro pela
internet. Termine TLS num proxy reverso (Caddy, nginx) antes de aceitar tráfego de
produtores externos de verdade; o Node não deveria terminar TLS ele mesmo.
retryLimit/retryDelay/retryBackoff da aba
Parâmetros) — ao contrário do uso via biblioteca pg-boss abaixo, aqui não tem como
esquecer de passar essas opções.
Alternativa avançada: biblioteca pg-boss direto (só Node.js)
Se o produtor já é Node.js e prefere não depender de rede/HTTP (ex: outro processo dentro
da mesma infraestrutura, com acesso direto ao Postgres do broker), dá para inserir o job
direto na fila com o cliente pg-boss
— exatamente como scripts/seed.js e scripts/console.js já fazem.
Essa via não passa pela autenticação/limite de tamanho da API HTTP (é o mesmo nível de
confiança que o próprio worker).
import pg from 'pg';
import { env } from './src/config/env.js';
import { createBoss } from './src/config/pgboss.js';
import { queueNameFor, listEndpoints, buildEndpointSendOptions } from './src/config/endpoints.js';
const ENDPOINT_ID = 'orders'; // id cadastrado na aba Parametros
const pool = new pg.Pool({ connectionString: env.databaseUrl });
const [endpoint] = await listEndpoints(pool, { onlyEnabled: true })
.then((rows) => rows.filter((e) => e.id === ENDPOINT_ID));
await pool.end();
if (!endpoint) throw new Error(`endpoint "${ENDPOINT_ID}" nao encontrado ou desativado`);
const boss = createBoss();
await boss.start();
const jobId = await boss.send(
queueNameFor(endpoint.id),
{ orderId: 'A-1029', status: 'paid', total: 149.9 },
buildEndpointSendOptions(endpoint), // aplica o retryLimit/retryDelay/backoff DESSE endpoint
);
console.log(`job ${jobId} enfileirado na fila ${queueNameFor(endpoint.id)}`);
await boss.stop();
retryLimit/retryDelay/retryBackoff
no boss.send(), o pg-boss usa os defaults dele — não os
valores configurados para o endpoint no dashboard. Use buildEndpointSendOptions()
(ou copie os mesmos valores) para que o retry realmente siga a política daquele endpoint.Testando sem uma API real
Para popular filas com dados fake e validar o fluxo de ponta a ponta antes de conectar um produtor de verdade, use o script pronto (ver Laboratório de testes):
# popula TODOS os endpoints habilitados com 20 jobs cada
node scripts/seed.js 20
# popula so um endpoint especifico
node scripts/seed.js 20 orders
Laboratório de testes (mock-api)
Serviço à parte, só Node core, com 5 endpoints — um por tipo de credencial — que sorteiam sucesso/erro para exercitar circuit breaker, retry e rate limit sem depender de internet.
| Endpoint | Credencial |
|---|---|
POST /webhook/api-key | header X-Api-Key |
POST /webhook/bearer | Authorization: Bearer |
POST /webhook/basic | HTTP Basic |
POST /webhook/hmac-signature | HMAC do corpo — referência, não testável pelo client real |
POST /webhook/public | nenhuma |
⚠️ Nunca sobe em produção — só existe no overlay docker-compose.dev.yml.
Guia do dashboard
Tour pelas abas do dashboard, com screenshots reais da instância local. O ícone de sol/lua no cabeçalho alterna entre tema claro e escuro (preferência salva no navegador, detecta o tema do sistema operacional na primeira visita) — as capturas abaixo estão no tema escuro (padrão), mas toda a interface tem equivalente claro.
Todos os Endpoints (tela inicial)
Visão agregada de todas as integrações lado a lado: diagrama (Worker → cada endpoint), totais consolidados, e uma tabela com circuit breaker, contagem por estado, volume e latência de cada fila — inclusive quantos workers estão atribuídos a cada uma.
Overview (por endpoint)
Escopada pelo seletor de endpoint no topo: contagem por estado, circuit breaker, backlog, taxa de erro, throughput com seletor de período, gráficos de completados x falhados e criados x resolvidos, distribuição de retries, diagnóstico automático de gargalo (fila vs. API externa vs. rate limit).
Jobs
Grid paginada com todos os jobs de qualquer estado. Filtro de Fila independente do
seletor do topo (endpoint selecionado, todas as filas juntas, ou uma específica), filtro por estado
e busca no conteúdo. Jobs failed podem ser reprocessados; created/retry
podem ser editados antes de rodar.
Tabelas
Browser genérico das tabelas internas do pg-boss e das tabelas próprias da aplicação. Tabelas com identidade única têm checkbox de seleção e os botões Excluir selecionados (protegido — nunca apaga jobs pendentes/em processamento) e Limpar tabela inteira (sem exceção).
Parâmetros
Edição em tempo real dos parâmetros globais (alertas, refresh do dashboard) e a seção de manutenção para limpar dados de teste. Gestão de endpoints, usuários e o envio de carga de teste ficaram numa aba própria — ver Admin — visível só para administradores.
Admin só administradores
Aba inteira invisível para o perfil user (nem aparece na barra de abas) — ver
Autenticação e controle de acesso. Reúne tudo que é gestão de
acesso/provisionamento, separado da aba Parâmetros (que ficou só com ajuste fino):
- Endpoints — o mesmo CRUD que existia em Parâmetros (criar, editar, ativar/desativar, excluir, gerar/regenerar chave de ingestão).
- Usuários do dashboard — criar/excluir usuários e definir o perfil
(
admin/user). - Enviar carga de teste — checklist com todos os endpoints
cadastrados (nome + status ativo/desativado, seleção múltipla, atalhos "todos"/
"nenhum"), um campo de quantidade (padrão 10, editável), botão
Enviar, e ao lado um contador ao vivo ("Na fila agora")
somando os jobs pendentes (
created+retry) só dos endpoints marcados — atualizado via SSE, sem polling próprio.
Equivale a POST /api/admin/seed com { count, endpointIds } — envia
pra cada endpoint marcado, e devolve separadamente o que foi enfileirado
(seeded) e o que foi ignorado (skipped, com o motivo:
endpoint_disabled ou endpoint_not_found) — selecionar um endpoint
desativado não derruba o envio dos demais.
Arquitetura (ao vivo)
Diagrama animado: Dashboard → Postgres → Worker(s) → API Externa, com o estado real do circuit breaker, slots de concorrência ocupados, e a lista de todas as instâncias de worker vivas — cada uma com suas filas atribuídas e os botões + (subir novo worker) e − (encerrar uma instância específica).
Autenticação e controle de acesso
O dashboard inteiro fica atrás de login. Dois perfis:
| Perfil | Pode |
|---|---|
| admin | Tudo — CRUD de endpoints, gerenciar usuários, gerar/regenerar chaves de ingestão, editar parâmetros globais, botão de teste (seed), Swagger, pausar/retomar consumo, excluir/limpar tabelas, subir/derrubar workers. |
| user | Somente leitura — Overview, Jobs, Logs, Tabelas (visualizar), Todos os Endpoints, Arquitetura. Qualquer tentativa de escrita retorna 403, reforçado no servidor (o front-end só esconde os botões, não é a barreira de segurança real). |
Usuário master
Existe um usuário "master" sempre disponível, independente do que
estiver cadastrado em app_users — vem de variáveis de ambiente e nunca
é gravado como linha comum na tabela:
ADMIN_USERNAME=julio # default se omitido do .env
ADMIN_PASSWORD=102938 # default se omitido - TROQUE em producao
app_users — é o acesso de emergência da aplicação. Não dá pra criar um usuário chamado julio pela aba Admin (nome reservado).Sessão e senha
- Sessão em Postgres (tabela
app_sessions, cookieHttpOnly, 24h) — não é um cookie assinado sem estado. Isso importa porque permite revogar na hora: quando um admin exclui um usuário, a sessão dele morre imediatamente, não só quando o cookie expira sozinho. - Senha com
crypto.scryptSyncnativo do Node (memory-hard, resistente a GPU/ASIC) — sem trazer bcrypt/argon2 como dependência nova só por causa disso. Comparação sempre viatimingSafeEqual.
Gerenciar usuários
Aba Admin → "Usuários do dashboard", ou via API:
GET /api/users (admin)
POST /api/users { username, password, role } (admin)
DELETE /api/users/:id (admin - revoga a sessao na hora)
Tokens de ingestão continuam por-endpoint
Decisão deliberada: não criamos um segundo mecanismo de token
por-usuário. Cada endpoint já é uma fila isolada — "1 token = 1 endpoint" já dá o
isolamento certo (comprometer uma chave expõe só aquela fila, não várias). O que mudou
com o login: gerar/regenerar a X-Ingest-Key agora exige sessão de
admin, e fica registrado quem gerou (coluna
ingest_key_created_by, visível na aba Admin) — auditoria sem duplicar
o mecanismo de autenticação. Ver API HTTP de ingestão.
Swagger (admin)
Link no menu Ajuda (só aparece logado como admin) →
/admin/swagger.html — Swagger UI carregada via CDN, documentando
só a API de ingestão (POST /api/ingest/:endpointId e o
endpoint de regeneração de chave). É o único contrato que um sistema externo de fato
consome; o resto do dashboard é operação interna do próprio time, não vale a pena
formalizar em OpenAPI. O spec (GET /api/openapi.json) também exige sessão
de admin — a página HTML em si carrega pra qualquer um, mas fica vazia/nega acesso sem
um fetch autenticado bem-sucedido.
Botão de teste (seed)
Aba Admin → "Enviar carga de teste" — checklist de endpoints (seleção
múltipla, mostra nome + status ativo/desativado), campo de quantidade (padrão 10) e um
contador ao vivo de jobs pendentes nos endpoints marcados. Mesma lógica de
scripts/seed.js, mas roda dentro do processo do dashboard
(reaproveitando o sendBoss já existente ali para reprocessamento), sem subir
um subprocesso. Equivale a POST /api/admin/seed (admin-only), que aceita
{ count, endpointIds } no corpo — sem eles, usa 10 jobs em todos os endpoints
habilitados; endpoints desativados ou inexistentes na lista voltam em skipped
em vez de derrubar a chamada inteira. Ver tela Admin.
Segurança e boas práticas
- Credenciais nunca são reexibidas.
authHeaderValueé write-only — a API só retorna{configured: true/false}. - Postgres dedicado. Não compartilhar a instância com outras aplicações.
- Sem socket do Docker no dashboard. O botão "+" de subir worker roda um processo filho dentro do próprio container do dashboard, deliberadamente — não monta o socket do Docker, que daria ao dashboard controle total sobre containers do host.
- Exclusão de tabelas é protegida por padrão.
pgboss.jobnunca é apagado em estado pendente/ativo pela grid, exceto na ação explícita "Limpar tabela inteira". - mock-api nunca em produção — trava por
NODE_ENVe por estar isolado num overlay de compose separado.
Sobre esta documentação
Esta página (src/dashboard/public/docs.html) é servida pelo próprio dashboard —
acessível pelo menu Ajuda → Documentação no cabeçalho. Ela é parte do
código do projeto, não um documento à parte: toda vez que a arquitetura, as telas ou o
processo de configuração mudarem, esta página deve ser atualizada na mesma alteração (mesmo
princípio já seguido para o README.md).
Os screenshots em docs-assets/ foram capturados via Chrome headless
(--headless --screenshot) direto da instância local rodando — para atualizá-los depois
de uma mudança visual relevante:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless --disable-gpu --window-size=1400,1000 \
--screenshot=src/dashboard/public/docs-assets/overview.png \
"http://localhost:8080/#overview"
Trocando #overview pela aba desejada (#all-endpoints, #jobs,
#tables, #params, #architecture) — o hash na URL já aciona a
troca de aba automaticamente (mesmo mecanismo usado por link direto no dashboard).