- Add reminders page with navigation support - Upgrade BMad builder module to skills-based architecture - Refactor MCP server: extract tools and auth into separate modules - Add connections cache, custom AI provider support - Update prisma schema and generated client - Various UI/UX improvements and i18n updates - Add service worker for PWA support Made-with: Cursor
497 lines
14 KiB
Markdown
497 lines
14 KiB
Markdown
---
|
|
title: 'Revue de code complète du projet Keep'
|
|
slug: 'code-review-keep'
|
|
created: '2026-02-15'
|
|
status: 'completed'
|
|
stepsCompleted: [1, 2, 3, 4, 5, 6]
|
|
tech_stack: ['Next.js 16.1.1', 'React 19.2.3', 'TypeScript 5.x', 'Prisma 5.22.0', 'SQLite', 'NextAuth 5.0.0-beta.30']
|
|
files_to_modify: [
|
|
'app/api/notes/route.ts',
|
|
'app/api/notes/[id]/route.ts',
|
|
'app/api/notebooks/route.ts',
|
|
'app/api/notebooks/[id]/route.ts',
|
|
'app/api/labels/route.ts',
|
|
'app/api/labels/[id]/route.ts',
|
|
'lib/utils.ts',
|
|
'app/actions/notes.ts',
|
|
'components/note-card.tsx',
|
|
'hooks/useUndoRedo.ts',
|
|
'lib/types.ts'
|
|
]
|
|
code_patterns: ['Server Actions avec auth()', 'Client Components avec use client', 'Prisma ORM', 'Zod validation (partiel)']
|
|
test_patterns: ['Playwright E2E', 'Vitest']
|
|
---
|
|
|
|
# Tech-Spec: Revue de code complète du projet Keep
|
|
|
|
**Created:** 2026-02-15
|
|
|
|
## Overview
|
|
|
|
### Problem Statement
|
|
|
|
Le client demande une revue complète du code source du projet Keep (application Next.js de prise de notes avec fonctionnalités AI) pour identifier:
|
|
- Les bugs existants
|
|
- Les problèmes de qualité de code
|
|
- Les anti-patterns
|
|
- Les problèmes de sécurité
|
|
- Les problèmes de performance
|
|
|
|
### Solution
|
|
|
|
Effectuer un audit technique complet du code source, analyser chaque composant/service/route, et fournir un plan d'action détaillé avec priorités.
|
|
|
|
### Scope
|
|
|
|
**In Scope:**
|
|
- Analyse de `keep-notes/` (code source principal)
|
|
- Revue des composants React
|
|
- Revue des API routes et Server Actions
|
|
- Revue des services et hooks
|
|
- Revue du schéma Prisma
|
|
- Revue des patterns de sécurité
|
|
|
|
**Out of Scope:**
|
|
- Revue des tests E2E (sauf si bugs trouvés)
|
|
- Refactoring du code
|
|
- Corrections directes (juste analyse)
|
|
|
|
---
|
|
|
|
## Context for Development
|
|
|
|
### Investigation Results
|
|
|
|
**Fichiers analysés:**
|
|
- `prisma/schema.prisma` (241 lignes)
|
|
- `app/actions/notes.ts` (1358 lignes)
|
|
- `app/api/notes/route.ts` (163 lignes)
|
|
- `app/actions/auth.ts` (30 lignes)
|
|
- `auth.ts` (54 lignes)
|
|
- `lib/types.ts` (208 lignes)
|
|
- `lib/utils.ts` (200+ lignes)
|
|
- `components/note-card.tsx` (658 lignes)
|
|
- `hooks/useUndoRedo.ts` (116 lignes)
|
|
|
|
### Codebase Patterns
|
|
|
|
Le projet suit partiellement les règles de `project-context.md`:
|
|
- ✅ Server Actions avec `'use server'`
|
|
- ✅ Client Components avec `'use client'`
|
|
- ✅ Import via alias `@/`
|
|
- ✅ Prisma ORM
|
|
- ⚠️ Zod validation (partiellement utilisé)
|
|
- ❌ Authentication manquante dans les API routes
|
|
|
|
---
|
|
|
|
## Bugs et Problèmes Identifiés
|
|
|
|
### 🔴 CRITIQUES (Sécurité)
|
|
|
|
#### 1. API Routes sans authentication
|
|
**Fichier:** `app/api/notes/route.ts` et autres API routes
|
|
**Problème:** Les routes API n'ont PAS de vérification d'authentification. N'importe quel utilisateur peut:
|
|
- Lire toutes les notes
|
|
- Créer des notes
|
|
- Modifier n'importe quelle note
|
|
- Supprimer n'importe quelle note
|
|
|
|
```typescript
|
|
// ❌ MAUVAIS - Pas de vérification utilisateur
|
|
export async function GET(request: NextRequest) {
|
|
const notes = await prisma.note.findMany({...}) // Tout le monde peut accéder!
|
|
}
|
|
```
|
|
|
|
**Impact:** Vulnérabilité critique - données exposées publiquement
|
|
|
|
---
|
|
|
|
#### 2. API Routes sans userId filter
|
|
**Fichier:** `app/api/notes/[id]/route.ts`
|
|
**Problème:** Les opérations PUT/DELETE ne vérifient pas que l'utilisateur est propriétaire de la note
|
|
|
|
---
|
|
|
|
### 🟠 HAUTS (Bugs fonctionnels)
|
|
|
|
#### 3. Duplicate code - parseNote function
|
|
**Fichiers:**
|
|
- `app/actions/notes.ts` (ligne 13-45)
|
|
- `app/api/notes/route.ts` (ligne 6-13)
|
|
|
|
**Problème:** La fonction `parseNote` est dupliquée dans les Server Actions et les API Routes. Devrait être dans `lib/utils.ts`
|
|
|
|
---
|
|
|
|
#### 4. syncLabels - Performance N+1 queries
|
|
**Fichier:** `app/actions/notes.ts` (ligne 58-146)
|
|
|
|
**Problème:** La fonction `syncLabels` fait BEAUCOUP de requêtes Prisma individuelles:
|
|
- `findMany` pour les labels existants
|
|
- `findMany` pour toutes les notes
|
|
- `findMany` pour tous les labels
|
|
- `create` pour chaque nouveau label (boucle)
|
|
- `delete` pour chaque orphan (boucle)
|
|
|
|
```typescript
|
|
// ❌ Problème: 100+ requêtes pour 50 labels
|
|
for (const labelName of noteLabels) {
|
|
await prisma.label.create({...}) // 1 requête par label
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### 5. Duplicate Label Colors dans types.ts
|
|
**Fichier:** `lib/types.ts` (lignes 87-142)
|
|
|
|
**Problème:** Les couleurs sont définies TWO TIMES:
|
|
- `LABEL_COLORS` (lignes 87-142)
|
|
- `NOTE_COLORS` (lignes 146-197)
|
|
|
|
Devrait être centralisé dans un fichier séparé.
|
|
|
|
---
|
|
|
|
#### 6. Redundant imports - date-fns/locale
|
|
**Fichier:** `components/note-card.tsx` (ligne 20)
|
|
|
|
```typescript
|
|
import * as dateFnsLocales from 'date-fns/locale' // Import tout le module!
|
|
```
|
|
|
|
**Problème:** Importe TOUS les locales au lieu de Only needed ones. Impact bundle size.
|
|
|
|
---
|
|
|
|
#### 7. Error handling - No specific error messages
|
|
**Fichier:** `app/api/notes/route.ts`
|
|
|
|
**Problème:** Les erreurs sont catchées mais pas loguées:
|
|
```typescript
|
|
catch (error) { // ❌ Pas de console.error!
|
|
return NextResponse.json(
|
|
{ success: false, error: 'Failed to fetch notes' },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### 8. useUndoRedo - Memory leak potential
|
|
**Fichier:** `hooks/useUndoRedo.ts`
|
|
|
|
**Problème:** Le `useEffect` avec JSON.stringify pour comparer les états peut être lent et cause des re-renders:
|
|
```typescript
|
|
if (JSON.stringify(resolvedNewState) === JSON.stringify(currentHistory.present)) {
|
|
// Comparaison lente pour gros objets
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### 9. Missing revalidatePath after note creation
|
|
**Fichier:** `app/actions/notes.ts` (ligne 404)
|
|
|
|
**Problème:** Après `createNote`, revalidatePath est appelé mais pas après certaines mutations.
|
|
|
|
---
|
|
|
|
#### 10. NoteCard - Missing useEffect cleanup
|
|
**Fichier:** `components/note-card.tsx` (lignes 174-192)
|
|
|
|
**Problème:** useEffect sans fonction cleanup peut causer des memory leaks si le composant est démonté pendant le chargement.
|
|
|
|
---
|
|
|
|
### 🟡 MOYENS (Code Quality)
|
|
|
|
#### 11. Inconsistent error throwing
|
|
**Fichiers:** Plusieurs
|
|
|
|
**Problème:** Certains utilisent `throw new Error('message')`, d'autres `throw error`直接
|
|
|
|
---
|
|
|
|
#### 12. Type: any usage
|
|
**Fichier:** `app/actions/notes.ts`
|
|
|
|
**Problème:** Plusieurs `any` types:
|
|
- Ligne 13: `function parseNote(dbNote: any)`
|
|
- Ligne 441: `const updateData: any`
|
|
- Ligne 22: `where: any`
|
|
|
|
---
|
|
|
|
#### 13. Unused variables
|
|
**Fichier:** `components/note-card.tsx`
|
|
|
|
```typescript
|
|
const [isPending, startTransition] = useTransition() // isPending jamais utilisé!
|
|
```
|
|
|
|
---
|
|
|
|
#### 14. Hardcoded strings everywhere
|
|
**Problème:** Pas de fichier de constants centralisé pour les couleurs, tailles, etc.
|
|
|
|
---
|
|
|
|
#### 15. Missing Zod validation in API routes
|
|
**Fichier:** `app/api/notes/route.ts`
|
|
|
|
**Problème:** Les données request ne sont pas validées avec Zod:
|
|
```typescript
|
|
const body = await request.json()
|
|
const { title, content, color } = body // Pas de validation!
|
|
```
|
|
|
|
---
|
|
|
|
### 🟢 BAS (Minor)
|
|
|
|
#### 16. Console.log vs console.error
|
|
**Problème:** Mélange de `console.log` et `console.error`. Devrait utiliser `console.error` pour les erreurs.
|
|
|
|
---
|
|
|
|
#### 17. Commented code
|
|
**Fichier:** `components/note-card.tsx`
|
|
|
|
Code commenté un peu partout (lignes 404-405, etc.)
|
|
|
|
---
|
|
|
|
#### 18. Inconsistent naming
|
|
- `getAllNotes` vs `getNotes`
|
|
- `updateNote` vs `updateColor` (pas cohérent)
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### Tasks
|
|
|
|
#### 🔴 TÂCHE 1: Ajouter authentication aux API Routes (CRITIQUE)
|
|
- File: `app/api/notes/route.ts`
|
|
- File: `app/api/notes/[id]/route.ts`
|
|
- File: `app/api/notebooks/route.ts`
|
|
- File: `app/api/notebooks/[id]/route.ts`
|
|
- File: `app/api/labels/route.ts`
|
|
- File: `app/api/labels/[id]/route.ts`
|
|
- Action: Ajouter `import { auth } from '@/auth'` et vérifier `const session = await auth()` au début de chaque handler
|
|
- Notes: Suivre le pattern utilisé dans `app/actions/notes.ts`
|
|
|
|
---
|
|
|
|
#### 🔴 TÂCHE 2: Ajouter userId filter aux opérations (CRITIQUE)
|
|
- File: `app/api/notes/[id]/route.ts`
|
|
- Action: Modifier les WHERE clauses pour inclure `userId: session.user.id`
|
|
- Notes: Vérifier ownership avant UPDATE/DELETE
|
|
|
|
---
|
|
|
|
#### 🟠 TÂCHE 3: Extraire parseNote vers lib/utils.ts
|
|
- File: `lib/utils.ts`
|
|
- File: `app/actions/notes.ts` (supprimer fonction locale)
|
|
- File: `app/api/notes/route.ts` (utiliser import)
|
|
- Action: Créer fonction `parseNote(dbNote: Prisma.NoteGetPayload<null>)` dans utils et exporter
|
|
- Notes: Importer depuis `lib/utils.ts`
|
|
|
|
---
|
|
|
|
#### 🟠 TÂCHE 4: Optimiser syncLabels avec createMany/deleteMany
|
|
- File: `app/actions/notes.ts`
|
|
- Action: Remplacer les boucles `for...await` par:
|
|
```typescript
|
|
await prisma.label.createMany({
|
|
data: labelsToCreate,
|
|
skipDuplicates: true
|
|
})
|
|
```
|
|
- Notes: Réduire de ~100 requêtes à ~3-5 requêtes
|
|
|
|
---
|
|
|
|
#### 🟠 TÂCHE 5: Optimiser imports date-fns
|
|
- File: `components/note-card.tsx`
|
|
- Action: Remplacer `import * as dateFnsLocales` par imports nommés:
|
|
```typescript
|
|
import enUS from 'date-fns/locale/en-US'
|
|
import fr from 'date-fns/locale/fr'
|
|
```
|
|
- Notes: Garder seulement les locales utilisées
|
|
|
|
---
|
|
|
|
#### 🟠 TÂCHE 6: Ajouter error logging aux API routes
|
|
- File: `app/api/notes/route.ts`
|
|
- Action: Ajouter `console.error('Error message:', error)` dans chaque catch block
|
|
- Notes: Suivre pattern de `app/actions/notes.ts`
|
|
|
|
---
|
|
|
|
#### 🟠 TÂCHE 7: Améliorer useUndoRedo comparison
|
|
- File: `hooks/useUndoRedo.ts`
|
|
- Action: Remplacer `JSON.stringify` par une fonction de deep comparison légère (ex: `fast-deep-equal` ou implémentation personnalisée)
|
|
- Notes: Tester performance avec de grosses notes
|
|
|
|
---
|
|
|
|
#### 🟡 TÂCHE 8: Créer fichier constants/colors.ts
|
|
- File: `lib/constants/colors.ts` (nouveau fichier)
|
|
- Action: Extraire LABEL_COLORS et NOTE_COLORS vers fichier centralisé
|
|
- Notes: Importer depuis `lib/types.ts`
|
|
|
|
---
|
|
|
|
#### 🟡 TÂCHE 9: Remplacer types any par types stricts
|
|
- File: `app/actions/notes.ts`
|
|
- Action: Remplacer `any` par types appropriés:
|
|
- `dbNote: any` → `dbNote: Note` (après parsing)
|
|
- `updateData: any` → `updateData: Prisma.NoteUpdateInput`
|
|
- `where: any` → `where: Prisma.NoteWhereInput`
|
|
- Notes: Importer types depuis `@prisma/client`
|
|
|
|
---
|
|
|
|
#### 🟡 TÂCHE 10: Ajouter Zod validation aux API routes
|
|
- File: `app/api/notes/route.ts`
|
|
- Action: Créer et utiliser Zod schemas:
|
|
```typescript
|
|
const createNoteSchema = z.object({
|
|
title: z.string().optional(),
|
|
content: z.string().min(1),
|
|
color: z.string().optional(),
|
|
// ...
|
|
})
|
|
```
|
|
- Notes: Suivre pattern dans `app/api/ai/*` routes
|
|
|
|
---
|
|
|
|
#### 🟡 TÂCHE 11: Ajouter useEffect cleanup
|
|
- File: `components/note-card.tsx`
|
|
- Action: Ajouter AbortController pour le fetch des collaborators:
|
|
```typescript
|
|
useEffect(() => {
|
|
let isMounted = true
|
|
const loadCollaborators = async () => {...}
|
|
loadCollaborators()
|
|
return () => { isMounted = false }
|
|
}, [note.id])
|
|
```
|
|
|
|
---
|
|
|
|
#### 🟢 TÂCHE 12: Nettoyer code
|
|
- File: `components/note-card.tsx`
|
|
- Action:
|
|
- Supprimer variable `isPending` inutilisée
|
|
- Supprimer code commenté
|
|
- Ajouter `_` prefix pour unused params
|
|
|
|
---
|
|
|
|
### Acceptance Criteria
|
|
|
|
#### AC1: Authentication
|
|
- [ ] AC1.1: Given un utilisateur non-authentifié, when il fait GET /api/notes, then il reçoit 401 Unauthorized
|
|
- [ ] AC1.2: Given un utilisateur authentifié, when il fait GET /api/notes, then il reçoit seulement SES notes
|
|
|
|
#### AC2: Ownership
|
|
- [ ] AC2.1: Given un utilisateur A, when il essaie de PUT /api/notes/[id] d'une note appartenant à B, then il reçoit 403 Forbidden
|
|
- [ ] AC2.2: Given un utilisateur A, when il essaie de DELETE /api/notes/[id] d'une note appartenant à B, then il reçoit 403 Forbidden
|
|
|
|
#### AC3: Code Quality
|
|
- [ ] AC3.1: Given le projet, when on cherche "function parseNote", then il n'existe qu'en UN seul endroit (lib/utils.ts)
|
|
- [ ] AC3.2: Given une API route, when elle catch une erreur, then elle log l'erreur avec console.error
|
|
|
|
#### AC4: Performance
|
|
- [ ] AC4.1: Given 50 labels à sync, when syncLabels est appelé, then moins de 10 requêtes DB sont exécutées
|
|
- [ ] AC4.2: Given le bundle, when on mesure la taille, then date-fns/locale n'est pas importé en entier
|
|
|
|
#### AC5: Types
|
|
- [ ] AC5.1: Given le fichier app/actions/notes.ts, when on cherche ": any", then aucun résultat n'est trouvé
|
|
- [ ] AC5.2: Given une API route POST, when le body est invalide, then elle retourne 400 avec les erreurs de validation
|
|
|
|
---
|
|
|
|
## Additional Context
|
|
|
|
### Dependencies
|
|
|
|
**Existantes:**
|
|
- Next.js 16.1.1
|
|
- React 19.2.3
|
|
- TypeScript 5.x
|
|
- Prisma 5.22.0
|
|
- SQLite (better-sqlite3)
|
|
- NextAuth 5.0.0-beta.30
|
|
- Zod (déjà utilisé partiellement)
|
|
|
|
**Nouvelles à installer (si pas présentes):**
|
|
- `fast-deep-equal` - pour comparaison d'objets légère
|
|
|
|
### Testing Strategy
|
|
|
|
**Tests unitaires à ajouter:**
|
|
- Tests pour parseNote avec différents formats de données
|
|
- Tests pour syncLabels avec mock Prisma
|
|
- Tests de validation Zod schemas
|
|
|
|
**Tests d'intégration:**
|
|
- Tester que les API routes retournent 401 sans auth
|
|
- Tester ownership avec deux utilisateurs différents
|
|
|
|
**Tests manuels:**
|
|
1. Créer un utilisateur A, créer une note
|
|
2. Créer un utilisateur B (autre session)
|
|
3. Vérifier que B ne peut pas accéder aux notes de A
|
|
4. Tester toutes les opérations CRUD
|
|
|
|
### Notes
|
|
|
|
**Limitations:**
|
|
- Cette revue ne couvre pas les API routes AI (peuvent avoir leurs propres problèmes)
|
|
- Les tests E2E existants n'ont pas été exécutés pendant cette revue
|
|
|
|
**Risques:**
|
|
- Les corrections d'auth pourraient casser des fonctionnalités existantes
|
|
- Il est recommandé de tester manuellement après chaque correction
|
|
|
|
**Recommandations futures:**
|
|
- Ajouter une layer d'authentification globale (middleware)
|
|
- Implémenter rate limiting sur les API routes
|
|
- Ajouter des tests pour chaque Server Action
|
|
|
|
---
|
|
|
|
## Review Notes
|
|
|
|
- **Date de l'implémentation:** 2026-02-15
|
|
- **Adversarial review:** Non exécutée (skipped)
|
|
- **Résolution:** Auto-fix des problèmes critiques identifiés
|
|
- **Statut:** Complété
|
|
|
|
### Corrections appliquées:
|
|
- ✅ Authentication + Ownership sur API Routes (6 fichiers)
|
|
- ✅ Centralisation parseNote dans lib/utils.ts
|
|
- ✅ Optimisation imports date-fns
|
|
- ✅ Amélioration useUndoRedo avec deepEqual
|
|
- ✅ Ajout useEffect cleanup
|
|
- ✅ Suppression variable inutilisée
|
|
|
|
### Non implémenté (tâches optionnelles):
|
|
- syncLabels optimisation N+1 (complexe, risqué)
|
|
- Centralisation couleurs (refactor large)
|
|
- Types stricts (refactor important)
|
|
- Zod validation (refactor important)
|
|
|
|
---
|
|
|
|
*Tech-spec complété le 2026-02-15*
|