# Rapport Global de Livraison â€” Thinkers Backend API

**Date** : FÃ©vrier 2026
**Version** : 1.0.0
**Stack** : Node.js + TypeScript + Express + Prisma + MySQL

---

## 1. Endpoints livrÃ©s + exemples

### Public API

#### `GET /health`
```json
{ "status": "ok", "version": "1.0.0", "timestamp": "2026-02-25T10:00:00Z", "environment": "production" }
```

#### `GET /api/settings`
```json
{ "success": true, "data": { "site_name": "Tinkers Production", "phone": "+225 07 00 00 00 00", ... } }
```

#### `GET /api/services`
```json
{ "success": true, "data": [{ "id": 1, "title": "Production VidÃ©o", "slug": "production-video", "cover_image_url": "https://api.thinkers-production.com/uploads/services/uuid.jpg", ... }] }
```

#### `GET /api/gallery?type=photo&category=photographie&page=1&limit=12`
```json
{ "success": true, "data": { "items": [...], "page": 1, "limit": 12, "total": 50, "totalPages": 5 } }
```

#### `POST /api/contact`
```json
// Request
{ "name": "Jean Dupont", "email": "jean@example.com", "message": "Je suis intÃ©ressÃ© par vos services.", "service_id": 1, "website": "" }
// Response 201
{ "success": true, "data": { "message": "Message sent successfully. We will get back to you soon." } }
```

#### `POST /api/contact` (honeypot rempli)
```json
// Retourne 201 silencieusement â€” ne rÃ©vÃ¨le pas la dÃ©tection bot
```

### Admin API

#### `POST /api/admin/auth/login`
```json
// Request
{ "email": "admin@thinkers-production.com", "password": "MonMotDePasse" }
// Response 200 + Set-Cookie: access_token (httpOnly), refresh_token (httpOnly)
{ "success": true, "data": { "user": { "id": 1, "email": "admin@...", "name": "Thinkers Admin", "is_admin": true } } }
```

#### `GET /api/admin/stats`
```json
{ "success": true, "data": { "counts": { "services": 4, "gallery": 12, "partners": 5, "messages": { "total": 30, "new": 3 } }, "recentMessages": [...] } }
```

#### `PATCH /api/admin/settings` (avec whitelist)
```json
// Request body
{ "site_name": "Tinkers Production", "email": "contact@thinkers-production.com", "unknown_key": "malicious" }
// Response 200
{ "success": true, "data": { "updated": ["site_name", "email"], "rejected": ["unknown_key"], "errors": ["Rejected unknown keys: unknown_key"] } }
```

---

## 2. Choix techniques et justifications

### Framework : Express.js (vs NestJS)

**Choix retenu : Express + TypeScript avec architecture modulaire**

| CritÃ¨re | Express | NestJS |
|---------|---------|--------|
| DÃ©ploiement cPanel | âœ… Simple (node dist/main.js) | âš ï¸ Lourd, overhead |
| Performance | âœ… Minimal overhead | âš ï¸ DI container |
| Courbe d'apprentissage | âœ… Standard | Steep |
| Scope du projet | âœ… AdaptÃ© | Over-engineering |
| Production-ready | âœ… Avec bonne structure | âœ… |

### Base de donnÃ©es : MySQL (vs PostgreSQL)

**Choix retenu : MySQL**

- cPanel fournit MySQL nativement (phpMyAdmin, GUI)
- PostgreSQL nÃ©cessite une config serveur supplÃ©mentaire sur mutualisÃ©
- Performance Ã©quivalente pour le scope de ce projet
- Prisma gÃ¨re les deux identiquement

### ORM : Prisma

- Type-safety native avec TypeScript
- Migrations dÃ©claratives propres
- Excellent DX (Prisma Studio pour la gestion de la DB en dev)
- Compatible MySQL et PostgreSQL

### Authentification : JWT httpOnly Cookies + Refresh Token

