Skip to content

@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: *.

EndpointDescrição
GET /indices/api/v1/tabelaTabela 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/reajusteTabela de reajuste de benefícios previdenciários (filtros opcionais por competenciaInicial e competenciaFinal)
GET /indices/api/v1/reajuste-pre-realTabela de reajuste de benefícios pré-Real
GET /indices/api/v1/adiantamentoTabela de adiantamento de abono anual
GET /indices/api/v1/moedasTabela de moedas do período pré-Real
GET /indices/api/v1/os121Tabela de reajuste OS 121 (pré-Real)
GET /indices/api/v1/tabua-mortalidadeTá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.

EndpointAuthDescrição
GET /apps/api/v1/calculadorasNenhumaLista o catálogo público dos apps ativos de categoria calculadora, com IDs canônicos e URLs resolvidas do ambiente
POST /apps/api/v1/exchangeX-Api-KeyCria 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-DD ou YYYY-MM-DDTHH:mm[:ss[.sss]][Z|±hh:mm]). Rejeita valores que produziriam Invalid Date.
  • zCpf — 11 dígitos numéricos com DVs válidos (via validarCpf de @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 revisao para 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:

ColunaTipoDescrição
atualizadoEmtimestamp with time zoneData/hora da alteração
atualizadoPorvarchar(11)CPF do operador (ou 'SISTEMA' para jobs)
tipoAlteracaoenumINSERCAO, ALTERACAO, EXCLUSAO, INSERCAO_AUTOMATICA, ALTERACAO_AUTOMATICA
justificativatextTexto 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 preview executa o pipeline parse → normaliza → valida → calcula diff, mas não persiste nada.
  • A rota de aplicar recalcula o diff dentro de transação, antes do upsert, para evitar aplicar um plano obsoleto.
  • A aplicação usa pg_advisory_xact_lock por 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-Length antes 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 *_audit correspondente, com cpf, 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 ​

FormaComo
Automática (Nitro task)Configurado como defineTask com nome db:job
Manual pelo painelBotão no painel admin dispara a rota privada de execução de job
Agendamento da plataformaExecuta a imagem dedicada do job de atualização
Script diretopnpm 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 .env

O 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 -d

Aguarde 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:migrate

As migrações ficam em ./server/db/migrations e são geradas com pnpm db:generate (Drizzle Kit).

4. Carga inicial de dados ​

Popular a tabela de índices com os dados históricos:

bash
pnpm db:seed

Se 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:local

5b. Dados locais mocados (opcional) ​

Para exercitar manualmente os fluxos de grupos, convites e comunicados na UI, existe um seed idempotente:

bash
pnpm db:dados-locais

O 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/anonimos e 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 dev

pnpm 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:

PathDescrição
/Redireciona para o painel
/_painelPainel admin (requer autenticação)
/_openapi.jsonSpec OpenAPI individual do backoffice (apps, indices)
/authEndpoint de redirecionamento para login via SSO

Fluxo de acesso administrativo ​

  • /_painel reutiliza o bootstrap compartilhado de base-servicos: sessão local, tentativa silenciosa de SSO PDPJ-br/PJe e, por fim, login explícito.
  • O redirecionamento automático para /_painel/:cpf acontece apenas quando o usuário está autenticado e autorizado no cadastro de administradores.
  • backoffice continua responsável apenas pela autorização específica da app: checar o CPF no cadastro de administradores e registrar ultimo_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 apenas Logger
  • scripts/** pode usar consola, por serem comandos CLI executados diretamente no terminal

Tipos de log ​

TagDescrição
backofficeLog operacional de requisições HTTP
backoffice, auditoriaLog 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.concluida
  • indices.atualizacao.automatica.concluida
  • indices.seed.concluido
  • indices.usuarios.listados
  • indices.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:run

Cobrem 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 em server/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 para dist/job.js, formato usado pela imagem de CronJob.
  • pnpm job:build:geoip-download: compila o job de download da base GeoLite2 para dist/job.js.
  • pnpm job:build:geoloc: compila o job de geolocalização para dist/job.js.
  • pnpm job:build:telemetria: compila o job de enriquecimento de usuários da telemetria para dist/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 para db:migrate + db:seed em 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 do backoffice em .swagger/. O portal agregado (que consome esta spec) é servido pela app @fc/docs-api.