# Arquitetura implementada

Um repositório, domínio, processo Node, SQLite e storage privado por instituição. A baseline foi extraída para um repositório independente; as fontes CSPVF foram preservadas. O desenho inicial desta arquitetura foi escrito antes de qualquer refactor.

## Separação concreta

- `app/`: páginas institucionais estáticas, backoffice dinâmico e handlers HTTP.
- `components/`: cabeçalho/rodapé e imagens no servidor, formulário e confirmação de arquivo no cliente.
- `config/`: identidade, branding, processo de inscrições, email e PDF.
- `content/`: conteúdos, navegação, documentos e manifesto de imagens.
- `server/`: SQLite/storage, segurança, autenticação, validação, emails, aquisição de dados PDF e jobs.
- `emails/`: templates HTML. `pdf/`: desenho do PDF sem dependência de request/DB.
- `lib/`: pequenos adaptadores utilizados pelo backoffice herdado e rótulos. `server-env` tem DB/FILES locais com tipos próprios, sem dependência Cloudflare.
- `migrations/`: SQL versionado e checksums. Não há ORM nem migrations durante requests.
- `assets/images/`: originais para build; `public/`: branding, imagens otimizadas e documentos públicos.
- `scripts/`: operações finitas. `tests/`: validação e integração com dados temporários.

Mantém-se `app/` na raiz, em vez de mover tudo para `src/`: evitar uma alteração de caminhos que não poupa manutenção. Não existe abstração genérica de campos ou templates visuais. A aparência neutra pode ser substituída sem alterar os handlers de inscrições.

## Fluxos

**Institucional:** build → HTML estático/cache Next. Nenhuma leitura de DB/auth/serviços externos pela homepage. Links HTML evitam prefetch do backoffice/formulário. Fontes de sistema, sem trackers. O runtime base Next/React continua a ser descarregado; peso medido e explicitado no benchmark.

**Inscrição:** limite de corpo real → validação servidor de campos/documentos/consentimentos → escrita de uploads privados → transação candidatura + histórico + comunicações + outbox → 201. Unique idempotency key e limpeza dos uploads em rollback/falha. Todos os ficheiros são validados antes da primeira escrita. Não há uma transação distribuída entre filesystem e SQLite: um crash abrupto entre upload e commit ainda pode deixar um ficheiro órfão; não é exposto publicamente. Comparar storage com chaves registadas antes de qualquer limpeza manual.

**Rascunho:** valores limitados → AES-GCM + IV aleatório → token aleatório devolvido à família, apenas hash na DB → expiração. Sem uploads no rascunho. Ligação com token funciona como segredo; no-store e no-referrer.

**Admin:** password scrypt por utilizador → sessão opaca de oito horas, só hash na DB → cookie HttpOnly/SameSite/Secure em HTTPS → papel ADMIN em cada handler. Origem obrigatória em mutações, rate limit no login e APIs; logout POST revoga sessão. Nenhum bypass de domínio de email.

**PDF/email:** comando finito reclama trabalho por UPDATE ... RETURNING atómico → lease de 5 min → geração local PDF / SMTP com timeout → DONE, ou backoff e FAILED após 5 tentativas. Trabalhos recuperam após lease expirado. Os retries usam Message-ID estável, mas SMTP não garante ausência de duplicados após crash entre envio e persistência.

## Decisões de simplicidade

Sem builder/CMS, banco de conteúdos, Redis, broker, microserviços, Docker ou plataforma de tenants. SQL parametrizado explícito e índices; paginação 25, CSV 200 por lote. Sem frameworks CSS: um global pequeno + CSS por funcionalidade. Imagens no build (Sharp de desenvolvimento), sem consumo de CPU para redimensionar na primeira visita.

SQL e storage local pressupõem um host por instituição. O processo web existe permanentemente; jobs e manutenção terminam. O deploy normal usa `.next` + dependências de produção e `server.mjs`; a distribuição standalone duplicada foi retirada.