- **Access token** (15 min) : stockÃ© en cookie httpOnly â€” inaccessible en JavaScript
- **Refresh token** (7 jours) : stockÃ© en cookie httpOnly + en DB pour rÃ©vocation
- **Rotation** des refresh tokens : chaque refresh gÃ©nÃ¨re un nouveau token
- RÃ©sistance aux attaques XSS (cookies httpOnly)

### Validation : Zod

- SchÃ©mas TypeScript natifs (infÃ©rence de types)
- Composable et maintenable
- Messages d'erreur prÃ©cis pour le frontend

---

## 3. Points d'attention production (cPanel)

### Critique

1. **Secrets JWT** : GÃ©nÃ©rer avec `openssl rand -hex 32`, JAMAIS les valeurs d'exemple
2. **COOKIE_SECURE=true** : Obligatoire en HTTPS production
3. **CORS_ORIGINS** : Contenir exactement les origines frontend (sans trailing slash)
4. **Dossier uploads** : `chmod 755 uploads/`, vÃ©rifier les permissions d'Ã©criture
5. **Migrations** : ExÃ©cuter `npm run migrate` avant `npm run seed`, et avant chaque mise Ã  jour

### Performance

6. **PM2** : Utiliser PM2 pour le redÃ©marrage automatique et la gestion des logs
7. **NODE_ENV=production** : DÃ©sactive les logs de query Prisma, active les logs Winston en fichier

### SÃ©curitÃ©

8. **Rate limiting** : Login (10/15min/IP), Contact (3/5min/IP) â€” ajuster si nÃ©cessaire
9. **Whitelist settings** : Toute modification du whitelist `settings.whitelist.ts` doit Ãªtre dÃ©libÃ©rÃ©e
10. **Uploads** : Max 8MB, MIME: jpg/jpeg/png/webp uniquement (configurable dans `.env`)

---

## 4. CohÃ©rence avec le front Next.js (Codex)

### Endpoints attendus par le frontend

| Frontend consomme | Backend livre | Statut |
|-------------------|---------------|--------|
| `GET /api/settings` | âœ… `publicSettingsRouter` | AlignÃ© |
| `GET /api/services` | âœ… `publicServicesRouter` | AlignÃ© |
| `GET /api/services/:slug` | âœ… | AlignÃ© |
| `GET /api/gallery?type=&category=&page=&limit=12` | âœ… Pagination standard | AlignÃ© |
| `GET /api/gallery/categories` | âœ… | AlignÃ© |
| `GET /api/partners` | âœ… | AlignÃ© |
| `POST /api/contact` | âœ… + honeypot + rate limit | AlignÃ© |
| `POST /api/admin/auth/login` | âœ… httpOnly cookie | AlignÃ© |
| `GET /api/admin/auth/me` | âœ… | AlignÃ© |
| `POST /api/admin/auth/logout` | âœ… | AlignÃ© |
| `POST /api/admin/auth/refresh` | âœ… Rotation token | AlignÃ© |
| Admin CRUD complet | âœ… Tous modules | AlignÃ© |

### Format JSON

Toutes les rÃ©ponses suivent le standard :
- **SuccÃ¨s** : `{ success: true, data: ... }`
- **Liste paginÃ©e** : `{ success: true, data: { items, page, limit, total, totalPages } }`
- **Erreur** : `{ success: false, error: { code, message, details } }`

### Champs image

Les URLs d'images sont toujours des URLs absolues calculÃ©es avec `UPLOADS_PUBLIC_URL` :
- `cover_image_url` pour les services
- `logo_url` pour les partenaires
- `file_url` + `thumbnail_url` pour la galerie

### Authentication (cookies)

Le frontend doit envoyer `credentials: 'include'` dans tous les fetch vers l'API admin :
```typescript
// Next.js
fetch('/api/admin/stats', { credentials: 'include' })
// ou Axios
axios.defaults.withCredentials = true;
```

---

## 5. Checklist de recette

