This commit is contained in:
2026-03-14 19:45:08 -03:00
parent 9ac83ad823
commit 74adc35b53
23 changed files with 1082 additions and 150 deletions
+134
View File
@@ -0,0 +1,134 @@
# MeowSpool Backend — TODO
> Última atualização: 14/03/2026
---
## Crítico — Funcionalidades Quebradas
### Auth: Email e Reset de Senha
Todos os três handlers abaixo retornam `200 OK` sem executar nenhuma lógica real.
Dependem de infraestrutura compartilhada que ainda não existe (ver "Bloqueadores").
- [ ] **`forgot_password_handler`** (`src/adapters/inbound/auth_handler.rs:131`)
- Gerar token de reset seguro (UUID v4 ou CSPRNG)
- Salvar na tabela `password_reset_tokens` (ver migration abaixo)
- Enviar email com link de reset via serviço de email
- Token deve ter TTL de 1h e ser invalidado após uso
- [ ] **`verify_email_handler`** (`src/adapters/inbound/auth_handler.rs:138`)
- Validar token recebido contra tabela `email_verification_tokens`
- Chamar `user_repo.verify_email(user_id)` (método já implementado no repo)
- Marcar token como usado / deletar após validação
- Retornar `400 Bad Request` para token inválido ou expirado
- [ ] **`reset_password_handler`** (`src/adapters/inbound/auth_handler.rs:143`)
- Validar token recebido contra tabela `password_reset_tokens`
- Verificar TTL e se já foi usado
- Hash da nova senha com Argon2id
- Atualizar `password_hash` via `user_repo.update()`
- Invalidar token após uso
### Bloqueadores das features de Auth acima
- [ ] **Migration: `password_reset_tokens`**
```sql
CREATE TABLE password_reset_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
used_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON password_reset_tokens (token);
```
- [ ] **Migration: `email_verification_tokens`**
```sql
CREATE TABLE email_verification_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
used_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON email_verification_tokens (token);
```
- [ ] **Serviço de email** (`src/infrastructure/email_service.rs` — novo arquivo)
- Adicionar dependência: `lettre` (SMTP) ou integração com API (Resend, SendGrid, Mailgun)
- Adicionar vars de ambiente no `config.rs`: `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `EMAIL_FROM`
- Implementar `send_password_reset_email(to, reset_link)`
- Implementar `send_verification_email(to, code)`
### Perfil de Usuário
- [ ] **`update_me_handler`** (`src/adapters/inbound/user_handler.rs:64`)
- Handler valida formato de email mas não persiste nada
- Implementar chamada a `user_repo.update()` com os campos recebidos
- Considerar campos permitidos: `name` (se adicionado ao domínio), `email`
- Revalidar unicidade de email antes de atualizar
---
## Médio — Segurança
- [ ] **Google OAuth: validar `aud` claim** (`src/adapters/inbound/auth_handler.rs:159`)
- O parâmetro `_client_id` é recebido mas ignorado
- Após obter o payload do tokeninfo do Google, verificar que `aud == GOOGLE_CLIENT_ID`
- Rejeitar com `401 Unauthorized` se o `aud` não corresponder
- Sem isso, qualquer Google token válido de qualquer app é aceito
- [ ] **Logout: invalidar refresh token** (`src/adapters/inbound/auth_handler.rs:121`)
- Atualmente retorna `204 No Content` sem nenhuma ação
- Opção A (simples): tabela `revoked_tokens` no PostgreSQL com TTL via `expires_at`
- Opção B (recomendada): Redis com `SET revoked:{jti} 1 EX <ttl>`
- Middleware de auth deve checar blocklist ao validar tokens
- Adicionar campo `jti` (JWT ID) aos tokens para identificação única
---
## Baixo — Qualidade e Completude
- [ ] **Sort de filamentos não aplicado** (`src/adapters/outbound/postgres_filament_repo.rs`)
- `SortOrder` enum está definido em `ports/filament_repository.rs`
- SQL em `list()` sempre usa `ORDER BY created_at DESC`, ignorando o campo `sort`
- `filament_handler.rs:203` tem `// TODO: parse sort string para SortOrder` — completar o parse
- Aplicar o `SortOrder` no SQL com match: `net_weight_g ASC`, `net_weight_g DESC`, `created_at DESC`
- [ ] **Paginação: retornar total de itens**
- `FilamentRepository::count()` está implementado no repo mas nunca é chamado
- `list_filaments_handler` não retorna metadados de paginação
- Adicionar ao response: `{ data: [...], total: N, page: N, per_page: N }`
- Chamar `repo.count(user_id, filter)` em paralelo com `repo.list()`
- [ ] **`GET /spool-presets/:id` — handler ausente**
- Rota está registrada no `router.rs`
- Nenhum handler correspondente existe em `spool_preset_handler.rs`
- Implementar `get_preset_handler` ou remover a rota do router
- [ ] **Testes**
- Sem nenhum teste unitário ou de integração no projeto
- Prioridade: `AuthService` (hash, JWT), `FilamentService` (cálculos de peso, conflito offline), handlers de auth
- Usar `sqlx::test` para testes de repositório com banco real em transação
---
## Referência Rápida — Arquivos por Área
| Área | Arquivo |
| --------------------- | ------------------------------------------------- |
| Handlers de auth | `src/adapters/inbound/auth_handler.rs` |
| Handler de usuário | `src/adapters/inbound/user_handler.rs` |
| Handler de filamentos | `src/adapters/inbound/filament_handler.rs` |
| Handler de presets | `src/adapters/inbound/spool_preset_handler.rs` |
| Repo de usuário | `src/adapters/outbound/postgres_user_repo.rs` |
| Repo de filamentos | `src/adapters/outbound/postgres_filament_repo.rs` |
| Serviço de auth | `src/application/auth_service.rs` |
| Config / env vars | `src/config.rs` |
| Migrations | `backend/migrations/` |