275 lines
12 KiB
Markdown
275 lines
12 KiB
Markdown
# 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_id` em 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 por `id`.
|
|
- `expandRules` post-pass: itera exceções não consumidas (`handledExIds`), injeta inbound reschedules com `buildOccurrence(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_id` onde null (resultado: 0 órfãos — todos já tinham `patient_id`)
|
|
- `ALTER TABLE agenda_eventos DROP COLUMN paciente_id` executado com sucesso
|
|
|
|
**Código atualizado:**
|
|
- `useAgendaEvents.js`: `paciente_id` removido do `BASE_SELECT`; `create()`/`update()` stripam `paciente_id` do payload
|
|
- `agendaRepository.js`: workaround N+1 de orphan ids removido
|
|
- `agendaMappers.js`: `paciente_id` agora é alias de `patient_id` (UI only)
|
|
- `AgendaTerapeutaPage.vue` + `AgendaClinicaPage.vue`: `pickDbFields` usa `patient_id`
|
|
- `AgendamentosRecebidosPage.vue`: `dbFields` removeu `paciente_id`
|
|
- `PatientsListPage.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.vue`
|
|
- `src/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.vue`
|
|
- `comandos.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 `globalRole` no 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.js` vs `domain/agenda.mappers.js`)
|
|
- [ ] Remover arquivos obsoletos (ConfiguracoesPage-old, Landingpage-v1 bkp, etc.)
|
|
|
|
---
|
|
|
|
## 8. Prioridades de Ação
|
|
|
|
### ✅ Fazer AGORA — todos concluídos
|
|
1. `[x]` ~~Remover dumps SQL~~ → nunca commitados, movidos para fora do repo
|
|
2. `[x]` ~~Corrigir bug `occurrenceCount`~~ → pré-contagem em todos os branches
|
|
3. `[x]` ~~Adicionar `tenant_id` ao `useAgendaEvents` e `loadRules`~~ → injetado em todas as operações
|
|
4. `[x]` ~~Remover `console.log` com dados de pacientes~~ → `supportLogger`
|
|
|
|
### ✅ Fazer em seguida — todos concluídos
|
|
5. `[x]` ~~Corrigir bug remarcação cross-range~~ → 2 queries + post-pass em `expandRules`
|
|
6. `[x]` ~~Consolidar dois `useAgendaEvents`~~ → legado deletado
|
|
7. `[x]` ~~Unificar `paciente_id` + `patient_id`~~ → migration executada + código limpo
|
|
8. `[x]` ~~Remover Sakai de demo~~ → removido + menu SaaS limpo
|
|
|
|
### Backlog
|
|
9. `[x]` Cache de `globalRole` no guard — `globalRoleCacheUid/globalRoleCache` em guards.js
|
|
10. `[ ]` Notificações (WhatsApp/Email via Edge Functions) ← próximo
|
|
11. `[x]` Integração prontuário ↔ sessões — seção "Sessões" em `PatientProntuario.vue`
|
|
12. `[x]` Relatórios básicos — `RelatoriosPage.vue` em /therapist/relatorios
|
|
13. `[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 `occurrenceCount` corrigido (itens 2 e 4)
|
|
- `tenant_id` adicionado ao `useAgendaEvents` e `loadRules` (item 3)
|
|
- `console.*` substituídos por `supportLogger`
|
|
|
|
### Sessão 4 — 2026-03-11
|
|
- Cache `globalRole` adicionado 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.vue` criada em `/therapist/relatorios` (item 12) — cards, gráfico Chart.js, tabela DataTable
|
|
- Consolidação mappers (item 13) — `domain/agenda.mappers.js` vazio deletado + `agenda.types.js` + dir `domain/`
|
|
- `ConfiguracoesPage-old.vue` deletado (limpeza)
|
|
|
|
### Sessão 3 — 2026-03-11
|
|
- Bug remarcação cross-range resolvido (item 5)
|
|
- `logPerf is not defined` em guards.js corrigido
|
|
- `pgcrypto` → substituído por `gen_random_uuid()` duplo no support_sessions
|
|
- Sakai demo removido completamente (item 8) + `styles.scss` corrigido
|
|
- `useAgendaEvents` legado deletado (item 6)
|
|
- `paciente_id` unificado em `patient_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.*
|