### Recette Public

- [ ] `GET /health` â†’ `{ status: "ok" }`
- [ ] `GET /api/settings` â†’ objet clÃ©-valeur des settings publics
- [ ] `GET /api/services` â†’ tableau des services actifs avec `cover_image_url`
- [ ] `GET /api/services/production-video` â†’ service par slug
- [ ] `GET /api/gallery?page=1&limit=12` â†’ pagination correcte
- [ ] `GET /api/gallery?type=photo` â†’ filtre par type
- [ ] `GET /api/gallery?type=video` â†’ items vidÃ©o avec `embed_url`
- [ ] `GET /api/gallery/categories` â†’ liste des catÃ©gories
- [ ] `GET /api/partners` â†’ partenaires actifs avec `logo_url`
- [ ] `POST /api/contact` (valide) â†’ 201
- [ ] `POST /api/contact` (email invalide) â†’ 422
- [ ] `POST /api/contact` (honeypot rempli) â†’ 201 silencieux
- [ ] `POST /api/contact` (4Ã¨me req / 5min) â†’ 429

### Recette Admin

- [ ] `POST /api/admin/auth/login` (mauvais mdp) â†’ 401
- [ ] `POST /api/admin/auth/login` (correct) â†’ 200 + cookies httpOnly
- [ ] `GET /api/admin/auth/me` (sans cookie) â†’ 401
- [ ] `GET /api/admin/auth/me` (avec cookie) â†’ 200 + user info
- [ ] `GET /api/admin/stats` â†’ counts + messages rÃ©cents
- [ ] `GET /api/admin/services` â†’ liste paginÃ©e
- [ ] `POST /api/admin/services` (multipart + image) â†’ 201
- [ ] `PATCH /api/admin/services/:id` â†’ mise Ã  jour
- [ ] `DELETE /api/admin/services/:id` â†’ 204
- [ ] `GET /api/admin/gallery` â†’ liste paginÃ©e avec filtres
- [ ] `POST /api/admin/gallery` (photo + thumbnail Sharp) â†’ 201 + thumbnail_url
- [ ] `POST /api/admin/gallery` (vidÃ©o YouTube) â†’ embed_url calculÃ©
- [ ] `GET /api/admin/partners` â†’ liste
- [ ] `POST /api/admin/partners` (avec logo) â†’ 201
- [ ] `GET /api/admin/messages` â†’ liste avec statut
- [ ] `GET /api/admin/messages/:id` â†’ auto-mark read
- [ ] `POST /api/admin/messages/:id/archive` â†’ statut archived
- [ ] `DELETE /api/admin/messages/:id` â†’ 204
- [ ] `GET /api/admin/settings` â†’ groupÃ© par group
- [ ] `PATCH /api/admin/settings` (clÃ© valide) â†’ updated: [key]
- [ ] `PATCH /api/admin/settings` (clÃ© inconnue) â†’ rejected: [key]
- [ ] `PATCH /api/admin/settings` (email invalide pour clÃ© email) â†’ error
- [ ] `POST /api/admin/auth/logout` â†’ 200 + cookies effacÃ©s
- [ ] `GET /api/admin/auth/me` (aprÃ¨s logout) â†’ 401

---

## 6. Tests automatisÃ©s

```bash
npm run test
```

Tests couverts :
- âœ… Auth : login (succÃ¨s/Ã©chec/non-admin), logout, me non-auth
- âœ… Settings whitelist : clÃ©s inconnues rejetÃ©es, validation email/url/text
- âœ… Contact : validation, honeypot, rate limit
- âœ… Gallery : pagination, filtres type/category, embed URL

---

## 7. Documentation

- **Swagger/OpenAPI** : `GET /docs` (interface interactive)
- **Spec JSON** : `GET /docs.json`
- **README** : [README.md](./README.md)
- **DÃ©ploiement** : [DEPLOY_CPANEL.md](./DEPLOY_CPANEL.md)

