# Arquitetura

## Visão geral

```
Navegador (React) ──certificadoId + chave──▶ API (Fastify) ──PowerShell/.NET──▶ Windows (chave privada) ──mTLS──▶ SEFAZ
                  └─(alternativa) .pfx + senha─┘            └──────────── Node (chave em memória) ──mTLS──┘
```

O navegador não consegue usar a chave privada de um certificado para conversar com a SEFAZ, por
isso toda a comunicação passa pela API. O certificado em uso é um `CertificadoCarregado` com duas
origens (`windows` | `arquivo`); `requisicaoMtls` e `assinarXmlSefaz` escolhem o caminho
conforme a origem, e o restante do código não precisa saber de onde ele veio.

## Frontend — `apps/web`

Organizado por **funcionalidade** (feature folders):

```
src/
  app/                 Shell da aplicação: rotas, layout, menu (navegacao.ts)
  components/          Componentes visuais reutilizáveis
  lib/                 Infra do frontend (cliente HTTP, download de arquivos)
  styles/              Tokens de design (cores da marca, tipografia) e estilos base
  features/
    certificado/       Contexto do certificado da sessão + painel de upload
    consulta-documentos/  Página de consulta (uma chave por vez) e cartão de resultado
    danfe/             Leitura do XML da NF-e e layout da DANFE (+ blocos de layout compartilhados)
    danfse/            Leitura do XML da NFS-e Nacional e layout da DANFSe (com QR Code)
    visualizacao/      Página /visualizar/:chave, aberta em nova aba; o XML é repassado via localStorage
```

**Adicionar um módulo novo** (ex.: "Empresas"):

1. Criar `src/features/empresas/` com a página e seus componentes.
2. Registrar a rota em `src/app/router.tsx`.
3. Remover `emBreve` do item correspondente em `src/app/navegacao.ts`.

O certificado é exposto pelo `CertificadoContext`: ao iniciar, ele busca os certificados
instalados (`GET /api/certificados/instalados`) e seleciona um automaticamente (a escolha fica
em `localStorage`). As telas só chamam `formularioBase()`, que envia o `certificadoId` — ou o
`.pfx` + senha, no modo alternativo.

## API — `apps/api`

```
src/
  config/              Variáveis de ambiente
  shared/              Erros de negócio (ErroAplicacao), leitura de formulário
  modules/             Regras de negócio, uma pasta por módulo (rotas + serviço)
    certificado/       Abertura do .pfx, extração de CNPJ/validade
    documentos/        Orquestração do download + cofre local de CT-e
  integracoes/         Clientes de serviços externos, sem regra de negócio
    sefaz/             SOAP 1.2, mTLS, endereços, distribuição NF-e/CT-e, eventos
    nfse/              API REST da NFS-e Nacional
    windows/           Repositório de certificados do Windows (listagem, mTLS e assinatura via .NET)
    xml/               Assinatura XMLDSig no padrão SEFAZ
```

- **Erros**: lance `ErroAplicacao(codigo, mensagem, { status, cStat })`. O handler global devolve
  `{ erro: { codigo, mensagem, cStat } }` (tipo `ErroApi` em `@agil/shared`).
- **Sem build**: o Node executa os `.ts` diretamente (type stripping). Por isso os imports usam a
  extensão `.ts` e não se usa sintaxe que gere código (enums, parameter properties).

## Pacote compartilhado — `packages/shared`

Código usado pelos dois lados: identificação/validação de chave de acesso e os contratos (tipos)
das respostas da API. Mudou um contrato? O TypeScript aponta os dois lados.

## Próximos passos sugeridos

- **Empresas e certificados**: cadastro de empresas com certificado criptografado no servidor
  (evita reenviar o `.pfx` a cada consulta) e autenticação de usuários.
- **Cofre de XML**: persistir todos os documentos baixados (banco + storage S3) e sincronizar
  automaticamente por NSU, para NF-e e CT-e, em rotina agendada.
- **Manifestação**: tela própria com os quatro eventos (ciência, confirmação, desconhecimento,
  operação não realizada).
- **DANFE / DACTE em PDF** a partir do XML.
- **Download em lote (.zip)**.
- Rate limit por empresa na API, respeitando as regras de consumo da SEFAZ.
