Documentação técnica
Stack, runtime, modelo de dados, segurança, observabilidade.
Stack
| Camada | Tecnologia | Observações |
|---|---|---|
| Frontend | Vite 5 + React 18 + TypeScript + Tailwind | PWA com service worker (Workbox), instalável em tablet. |
| Roteamento | react-router 6 (BrowserRouter) | Sem hash routes. |
| Estado de servidor | TanStack Query 5 | Cache + revalidação por foco / intervalo. |
| Ícones | lucide-react | Stroke uniforme; sem emojis. |
| Tema | Fluent light + dark opcional | Classe dark aplicada no <html> antes do React montar (sem flicker). |
| API | Cloudflare Workers + Hono 4 | Edge global. Frio típico < 50 ms. |
| Banco | Cloudflare D1 (SQLite serverless) | Schema v3.1 com 32 tabelas + 7 views. Multi-tenant por org_id. |
| Object storage | Cloudflare R2 | Fotos de kit, manuais PDF, laudos. |
| Filas | Cloudflare Queues | Jobs assíncronos: relatórios, manutenção, recall, webhooks ERP. |
| Cache | Cloudflare KV | Rate-limit, telemetria leve. |
| Crons | Workers Triggers | Bowie-Dick diário, BI 6h, manutenção 6h. |
| Validação | Zod (shared) | Schemas únicos worker ↔ web em packages/shared. |
| Auth | JWT HS256 (WebCrypto) + PBKDF2-SHA256 100k | Sem dependência externa. RFID e biometria (WebAuthn placeholder). |
Modelo de dados (resumo)
D1 (SQLite serverless). Prefixo ict_. Toda tabela operacional carrega org_id; isolamento garantido por middleware tenantScope.
Tenancy & identidade
ict_organizations— matriz/filial (org_type,parent_org_id)ict_users— RBAC, lockout por falhasict_user_credentials— RFID hex / biometria (separado por sensibilidade)ict_user_org_access— acesso cruzado a filiaisict_sectors/ict_locations
Catálogo
ict_specialties/ict_packaging_typesict_item_models— modelo (catálogo)ict_items— instância individual (barcode)ict_kit_templates/ict_kit_template_itemsict_kit_instancesict_equipments/ict_equipment_programs
Operação
ict_lifecycle_events— fonte da verdade da FSMict_batches— ciclos de esterilizaçãoict_batch_items— M:N batch ↔ kit/itemict_quality_tests— BD, QI, integradores 5/6ict_biological_indicators— incubação separadaict_quarantine_events/ict_recall_eventsict_surgery_links/ict_consumables_balance
Integrações & logs
ict_audit_log— toda ação sensívelict_erp_webhook_log— outbox + retryict_label_templates— ZPL configurável por orgict_rate_limit— janelas de contagem por IP- Views:
v_current_item_status,v_kit_inventory_fifo,v_ict_bi_ready_to_read,v_ict_items_due_preventive
Segurança
- Autenticação: e-mail/senha (PBKDF2-SHA256, 100k iterações, salt 128 bits) ou RFID HID. Biometria via WebAuthn (placeholder).
- Tokens: JWT HS256, TTL 8 h, claims
sub,role,org_id,home_org_id. - Rotação: secrets do worker (
JWT_SECRET,ERP_WEBHOOK_SIGNING_KEY) gerenciados via Wrangler. Não aparecem no repositório. - Lockout: 5 tentativas erradas em 15 min trava a conta por 15 min.
- Rate-limit: 10 req/min por IP em
/v1/auth/login*; 3 req em 5 min em/v1/auth/bootstrap. - RBAC: middleware
requireRole(...)em todas as rotas de mutação sensível. - Re-auth supervisor: release de quarentena exige reentrada de senha.
- Idempotência: header
Idempotency-Keydedup em KV ou tabela. - Audit log: imutável append-only com
actor_id,action,entity,payload_json,ip,request_id. - CSP: política estrita no
index.htmlda PWA; sem inline scripts (exceto bootstrap de tema). - Webhook ERP: payload assinado com HMAC-SHA256 (
X-Ictus-Signature). - LGPD: minimização (não armazena CPF do paciente, apenas
prontuario_id); criptografia em repouso por padrão na infra Cloudflare; retenção 5 anos com archive R2 cold.
Observabilidade
- Logs estruturados (JSON): cada request loga
{ t, req_id, method, path, status, dur_ms, org_id, user_id }. Compatível com Logpush para BigQuery / Datadog / S3. - Request ID: middleware injeta
X-Request-Idque aparece em logs, audit e resposta de erro. - Métricas: contagem nativa do Cloudflare Workers Analytics + view
v_dashboard_summary. - Health:
GET /healthzretorna{ ok: true, ts }.
Multi-tenant
Uma única database D1, todas as tabelas com org_id. Middleware requireAuth injeta user.org_id em c.var; helpers orgId(c) garantem que toda query carrega WHERE org_id = ?. A hierarquia matriz/filial usa parent_org_id em ict_organizations:
org_type = 'STANDALONE': comportamento default.org_type = 'HQ': matriz; ADMIN pode trocar para qualquer filial sob ela.org_type = 'BRANCH': filial;parent_org_idaponta para HQ.ict_user_org_access: concessões cruzadas (usuário X pode operar filial Y com role Z).
Pipeline de deploy
- Worker:
npx wrangler deploypublica em workers.dev (URL fixa, sem dados sensíveis no repo). - PWA:
npm run build+npx wrangler pages deploy distpublica no Cloudflare Pages. - Docs:
npx wrangler pages deploy docs-sitepublica este site. - Schema: migrações versionadas em
worker/migrations/; aplicadas comwrangler d1 execute --file=.... - Secrets:
wrangler secret put(interativo, nunca via repo). - CI: GitHub Actions roda
tsc --noEmit+npm run buildem PRs.