Appearance
@fc/backoffice
Backoffice da Fábrica de Cálculos — painel administrativo, API pública de indexadores monetários e taxas de juros, e persistência de dados (cálculos salvos, telemetria de uso, preferências, tokens de API e links de compartilhamento).
Construída com Nuxt (camadas base-api + base-servicos) + Drizzle ORM + PostgreSQL. Expõe:
- endpoints públicos (leitura, com CORS) para consumo pelas calculadoras da Fábrica e por sistemas externos;
- painel administrativo autenticado (
/_painel) para gestão e edição manual dos dados; - endpoints privados, autenticados e bloqueados na infraestrutura, para operações de escrita, auditoria e controle de usuários;
- job de atualização automática que busca índices atualizados nas APIs públicas do SGS/BACEN e do SIDRA/IBGE.
Visão geral
flowchart TD
Fonte["Fonte externa (SGS, SIDRA)"] --> Job["Job automático"]
Painel["Edição manual (painel)"] --> Tabela[("tabela de índices<br/>PostgreSQL")]
Job --> Tabela
Tabela --> Publicas["APIs públicas<br/>(CORS aberto)"]
Tabela --> Privadas["APIs privadas<br/>(autenticadas + isoladas na infra)"]
O app herda da camada base-servicos (serviços internos, notificações e comunicados, além da autenticação — JWT/JWK via SSO institucional e sessão local; a UI Quasar vem de base-ui, alcançada pela cadeia).
API pública
O contrato público desta app é exposto via /_openapi.json. O portal Swagger unificado, com landing e cards agregando todas as APIs da Fábrica (rgps, apps, indices), é servido pela app dedicada @fc/docs-api.
Índices (/indices/api/v1/)
Endpoints públicos (sem autenticação), habilitados com Access-Control-Allow-Origin: *.
| Endpoint | Descrição |
|---|---|
GET /indices/api/v1/tabela | Tabela completa de indexadores e taxas de juros, indexada pela competência em ISO (aaaa-mm, a mesma dos filtros opcionais por competenciaInicial, competenciaFinal e indice[]) |
GET /indices/api/v1/tabela/{competencia} | Linha única da tabela para uma competência específica (formato YYYY-MM) |
GET /indices/api/v1/reajuste | Tabela de reajuste de benefícios previdenciários (filtros opcionais por competenciaInicial e competenciaFinal) |
GET /indices/api/v1/reajuste-pre-real | Tabela de reajuste de benefícios pré-Real |
GET /indices/api/v1/adiantamento | Tabela de adiantamento de abono anual |
GET /indices/api/v1/moedas | Tabela de moedas do período pré-Real |
GET /indices/api/v1/os121 | Tabela de reajuste OS 121 (pré-Real) |
GET /indices/api/v1/tabua-mortalidade | Tábuas de mortalidade (IBGE) |
Apps (/apps/api/v1/)
Endpoints públicos ou explicitamente integráveis por sistemas externos. Persistência de cálculos salvos, pastas e preferências de usuário não é exposta aqui; as calculadoras acessam esses recursos por proxies server-side que chamam endpoints privados de serviço interno, autenticados por header proprietário.
| Endpoint | Auth | Descrição |
|---|---|---|
GET /apps/api/v1/calculadoras | Nenhuma | Lista o catálogo público dos apps ativos de categoria calculadora, com IDs canônicos e URLs resolvidas do ambiente |
POST /apps/api/v1/exchange | X-Api-Key | Cria token de exchange de uso único; retorna token, expiraEm e urlAbrir para abrir calculadoras com pré-preenchimento de dados (RGPS Benefícios e RGPS Atrasados) |
O payload de exchange aceita dados básicos do processo (numeroProcesso, autor, protocolo, citacao) e blocos de documentos estruturados, como RDCTC, CNIS e PrevJud. Para RGPS Atrasados, o bloco PrevJud pode carregar histórico de benefícios e pagamentos HISCRE; opcoesFiltroPrevjud define quais benefícios entram como devidos, pagos ou como descontos de pagamentos.
Documentos legais, links públicos de compartilhamento, permalinks de impressão e o consumo de tokens de exchange são acessados apenas pelas calculadoras via proxies server-side internos — não há endpoint público para esses fluxos.
Formato de resposta
As respostas de sucesso seguem o envelope padrão adotado pela app:
json
{ "status": "ok", "code": "200", "messages": [], "result": {} }Endpoints de listagem paginada incluem também page-info:
json
{
"status": "ok",
"code": "200",
"messages": [],
"result": [...],
"page-info": { "current": 1, "last": 5, "size": 50, "count": 230 }
}A paginação é controlada pelo query param page=size:N,page:N (ex: ?page=size:20,page:2).
Erros HTTP não são embrulhados nesse envelope. A app usa deliberadamente createError do Nitro/H3 como contrato nativo de erro, preservando statusCode, statusMessage/message e a integração padrão do framework.
Esse trade-off foi mantido para evitar dupla padronização:
- sucesso: envelope estável para consumo das apps clientes;
- erro: formato nativo do Nitro, documentado em Swagger/README e tratado no cliente por helpers de extração de mensagem.
Validação de filtros
Query strings das rotas administrativas e públicas são validadas com Zod via parsearQuery(event, schema) (em server/utils/parsearQuery.ts). Em falha, a resposta é 400 via createError({ statusCode: 400, statusMessage: 'Parâmetros inválidos', data: { detalhes } }) — não silenciosa.
Schemas reutilizados (importados via alias de camada @base-api/server/utils/validadores):
zDataIso— string ISO 8601 estrita (YYYY-MM-DDouYYYY-MM-DDTHH:mm[:ss[.sss]][Z|±hh:mm]). Rejeita valores que produziriamInvalid Date.zCpf— 11 dígitos numéricos com DVs válidos (viavalidarCpfde@fc/utils). Rejeita sequência repetida e DVs inválidos.
Ordem obrigatória dentro de cada handler protegido: autenticação antes da validação de query. Um token inválido tem que retornar 401 antes que uma query mal formada tenha chance de disparar 400, evitando que erros de autorização sejam mascarados como "filtro inválido".
APIs privadas
O backoffice mantém duas árvores privadas, isoladas por infraestrutura (gateway/firewall) e protegidas em código por um middleware dedicado:
- Rotas do painel admin: protegidas por sessão autenticada e por verificação positiva contra o cadastro de administradores. Cobrem listagens, edição manual de tabelas, carga em lote de índices, auditoria, gestão de comunicados, documentos legais e API keys, além das operações administrativas sobre cálculos salvos, preferências e links de compartilhamento.
- Rotas serviço-a-serviço: consumidas por outras apps do monorepo (calculadoras full-stack, prev-api, rmi-api). Cobrem validação de API key, catálogo interno de calculadoras, operações de cálculos salvos e pastas, gestão de grupos/convites/permalinks, telemetria de uso e preferências do usuário autenticado.
Segurança: as árvores privadas devem ser bloqueadas a nível de infraestrutura (nginx/gateway) para nunca serem acessíveis externamente. A autenticação no código é a segunda linha de defesa, não a única.
A spec OpenAPI completa, incluindo as rotas privadas, é gerada localmente e mantida em ambiente autenticado. A spec pública só inclui /apps/api/v1/* e /indices/api/v1/*.
Telemetria de uso
Payload de telemetria emitido pelas apps consumidoras:
json
{
"app": "rgps.beneficios",
"versao": "8.0.14",
"acao": "rgps.beneficios.web",
"status": "sucesso",
"duracaoMs": 123,
"idEventoCliente": "018fb25e-7c4a-7b1a-9c1e-6d4be7a6b81d",
"metadados": { "grupos": 2 }
}Taxonomia oficial de acao: <ns>.<recurso>.<ambiente>[.<forma|operacao>]. O dicionário canônico fica em @fc/comum (DICIONARIO_ACOES_USO) e dele é derivada a lista validável ACOES_USO_VALIDAS. O catálogo cobre RGPS web/API, documentos, cálculos salvos, links de compartilhamento e exchange. A escrita recusa aplicações desconhecidas, ações incompatíveis com a origem e metadados com CPF de terceiro ou nome civil; os produtores internos também não montam esses dados. metadados aceita apenas dimensões agregáveis; não envie payloads de cálculo, documentos, nomes, CPF ou número de processo.
status aceita apenas sucesso e erro. metadados é limitado por convenção a poucos KiB serializados em JSON para reduzir risco de dados sensíveis ou payloads grandes.
idEventoCliente é opcional e único. A fila de downloads .calc o reutiliza nas retentativas, permitindo que o backoffice confirme idempotentemente uma gravação cuja resposta tenha se perdido. Para salvamentos, a telemetria semântica registra apenas rgps.calculos-salvos.web.criar/usuario.grupos.web.calculo-criar após o POST; PUT manual, autosave, renomeação, tags e observações não geram eventos de criação. Downloads aceitos pelo navegador usam rgps.arquivos-calc.web.baixar.
Painel administrativo
O painel autenticado em /_painel/{cpf} usa a navegação compartilhada da camada base-ui.
- Navegação lateral baseada em modelo de itens via
FcPainelSidebar(com suporte a item simples e grupo). - Padrão de componentes e composables explicitando o domínio de índices e taxas.
- Lógicas de ordenação/listagem/salvamento extraídas para composables reutilizáveis.
Editor de comunicados e documentos legais
Comunicados e documentos legais usam o mesmo editor de HTML rico. A formatação disponível inclui títulos, parágrafos, citações, código, listas aninhadas, ênfase, alinhamento, links, cores, realces, separadores e seletores próprios de emojis e símbolos. O catálogo de símbolos organiza caracteres do português, tipografia, direito, moedas, matemática, frações, setas, letras gregas e marcadores. O conteúdo colado é normalizado imediatamente para que texto e estrutura de formatação sejam preservados ao salvar, reabrir e publicar. O editor privilegia a experiência de autoria; as prévias reproduzem a apresentação adequada ao destino.
A normalização do navegador e a sanitização do servidor seguem uma política canônica compartilhada. Scripts, formulários, embeds, eventos, URLs perigosas, classes e estilos externos de layout são removidos; imagens, tabelas, fontes e tamanhos arbitrários não fazem parte do formato persistido. Links incompletos são recusados pelo editor. O corpo pode ter até 500 mil caracteres, com contagem visível durante a autoria; o servidor rejeita excessos sem truncar. O banco armazena somente o HTML canônico e os contratos HTTP permanecem inalterados.
Página dedicada de testes
O fluxo de testes de índices e taxas vive em página própria, acessível pelo menu lateral do painel, com resultados renderizados no corpo da página e detalhes de cada suite exibidos inline abaixo da tabela de resultados.
Banco de dados
Gerenciado com Drizzle ORM + PostgreSQL. As tabelas de dados têm uma tabela *_audit correspondente que registra cada alteração.
Famílias de tabelas
- Indexadores e taxas: tabelas de índices monetários, reajuste, reajuste pré-Real, adiantamento, moedas, tabela OS 121 e tábuas de mortalidade — cada uma com sua tabela de auditoria.
- Administração: cadastro de administradores autorizados e log de acessos ao painel.
- Cálculos em nuvem: cálculos salvos com soft delete, vinculados ao CPF do JWT, com
revisaopara controle otimista; pastas pessoais ou de grupo; tokens de exchange de uso único; links públicos de compartilhamento; snapshots imutáveis (permalinks) gerados na impressão. - Grupos: grupos de compartilhamento de cálculos, participantes com níveis de acesso e convites de grupo.
- Comunicados e documentos legais: comunicados administrativos em HTML sanitizado, controle de leitura por CPF, documentos legais públicos versionados.
- Telemetria e segurança: registros de uso (
acao,status, duração, identificador idempotente opcional, origem interna declarada e catalogada, metadados agregáveis, IP, user-agent, CPF/API key); cadastro de API keys (armazenadas como hash).
Auditoria
Cada tabela de índices possui uma tabela *_audit com colunas adicionais:
| Coluna | Tipo | Descrição |
|---|---|---|
atualizadoEm | timestamp with time zone | Data/hora da alteração |
atualizadoPor | varchar(11) | CPF do operador (ou 'SISTEMA' para jobs) |
tipoAlteracao | enum | INSERCAO, ALTERACAO, EXCLUSAO, INSERCAO_AUTOMATICA, ALTERACAO_AUTOMATICA |
justificativa | text | Texto livre obrigatório em operações manuais |
Carga em lote de índices
A carga em lote é uma operação administrativa para aplicar conjuntos de dados nas tabelas de indexadores, reajuste, reajuste pré-Real, adiantamento, moedas, OS 121 e tábua de mortalidade.
Política operacional:
- A rota de
previewexecuta o pipelineparse → normaliza → valida → calcula diff, mas não persiste nada. - A rota de
aplicarrecalcula o diff dentro de transação, antes doupsert, para evitar aplicar um plano obsoleto. - A aplicação usa
pg_advisory_xact_lockpor tipo de carga. Duas cargas simultâneas do mesmo tipo são serializadas; cargas de tipos diferentes podem prosseguir em paralelo. - O corpo da requisição é limitado defensivamente; o limite é conferido por
Content-Lengthantes da leitura e pelo tamanho JSON serializado depois da leitura. - Quando
sobrescreveréfalse, valores já preenchidos no banco são preservados; apenas campos ausentes recebem valores novos. - Toda linha inserida ou alterada gera registro na tabela
*_auditcorrespondente, comcpf,tipoAlteracao, timestamp e justificativa obrigatória. - O preview pode conter muitos itens; a UI usa virtualização para renderizar a lista de diferenças sem criar todos os nós DOM de uma vez.
Indexadores disponíveis
A tabela de índices reúne os indexadores e taxas relevantes para os cálculos judiciais, organizados por competência (YYYY-MM):
ORTN/OTN, BTN, UFIR, IPC/IBGE, IRSM, URV (e variantes CJF e em valor), IPCR, IPC-FGV/CJF, IGP-DI, IGP-M, TR, IPCA, INPC (incluindo primitivo e INPC/INSS), IPCA-E (incluindo IPCA-E/CJF), SELIC, juros de poupança e taxa legal de juros — além de um campo livre observacoes por competência.
Job de atualização automática
O job busca os valores mais recentes nas APIs públicas do SGS/BACEN (@fc/cliente-sgs) e do SIDRA/IBGE (@fc/cliente-sidra), cobrindo da penúltima competência registrada até a competência corrente.
O job só insere ou atualiza linhas onde o dado veio de fonte externa (sobrescrever: false) — alterações manuais feitas pelo painel não são sobrescritas.
Formas de execução
| Forma | Como |
|---|---|
| Automática (Nitro task) | Configurado como defineTask com nome db:job |
| Manual pelo painel | Botão no painel admin dispara a rota privada de execução de job |
| Agendamento da plataforma | Executa a imagem dedicada do job de atualização |
| Script direto | pnpm db:update:indices (executa scripts/indices-job.ts via tsx) |
Preparação do ambiente local de desenvolvimento
1. Configuração
Copie o arquivo de exemplo para iniciar a configuração local:
bash
cp .env.example .envO catálogo de chaves, credenciais, endereços e integrações pertence à documentação interna do backoffice. Nenhum valor operacional deve ser copiado para esta superfície pública.
O catálogo público de calculadoras é derivado de @fc/comum e expõe somente apps ativas da categoria calculadora. Utilidades marcadas para o portal continuam disponíveis na navegação, mas não entram no endpoint de calculadoras. Apps inativas seguem aceitas nos fluxos que precisam preservar dados antigos e aliases legados.
A disponibilidade do SSO no cliente é exposta por GET /auth/config; a tela não tenta inferi-la lendo segredos. As rotas Bearer JWT exigem validação estrita de emissor e audiência e fixam em código o algoritmo RS256 e o claim canônico preferred_username. Falhas de configuração nunca degradam para acesso anônimo.
2. Banco de dados
Inicie o PostgreSQL e o pgAdmin via Docker:
bash
docker compose up -dAguarde o banco estar pronto antes de prosseguir.
Os dados ficam em
./.pg/(ignorado pelo git). Para resetar o banco do zero, remova essa pasta e suba os containers novamente.
3. Migrações
Aplique as migrações no banco:
bash
pnpm db:migrateAs migrações ficam em
./server/db/migrationse são geradas compnpm db:generate(Drizzle Kit).
4. Carga inicial de dados
Popular a tabela de índices com os dados históricos:
bash
pnpm db:seedSe a tabela já tiver registros, use pnpm db:update:indices para atualizar dados existentes.
5. Registrar administradores
Em desenvolvimento local integrado, pnpm dev:local aplica automaticamente os CPFs do arquivo de administradores locais ao cadastro de admins. Para concedê-lo manualmente, use o script:
bash
pnpm db:admins:local5b. Dados locais mocados (opcional)
Para exercitar manualmente os fluxos de grupos, convites e comunicados na UI, existe um seed idempotente:
bash
pnpm db:dados-locaisO script popula, em uma transação única e com UUIDs determinísticos (rodar duas vezes não duplica linhas):
- 5 grupos ativos distribuindo CPFs do realm como proprietários;
- 10 convites cobrindo os cinco estados runtime (
pendente,aceito,recusado,cancelado,expirado), com participantes para os aceitos; - 30 comunicados publicados misturando vigentes/expirados, públicos
todos/autenticados/anonimose alguns ostensivos; - leituras de comunicados pares para os CPFs informados (deixando ímpares pendentes).
Os CPFs sintéticos são informados ao assistente local e nunca persistidos em arquivos versionados.
O fluxo guiado fica em pnpm dev:local: o wizard pergunta se deve popular os dados e quais CPFs do realm receber convites/leituras.
6. Subir a aplicação
bash
pnpm dev
# ou, na raiz do monorepo, para reproduzir backoffice + APIs + calculadoras ativas:
pnpm dev:local
# ou, ainda na raiz:
pnpm --filter @fc/backoffice devpnpm dev:local executa setup:local, aplica os administradores locais, valida as URLs de SSO configuradas no .env e sobe backoffice, prev-api, rmi-api e somente as calculadoras ativas do registro compartilhado. Para ciclos rápidos, use pnpm dev:local -- --pular-setup.
Para desenvolvimento focado, pnpm dev:local aceita --apps=tc (padrão), --apps=tc,atrasados ou --apps=ativas, além de --keycloak=local|cnj|auto. No wizard interativo, ESC volta para o passo anterior e Ctrl+C encerra o ambiente limpando os processos filhos.
A app é servida na porta dedicada de desenvolvimento do backoffice, com os seguintes paths principais:
| Path | Descrição |
|---|---|
/ | Redireciona para o painel |
/_painel | Painel admin (requer autenticação) |
/_openapi.json | Spec OpenAPI individual do backoffice (apps, indices) |
/auth | Endpoint de redirecionamento para login via SSO |
Fluxo de acesso administrativo
/_painelreutiliza o bootstrap compartilhado debase-servicos: sessão local, tentativa silenciosa de SSO PDPJ-br/PJe e, por fim, login explícito.- O redirecionamento automático para
/_painel/:cpfacontece apenas quando o usuário está autenticado e autorizado no cadastro de administradores. backofficecontinua responsável apenas pela autorização específica da app: checar o CPF no cadastro de administradores e registrarultimo_login.- Usuários autenticados sem autorização recebem uma negativa clara no painel e as rotas privadas retornam
403. - A opção "Confiar neste navegador" grava uma sessão local mais duradoura; sem opt-in, o app usa uma sessão curta/de navegador.
- O compartilhamento de cookie entre apps da Fábrica é opcional e exige configuração de domínio e segredo compatíveis entre as participantes.
Logs
A aplicação emite logs estruturados em JSON via Logger de @fc/utils, sempre em stdout.
Regra do projeto nesta app:
server/**usa apenasLoggerscripts/**pode usarconsola, por serem comandos CLI executados diretamente no terminal
Tipos de log
| Tag | Descrição |
|---|---|
backoffice | Log operacional de requisições HTTP |
backoffice, auditoria | Log de auditoria para operações privadas e mutações de índices |
As leituras públicas GET /indices/api/v1/tabela e GET /indices/api/v1/tabela/{competencia} ficam somente no log operacional.
Parâmetros sensíveis de rota e credenciais nunca entram em log. Em especial:
- o endpoint interno de consumo de tokens de exchange tem a URL sanitizada antes do log (token bruto não aparece);
- o endpoint interno de leitura de cálculo via link de compartilhamento tem a URL sanitizada antes do log (token de acesso bruto não aparece);
- o endpoint interno de leitura de permalink tem a URL sanitizada antes do log (token bruto não aparece);
- logs de auditoria dessas rotas não persistem token bruto, assinatura nem qualquer valor reaproveitável.
Eventos de auditoria
indices.atualizacao.manual.concluidaindices.atualizacao.automatica.concluidaindices.seed.concluidoindices.usuarios.listadosindices.job.executado
Os eventos HTTP de auditoria incluem cpf e ip da origem da requisição. Os jobs internos registram cpf: 'SISTEMA' e não incluem ip.
Testes
Testes unitários (Vitest)
bash
pnpm test:run
# ou, na raiz do monorepo:
pnpm --filter @fc/backoffice test:runCobrem utilitários de servidor, operações de banco de dados e middlewares.
Scripts de desenvolvimento
pnpm db:generate: gera migration Drizzle a partir do schema emserver/db/schema/, nomeada com a versão do app.pnpm db:admins:local: aplica no cadastro de admins os CPFs de desenvolvimento do arquivo de administradores locais.pnpm db:comunicados:local: cria fixtures locais de comunicados vigentes, expirados e já lidos para testar pendentes e histórico.pnpm db:migrate: aplica migrações pendentes no banco.pnpm db:seed: carga inicial; popula a tabela de índices com histórico.pnpm db:update:indices: executa o job de atualização automática de forma manual (SGS + SIDRA).pnpm db:geoip:download: atalho local para baixar a base MaxMind GeoLite2-City para~/.fc-cache/geolite2/GeoLite2-City.mmdb.pnpm db:geoloc: atalho local para resolver IPs pendentes em tabela normalizada usando a base GeoLite2 local.pnpm job:build:indices: compila o job de índices paradist/job.js, formato usado pela imagem de CronJob.pnpm job:build:geoip-download: compila o job de download da base GeoLite2 paradist/job.js.pnpm job:build:geoloc: compila o job de geolocalização paradist/job.js.pnpm job:build:telemetria: compila o job de enriquecimento de usuários da telemetria paradist/job.js.
Todos os jobs compilados usam Dockerfile.job; a imagem final contém apenas node e o dist/job.js gerado pelo script informado em JOB_BUILD_SCRIPT.
pnpm db:init: atalho paradb:migrate+db:seedem sequência.pnpm setup:local: script interativo guiado para configurar o ambiente local do zero, com Docker, pgAdmin pré-configurado, banco e seed.pnpm spec:gen: gera a spec OpenAPI individual pública dobackofficeem.swagger/. O portal agregado (que consome esta spec) é servido pela app@fc/docs-api.