12 KiB
Auditoria Técnica — AgenciaPsi MVP
Data: 2026-03-11 Stack: Vue 3 · PrimeVue · Supabase · PostgreSQL · FullCalendar Modelo: claude-sonnet-4-6
1. Visão Geral Arquitetural
Pontos fortes:
- Estrutura feature-based bem definida (
features/agenda,features/patients) - Separação correta: repository → composable → page
- Multi-tenancy é first-class:
tenant_idem todos os queries críticos - Sistema de guards robusto: RBAC + entitlements + tenantFeatures + session race-condition handling
useRecurrenceé bem arquitetado: virtual occurrences no frontend + exceções no banco (sem N linhas futuras)
2. Bugs Críticos
✅ [RESOLVIDO] useRecurrence.js — variável occurrenceCount não declarada
Bug original: branches custom_weekdays, monthly e yearly usavam occurrenceCount sem declará-la → ReferenceError em runtime. Nenhum dos três contava ocorrências anteriores ao range, então max_occurrences nunca funcionava corretamente.
Correção (Sessão 2): Cada branch ganhou let occurrenceCount = 0 + fase de pré-contagem de ruleStart até effStart.
Resolvido em: Sessão 2 — 2026-03-11
✅ [RESOLVIDO] Exceção de remarcação fora do range não aparece
Bug original: loadExceptions só buscava original_date no range. Se original_date estivesse fora mas new_date caísse dentro, a sessão remarcada não aparecia.
Correção (Sessão 3):
loadExceptions: duas queries paralelas —q1(original_date no range) +q2(reschedule com new_date no range). Mescladas e deduplicadas porid.expandRulespost-pass: itera exceções não consumidas (handledExIds), injeta inbound reschedules combuildOccurrence(rule, newDate, ex.original_date, ex).
Resolvido em: Sessão 3 — 2026-03-11
3. Segurança
✅ [RESOLVIDO] SQL dumps no repositório
Arquivos: schema.sql, backup.sql, data_dump.sql, full_dump.sql
Verificado via git log --all --full-history — arquivos nunca foram commitados. Movidos para pasta externa ao repositório. Nenhuma purga de histórico necessária.
Resolvido em: Sessão 3 — 2026-03-11
✅ [RESOLVIDO] useAgendaEvents — sem tenant_id em nenhuma operação
Correção (Sessão 2): tenant_id injetado em create(), loadMyRange(), update(), remove(), removeSeriesFrom(), removeAllSeries(). Helpers assertTenantId() e getUid() adicionados.
Resolvido em: Sessão 2 — 2026-03-11
✅ [RESOLVIDO] loadRules em useRecurrence sem filtro tenant_id
Correção (Sessão 2): loadRules e loadAndExpand aceitam tenantId opcional e aplicam .eq('tenant_id', tenantId). Call site em AgendaTerapeutaPage._reloadRange passa tenantStore.activeTenantId.
Resolvido em: Sessão 2 — 2026-03-11
✅ [RESOLVIDO] console.log expõe dados de pacientes no browser
Correção (Sessão 2): Todos os console.* substituídos pelo supportLogger. Logs só aparecem quando modo suporte está ativo (token válido no banco).
Resolvido em: Sessão 2 — 2026-03-11
Arquivos criados: src/support/supportLogger.js, src/support/supportDebugStore.js
🟡 [ABERTO] window.__guardsBound / window.__supabaseAuthListenerBound
Usar window.* para controle de listeners é frágil em hot-reload.
Solução: Gerenciar via módulo singleton ou app.config.globalProperties.
✅ [RESOLVIDO] globalRole do profiles sem cache no guard
Adicionados globalRoleCacheUid + globalRoleCache no guards.js.
Cache invalida em: uid change, SIGNED_OUT, SIGNED_IN com user diferente.
Query ao banco ocorre apenas na primeira navegação por sessão.
Resolvido em: Sessão 4 — 2026-03-11
4. Duplicações e Inconsistências
✅ [RESOLVIDO] Dois composables para a mesma entidade
src/composables/useAgendaEvents.js era código morto (sem imports). Deletado.
O autoritativo src/features/agenda/composables/useAgendaEvents.js permanece.
Resolvido em: Sessão 3 — 2026-03-11
✅ [RESOLVIDO] Dois mappers para agenda
src/features/agenda/domain/agenda.mappers.js estava vazio e sem imports. Deletado.
src/features/agenda/domain/agenda.types.js também sem imports. Deletado. Diretório domain/ removido.
O autoritativo src/features/agenda/services/agendaMappers.js permanece.
Resolvido em: Sessão 4 — 2026-03-11
✅ [RESOLVIDO] N+1 Query — migração paciente_id → patient_id
Resolvido em: Sessão 3 — 2026-03-11
Migration executada: migrations/unify_patient_id.sql
- UPDATE copiou
paciente_id→patient_idonde null (resultado: 0 órfãos — todos já tinhampatient_id) ALTER TABLE agenda_eventos DROP COLUMN paciente_idexecutado com sucesso
Código atualizado:
useAgendaEvents.js:paciente_idremovido doBASE_SELECT;create()/update()stripampaciente_iddo payloadagendaRepository.js: workaround N+1 de orphan ids removidoagendaMappers.js:paciente_idagora é alias depatient_id(UI only)AgendaTerapeutaPage.vue+AgendaClinicaPage.vue:pickDbFieldsusapatient_idAgendamentosRecebidosPage.vue:dbFieldsremoveupaciente_idPatientsListPage.vue+AgendaEventDialog.vue:.or()→.eq('patient_id', id)
5. Limpeza Necessária
✅ [RESOLVIDO] Template Sakai removido — bundle de produção
Resolvido em: Sessão 3 — 2026-03-11
Removidos: src/views/uikit/ (15 arquivos), src/views/utilities/Blocks.vue, src/components/BlockViewer.vue, src/components/FloatingConfigurator.vue, src/views/pages/Documentation.vue, src/assets/demo/, src/navigation/menus/sakai.demo.menu.js, src/router/routes.demo.js, src/assets/styles.scss (@use demo removido)
Referências limpas: package.json renomeado para agenciapsi, demoRoutes e sakaiDemoMenu removidos dos index files, FloatingConfigurator removido de Login, NotFound, Access, Error, ResetPasswordPage.
🟡 [PARCIAL] Arquivos obsoletos no projeto
Deletados (Sessão 4):
src/layout/ConfiguracoesPage-old.vuesrc/features/agenda/domain/(diretório inteiro — 2 arquivos não usados)
Ainda presentes:
src/layout/ConfiguracoesPage - Copia.vue— verificar se está no git (staged como D)src/views/pages/public/Landingpage-v1 - bkp.vuecomandos.txt(na raiz)
✅ [RESOLVIDO] Logs excessivos em produção
console.time/timeLog/timeEnd/warn/error em guards.js substituídos por logGuard(), logError(), logPerf().
Resolvido em: Sessão 2 — 2026-03-11
6. Status das Features do MVP
| Feature | Status | Observação |
|---|---|---|
| Agenda de sessões | ✅ Implementado | FullCalendar + composables |
| Cadastro de pacientes | ✅ Implementado | CRUD completo |
| Recorrência de sessões | ✅ Corrigido | Bugs de occurrenceCount e cross-range resolvidos |
| Sessões presenciais/online | ✅ Implementado | campo modalidade |
| Controle de faltas | ✅ Implementado | exception_type = 'patient_missed' |
| Remarcação | ✅ Corrigido | Bug cross-range resolvido (Sessão 3) |
| Bloqueio de agenda | ✅ Implementado | BloqueioDialog.vue |
| Agendamento online | ✅ Implementado | AgendadorPublicoPage.vue |
| Prontuário | ✅ Integrado | Seção "Sessões" adicionada ao PatientProntuario.vue |
| Notificações/lembretes | ❌ Não implementado | Sem trigger/edge function |
| Financeiro/faturamento | ⚠️ Parcial | Páginas de plano mas sem sessão→pagamento |
| Relatórios | ✅ Implementado | RelatoriosPage.vue — terapeuta — sessões, faltas, taxa, gráfico |
7. Backlog Técnico
- Cache de
globalRoleno guard (reduzir queries por navegação) - Implementar notificações: WhatsApp/Email via Supabase Edge Functions
- Integração prontuário ↔ sessões
- Integração sessão ↔ pagamento (financeiro)
- Relatórios básicos: sessões realizadas, faltas, receita
- Migrar domínio de recorrência para TypeScript
- Consolidar dois mappers de agenda (
agendaMappers.jsvsdomain/agenda.mappers.js) - Remover arquivos obsoletos (ConfiguracoesPage-old, Landingpage-v1 bkp, etc.)
8. Prioridades de Ação
✅ Fazer AGORA — todos concluídos
[x]Remover dumps SQL→ nunca commitados, movidos para fora do repo[x]Corrigir bug→ pré-contagem em todos os branchesoccurrenceCount[x]Adicionar→ injetado em todas as operaçõestenant_idaouseAgendaEventseloadRules[x]Remover→console.logcom dados de pacientessupportLogger
✅ Fazer em seguida — todos concluídos
[x]Corrigir bug remarcação cross-range→ 2 queries + post-pass emexpandRules[x]Consolidar dois→ legado deletadouseAgendaEvents[x]Unificar→ migration executada + código limpopaciente_id+patient_id[x]Remover Sakai de demo→ removido + menu SaaS limpo
Backlog
[x]Cache deglobalRoleno guard —globalRoleCacheUid/globalRoleCacheem guards.js[ ]Notificações (WhatsApp/Email via Edge Functions) ← próximo[x]Integração prontuário ↔ sessões — seção "Sessões" emPatientProntuario.vue[x]Relatórios básicos —RelatoriosPage.vueem /therapist/relatorios[x]Consolidar mappers de agenda —domain/deletado,agendaMappers.jsé único
9. Sistema de Suporte Técnico SaaS
Sistema seguro para admins SaaS acessarem a agenda de terapeutas em modo debug.
| Arquivo | Responsabilidade |
|---|---|
migrations/support_sessions.sql |
Tabela, índices, RLS, RPCs (token gerado via gen_random_uuid() duplo — sem pgcrypto) |
src/support/supportLogger.js |
Logger centralizado — silencioso fora do modo suporte |
src/support/supportDebugStore.js |
Store Pinia — valida token via RPC validate_support_session |
src/support/supportSessionService.js |
CRUD de sessões de suporte (criar/listar/revogar) |
src/support/components/SupportDebugBanner.vue |
Banner fixo na agenda com painel de logs filtráveis |
src/views/pages/saas/SaasSupportPage.vue |
Painel SaaS para gerenciar sessões de suporte |
RPCs no banco:
create_support_session(p_tenant_id, p_ttl_minutes)→{ token, expires_at, session_id }validate_support_session(p_token)→{ valid, tenant_id }revoke_support_session(p_token)→boolean
10. Histórico de Sessões
Sessão 1 — 2026-03-11
- Auditoria técnica completa gerada
- Nenhum item resolvido
Sessão 2 — 2026-03-11
- Sistema de suporte técnico SaaS implementado (migration + 5 arquivos criados)
- Bug
occurrenceCountcorrigido (itens 2 e 4) tenant_idadicionado aouseAgendaEventseloadRules(item 3)console.*substituídos porsupportLogger
Sessão 4 — 2026-03-11
- Cache
globalRoleadicionado ao guard (item 9) — sem query ao banco por navegação - Integração prontuário ↔ sessões (item 11) — painel "Sessões" em
PatientProntuario.vue RelatoriosPage.vuecriada em/therapist/relatorios(item 12) — cards, gráfico Chart.js, tabela DataTable- Consolidação mappers (item 13) —
domain/agenda.mappers.jsvazio deletado +agenda.types.js+ dirdomain/ ConfiguracoesPage-old.vuedeletado (limpeza)
Sessão 3 — 2026-03-11
- Bug remarcação cross-range resolvido (item 5)
logPerf is not definedem guards.js corrigidopgcrypto→ substituído porgen_random_uuid()duplo no support_sessions- Sakai demo removido completamente (item 8) +
styles.scsscorrigido useAgendaEventslegado deletado (item 6)paciente_idunificado empatient_id— migration executada (item 7)- SQL dumps confirmados como nunca commitados (item 1 encerrado)
Para retomar: devolva este arquivo ao início da conversa e indique qual item quer atacar.