Files
eden/apps/api/src/app.module.ts
Matheus (Handix) 20b114a0bf feat(signatures): módulo completo de assinatura eletrônica
Porta o motor de assinatura do legado com fidelidade algorítmica
exata — hash-chain (SHA256 encadeado, seq bigserial define a ordem,
não timestamp), OTP (6 dígitos via crypto.randomInt, HMAC-SHA256 com
SIGNATURE_OTP_SECRET, TTL 5min, 5 tentativas, único ativo por vez) e
a máquina de estados de 19 estados do envelope + 8 do signatário —
tudo re-verificado contra o código-fonte real do legado por um
subagente dedicado antes de qualquer linha ser escrita, cruzado com
eden.md módulo 4.

5 tabelas novas (signature_envelopes/signers/documents/
otp_challenges/audit_events, esta última append-only via a mesma GUC
eden.allow_audit_mutation que já protege audit_log desde a Fase 1 —
canonicalização de hash extraída para packages/database/src/
hash-chain.ts, compartilhada entre as duas). Três adaptações
deliberadas ao EDEN (documentadas na migration): documentos vêm de
document_generations em vez de um módulo de anexo genérico que não
existe ainda; reseller_id em vez de reseller_registration_id; só a
versão correta (v2) da canonicalização, sem a v1 do legado (existia
lá só para tolerar hashes pré-correção de bug, EDEN nunca teve isso).

Certificado de conclusão renderizado no servidor via Chromium (não
client-side como no legado) — elimina o upload multipart do
certificado pronto. Os dois passos autenticados de finalização
(seal-checkpoint, finalize) continuam explícitos e manuais, não
automáticos, mesmo sendo tecnicamente possível agora — esse
checkpoint humano antes de uma finalização irreversível é
comportamento de negócio herdado do legado, não um detalhe do
desenho client-side do certificado que deixou de existir.

Fluxo público (token do link, nunca envelopeId/signerId do corpo da
requisição) e verificação pública (só para COMPLETED, recomputa
envelope_hash ao vivo, nomes mascarados) inteiros.

Verificado: 28 testes novos (15 puros — hash-chain/máquina de
estados/OTP/token — e 13 de ciclo de vida completo via HTTP real,
incluindo merge de PDF de verdade via pdf-lib contra saída real do
Chromium mockado). Suíte completa: 70/70 verdes. Testado ao vivo
através do container real, ponta a ponta, sem nenhum mock: criação →
convite → fluxo público completo dos dois signatários (OTP
recuperado via HMAC com o segredo real, já que SMTP não está
configurado neste ambiente) → SIGNED → seal-checkpoint → finalize
(certificado real via Chromium, merge real, upload real no MinIO) →
public-verify — SHA-256 do PDF final baixado bate byte a byte com o
gravado no Postgres, integrityValid:true.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
2026-09-04 13:44:43 -03:00

52 lines
1.9 KiB
TypeScript

import { Module } from "@nestjs/common";
import { TerminusModule } from "@nestjs/terminus";
import { HealthController } from "./health/health.controller.js";
import { ConfigModule } from "./config/config.module.js";
import { AuthModule } from "./auth/auth.module.js";
import { BootstrapModule } from "./bootstrap/bootstrap.module.js";
import { CompaniesModule } from "./companies/companies.module.js";
import { UsersModule } from "./users/users.module.js";
import { RolesModule } from "./roles/roles.module.js";
import { ProductsModule } from "./products/products.module.js";
import { ResellersModule } from "./resellers/resellers.module.js";
import { LeadsModule } from "./leads/leads.module.js";
import { OpportunitiesModule } from "./opportunities/opportunities.module.js";
import { QuotesModule } from "./quotes/quotes.module.js";
import { ClientRegistrationsModule } from "./client-registrations/client-registrations.module.js";
import { ContractsModule } from "./contracts/contracts.module.js";
import { InventoryModule } from "./inventory/inventory.module.js";
import { SupportModule } from "./support/support.module.js";
import { HrTimeclockModule } from "./hr-timeclock/hr-timeclock.module.js";
import { FiscalModule } from "./fiscal/fiscal.module.js";
import { BillingModule } from "./billing/billing.module.js";
import { DocumentsModule } from "./documents/documents.module.js";
import { SignaturesModule } from "./signatures/signatures.module.js";
@Module({
imports: [
ConfigModule,
TerminusModule,
AuthModule,
BootstrapModule,
CompaniesModule,
UsersModule,
RolesModule,
ProductsModule,
ResellersModule,
LeadsModule,
OpportunitiesModule,
QuotesModule,
ClientRegistrationsModule,
ContractsModule,
InventoryModule,
SupportModule,
HrTimeclockModule,
FiscalModule,
BillingModule,
DocumentsModule,
SignaturesModule,
],
controllers: [HealthController],
})
export class AppModule {}