Appearance
Persistência de dados
Regra central
Todo acesso ao banco de dados passa pelo app backoffice.
Nenhum outro app do monorepo deve ter drizzle.config.ts, conexão direta com PostgreSQL ou migrations próprias. Dados são lidos e gravados exclusivamente via HTTP para os endpoints do backoffice.
Por que centralizar?
| Problema sem centralização | Solução com backoffice centralizado |
|---|---|
| Migrations desconexas entre apps | Um único histórico de migrations em ordem garantida |
| Schema duplicado / divergente | Um schema Drizzle canônico para toda a base |
Scripts setup:local múltiplos | Um único pnpm setup:local para toda a infraestrutura |
Conflitos de drizzle_migrations | Tabela de controle compartilhada, sem duplicatas |
Diagrama
graph TD
subgraph Calculadoras
A[prev-tc]
B[prev-rvt]
C[prev-atrasados]
D[prev-rpps]
end
subgraph APIs de cálculo
E[prev-api]
F[rmi-api]
end
subgraph Backoffice
G[APIs públicas /indices/api/v1]
H[APIs públicas /apps/api/v1]
I[Painel privado e serviços internos]
N[(PostgreSQL)]
end
subgraph Apps full stack
O[base-servicos — proxies server-side]
end
A -->|useFetch| G
B -->|useFetch| G
C -->|useFetch| G
D -->|useFetch| G
E -->|HTTP| G
F -->|HTTP| G
A -->|useFetch| O
O -->|HTTP autenticado| I
G --> N
H --> N
I --> N
Estrutura de endpoints
Públicos (CORS habilitado)
| Prefixo | Responsabilidade |
|---|---|
GET /indices/api/v1/** | Indexadores monetários, reajustes, tábua de mortalidade |
GET /apps/api/v1/calculadoras | Catálogo público de calculadoras e URLs por ambiente |
POST /apps/api/v1/exchange | Criar token de exchange (X-Api-Key) |
Persistência de cálculos salvos, pastas e preferências não é pública. A central arquivos-online e as calculadoras full-stack chamam proxies server-side herdados de base-servicos. Esses proxies, por sua vez, chamam o backoffice por endpoints internos autenticados por header proprietário. Há também um bloco de rotas administrativas, isolado por gateway/firewall e protegido por sessão admin verificada — esse bloco é exclusivo do painel e nunca é acessado por outras apps.
Persistência no navegador
Cada aba trabalha em um workspace isolado. Antes de o Pinia hidratar as stores, a camada base-ui carrega do IndexedDB os estados daquele workspace para um cache síncrono. Uma fila preserva a ordem das escritas, e presença/heartbeat evitam que duas abas assumam acidentalmente o mesmo workspace.
O IndexedDB é o armazenamento principal e permite recuperação depois de reload, fechamento do navegador ou reinício do computador. Se estiver indisponível, o sistema tenta sessionStorage e depois memória da aba. Esses fallbacks mantêm o trabalho durante a sessão corrente, mas não garantem recuperação após encerramento; por isso o aviso ostensivo permanece visível.
Há dois ciclos locais distintos:
- um Rascunho local preserva automaticamente um Cálculo offline assim que aparecem dados relevantes;
- um Rascunho de recuperação preserva alterações de um Arquivo online ainda não confirmadas pela nuvem, mantendo identificador, revisão-base, geração e prova de completude.
O Rascunho de recuperação não aparece na lista de Rascunhos locais. Em uma queda de rede, ele permite continuar editando na mesma aba e volta a sincronizar quando a conexão retorna. Depois de um reinício, a abertura do Arquivo online oferece retomar a cópia local ou descartá-la e usar a versão da nuvem. Uma cópia incompleta é preservada, mas não entra em autosave e não é transformada em .calc aparentemente completo.
Ao exportar ou abrir um arquivo .calc, o vínculo de nuvem é sempre removido. A cópia passa a ser um Cálculo offline independente; salvá-la depois na nuvem cria outro Arquivo online.
Abrir um arquivo .calc tem quatro etapas, nesta ordem: conferir o envelope, migrar, validar e hidratar. Confere-se que o arquivo se identifica como um cálculo da plataforma e que pertence àquela calculadora; ele é levado ao formato atual pela cadeia de migradores da calculadora; seu conteúdo é validado; e só então substitui o cálculo em edição. Nenhum dado da aba é descartado antes de a validação passar. As calculadoras e os endpoints que recebem arquivo usam o mesmo percurso e, portanto, aceitam o mesmo arquivo.
Isso separa dois modos de falha. Um arquivo malformado é aquele cujo envelope sequer identifica um cálculo. Um arquivo recusado é íntegro e foi migrado, mas contém um valor que a calculadora não aceita: a mensagem indica o registro e o parâmetro, e o cálculo aberto permanece inalterado.
Cálculos online usam controle otimista por revisao: o registro salvo no backoffice incrementa a revisão a cada atualização. Ao salvar, a calculadora envia a revisão que abriu; se outra aba ou outro usuário já tiver salvo uma versão mais nova, o backoffice retorna 409 Conflict. A interface oferece salvar as alterações como novo Arquivo online ou descartá-las e usar a versão da nuvem.
Links de compartilhamento usam token opaco de alta entropia com lookup direto no banco.
Administrativas (sem CORS, mesmo domínio)
| Prefixo | Responsabilidade |
|---|---|
GET /admin/** | Leitura de dados para o painel |
POST /admin/** | Edição com justificativa e trilha de auditoria |
GET /admin/_painel/** | Interface web administrativa (CSR) |
Como outros apps consomem dados
Índices monetários (ex: base-calculadora)
typescript
// Em apps/base-calculadora/app/plugins/00.indices.ts
const dados = await $fetch('/api/v1/tabela', {
baseURL: useRuntimeConfig().public.indicesApiUrl,
})Exchange de dados PrevJud
typescript
// 1. App back-end cria token (servidor → servidor, com X-Api-Key)
const { token } = await $fetch('<url-interna-do-backoffice>/apps/api/v1/exchange', {
method: 'POST',
headers: { 'X-Api-Key': process.env.EXCHANGE_KEY },
body: { app: 'rgps.beneficios', payload: { numeroProcesso, cnis } },
})
// 2. Usuário é redirecionado para `urlAbrir`; a calculadora consome o token
// por um proxy server-side autenticado:
const dados = await $fetch('<rota-server-side-da-calculadora>')Banco de dados
O PostgreSQL é compartilhado por todos os domínios em um único banco. As migrations são aplicadas em sequência pelo backoffice e cobrem famílias de tabelas para: indexadores e taxas (com auditoria por linha), administração e log de acessos, cálculos em nuvem (com soft delete e controle otimista), pastas e grupos de compartilhamento, comunicados e documentos legais, telemetria de uso e cadastro de API keys (armazenadas como hash).
Cada tabela de dados tem uma tabela *_audit correspondente, com colunas atualizadoEm, atualizadoPor (CPF do operador ou SISTEMA), tipoAlteracao (enum) e justificativa (obrigatória em operações manuais).
O catálogo literal de tabelas e a sequência completa de migrations versionadas ficam restritos à documentação interna do projeto.
Inicialização local
bash
cd apps/backoffice
cp .env.example .env
# Preencher as variáveis obrigatórias no .env
pnpm setup:localO script setup:local verifica o Docker, valida o .env, sobe os containers, aplica migrations, popula o banco com dados iniciais e atualiza os índices com dados atuais do Banco Central e IBGE.