veza/docs/archive/frontend-sessions-2026/FRONTEND_INTEGRATION.md
senke 0e7097ed1b chore(cleanup): J1 — purge 220MB debris, archive session docs (complete)
First-attempt commit 3a5c6e184 only captured the .gitignore change; the
pre-commit hook silently dropped the 343 staged moves/deletes during
lint-staged's "no matching task" path. This commit re-applies the intended
J1 content on top of bec75f143 (which was pushed in parallel).

Uses --no-verify because:
- J1 only touches .md/.json/.log/.png/binaries — zero code that would
  benefit from lint-staged, typecheck, or vitest
- The hook demonstrated it corrupts pure-rename commits in this repo
- Explicitly authorized by user for this one commit

Changes (343 total: 169 deletions + 174 renames):

Binaries purged (~167 MB):
- veza-backend-api/{server,modern-server,encrypt_oauth_tokens,seed,seed-v2}

Generated reports purged:
- 9 apps/web/lint_report*.json (~32 MB)
- 8 apps/web/tsc_*.{log,txt} + ts_*.log (TS error snapshots)
- 3 apps/web/storybook_*.json (1375+ stored errors)
- apps/web/{build_errors*,build_output,final_errors}.txt
- 70 veza-backend-api/coverage*.out + coverage_groups/ (~4 MB)
- 3 veza-backend-api/internal/handlers/*.bak

Root cleanup:
- 54 audit-*.png (visual regression baselines, ~11 MB)
- 9 stale MVP-era scripts (Jan 27, hardcoded v0.101):
  start_{iteration,mvp,recovery}.sh,
  test_{mvp_endpoints,protected_endpoints,user_journey}.sh,
  validate_v0101.sh, verify_logs_setup.sh, gen_hash.py

Session docs archived (not deleted — preserved under docs/archive/):
- 78 apps/web/*.md     → docs/archive/frontend-sessions-2026/
- 43 veza-backend-api/*.md → docs/archive/backend-sessions-2026/
- 53 docs/{RETROSPECTIVE_V,SMOKE_TEST_V,PLAN_V0_,V0_*_RELEASE_SCOPE,
          AUDIT_,PLAN_ACTION_AUDIT,REMEDIATION_PROGRESS}*.md
                        → docs/archive/v0-history/

README.md and CONTRIBUTING.md preserved in apps/web/ and veza-backend-api/.

Note: The .gitignore rules preventing recurrence were already pushed in
3a5c6e184 and remain in place — this commit does not modify .gitignore.

Refs: AUDIT_REPORT.md §11
2026-04-14 17:12:03 +02:00

657 lines
12 KiB
Markdown

# Frontend Integration Guide — Veza Backend API
**Version**: 1.2.0
**Date**: 2025-01-27
**Base URL**: `http://localhost:8080/api/v1` (configurable via `VITE_API_URL`)
---
## Table des Matières
1. [Configuration](#configuration)
2. [Authentification](#authentification)
3. [Format des Réponses](#format-des-réponses)
4. [Gestion des Erreurs](#gestion-des-erreurs)
5. [Codes d'Erreur](#codes-derreur)
6. [Exemples de Requêtes](#exemples-de-requêtes)
7. [Health Checks](#health-checks)
---
## Configuration
### Variables d'Environnement Frontend
```bash
# .env.local ou .env.production
VITE_API_URL=http://localhost:8080/api/v1
VITE_WS_URL=ws://localhost:8081/ws
VITE_STREAM_URL=ws://localhost:8082/stream
```
### Headers Requis
Toutes les requêtes authentifiées doivent inclure :
```javascript
{
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
```
---
## Authentification
### Format du Token JWT
Le token JWT est fourni dans le header `Authorization` :
```
Authorization: Bearer <token>
```
### Endpoints d'Authentification
#### POST `/auth/login`
**Request** :
```json
{
"email": "user@example.com",
"password": "password123",
"remember_me": false
}
```
**Response Success (200)** :
```json
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"token_type": "Bearer",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "username",
"role": "user"
}
}
}
```
**Response Error (401)** :
```json
{
"success": false,
"data": null,
"error": {
"code": 1000,
"message": "Invalid credentials",
"request_id": "req-123",
"timestamp": "2025-01-27T10:00:00Z"
}
}
```
#### POST `/auth/register`
**Request** :
```json
{
"email": "user@example.com",
"password": "password123",
"username": "username"
}
```
#### POST `/auth/refresh`
**Request** :
```json
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
#### GET `/auth/me`
**Headers** :
```
Authorization: Bearer <token>
```
**Response Success (200)** :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "username",
"role": "user",
"created_at": "2025-01-27T10:00:00Z"
}
}
```
---
## Format des Réponses
### Réponse Succès
Toutes les réponses de succès suivent ce format :
```json
{
"success": true,
"data": { ... },
"message": "Optional success message"
}
```
**Exemples** :
- **GET** `/tracks/:id` :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Track Title",
"artist": "Artist Name",
"duration": 180.5,
"status": "ready"
}
}
```
- **POST** `/tracks` (201 Created) :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "New Track",
"status": "processing"
},
"message": "Track uploaded successfully"
}
```
### Réponse Erreur
Toutes les réponses d'erreur suivent ce format standardisé :
```json
{
"success": false,
"data": null,
"error": {
"code": 2000,
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Email is required"
}
],
"request_id": "req-123",
"timestamp": "2025-01-27T10:00:00Z",
"context": {
"user_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
```
**Champs** :
- `code` : Code d'erreur numérique (voir [Codes d'Erreur](#codes-derreur))
- `message` : Message d'erreur lisible
- `details` : Détails de validation (optionnel, pour erreurs 400)
- `request_id` : ID de requête pour corrélation des logs (optionnel)
- `timestamp` : Timestamp ISO 8601
- `context` : Contexte additionnel (optionnel)
---
## Gestion des Erreurs
### TypeScript Interface
```typescript
interface APIError {
code: number;
message: string;
details?: Array<{
field: string;
message: string;
}>;
request_id?: string;
timestamp: string;
context?: Record<string, any>;
}
interface APIResponse<T> {
success: boolean;
data: T | null;
error?: APIError;
}
```
### Helper Function
```typescript
async function apiRequest<T>(
endpoint: string,
options?: RequestInit
): Promise<T> {
const token = localStorage.getItem('access_token');
const response = await fetch(`${import.meta.env.VITE_API_URL}${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
...options?.headers,
},
});
const data: APIResponse<T> = await response.json();
if (!data.success || data.error) {
throw new APIError(data.error!);
}
return data.data!;
}
class APIError extends Error {
constructor(public error: APIError) {
super(error.message);
this.name = 'APIError';
}
}
```
### Gestion des Codes HTTP
| Code HTTP | Signification | Action Frontend |
|-----------|---------------|-----------------|
| 200 | Succès | Traiter `data` |
| 201 | Créé | Traiter `data` |
| 400 | Bad Request | Afficher `error.message` + `error.details` |
| 401 | Unauthorized | Rediriger vers login, rafraîchir token |
| 403 | Forbidden | Afficher erreur, vérifier permissions |
| 404 | Not Found | Afficher "Ressource non trouvée" |
| 409 | Conflict | Afficher `error.message` |
| 422 | Unprocessable Entity | Afficher `error.message` + `error.details` |
| 429 | Too Many Requests | Afficher "Trop de requêtes", attendre |
| 500 | Internal Server Error | Logger `request_id`, afficher message générique |
| 502 | Bad Gateway | Afficher "Service temporairement indisponible" |
---
## Codes d'Erreur
### Authentication & Authorization (1000-1999)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 1000 | Invalid credentials | 401 |
| 1001 | Token expired | 401 |
| 1002 | Token invalid | 401 |
| 1003 | Forbidden | 403 |
| 1004 | Unauthorized | 401 |
### Validation (2000-2999)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 2000 | Validation failed | 400 |
| 2001 | Required field | 400 |
| 2002 | Invalid format | 400 |
| 2003 | Out of range | 400 |
### Resource (3000-3999)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 3000 | Not found | 404 |
| 3001 | Already exists | 409 |
| 3002 | Conflict | 409 |
### Business Logic (4000-4999)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 4000 | Operation not allowed | 422 |
| 4005 | Quota exceeded | 403 |
### Rate Limiting (5000-5099)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 5000 | Rate limit exceeded | 429 |
### Internal (9000-9999)
| Code | Message | HTTP Status |
|------|---------|-------------|
| 9000 | Internal error | 500 |
| 9001 | Database error | 500 |
---
## Exemples de Requêtes
### Health Check
```bash
curl -X GET http://localhost:8080/api/v1/health
```
**Response** :
```json
{
"status": "ok"
}
```
### Login
```bash
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password123",
"remember_me": false
}'
```
**Response Success** :
```json
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"token_type": "Bearer",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "username",
"role": "user"
}
}
}
```
**Response Error (Invalid Credentials)** :
```json
{
"success": false,
"data": null,
"error": {
"code": 1000,
"message": "Invalid credentials",
"request_id": "req-123",
"timestamp": "2025-01-27T10:00:00Z"
}
}
```
### Get User Profile
```bash
curl -X GET http://localhost:8080/api/v1/auth/me \
-H "Authorization: Bearer <token>"
```
**Response Success** :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "username",
"role": "user",
"created_at": "2025-01-27T10:00:00Z"
}
}
```
### Get Track
```bash
curl -X GET http://localhost:8080/api/v1/tracks/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer <token>"
```
**Response Success** :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Track Title",
"artist": "Artist Name",
"duration": 180.5,
"status": "ready",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2025-01-27T10:00:00Z"
}
}
```
**Response Error (Not Found)** :
```json
{
"success": false,
"data": null,
"error": {
"code": 3000,
"message": "track not found",
"request_id": "req-123",
"timestamp": "2025-01-27T10:00:00Z"
}
}
```
### Create Playlist
```bash
curl -X POST http://localhost:8080/api/v1/playlists \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"title": "My Playlist",
"description": "A great playlist",
"is_public": true
}'
```
**Response Success (201)** :
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "My Playlist",
"description": "A great playlist",
"is_public": true,
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2025-01-27T10:00:00Z"
}
}
```
**Response Error (Validation)** :
```json
{
"success": false,
"data": null,
"error": {
"code": 2000,
"message": "Validation failed",
"details": [
{
"field": "title",
"message": "Title is required"
}
],
"request_id": "req-123",
"timestamp": "2025-01-27T10:00:00Z"
}
}
```
---
## Health Checks
### GET `/health`
Health check simple (toujours 200 si serveur actif).
```bash
curl -X GET http://localhost:8080/api/v1/health
```
**Response** :
```json
{
"status": "ok"
}
```
### GET `/readyz`
Readiness probe (vérifie DB, Redis, RabbitMQ).
```bash
curl -X GET http://localhost:8080/api/v1/readyz
```
**Response Success (200)** :
```json
{
"status": "ready",
"database": "ok",
"redis": "ok",
"rabbitmq": "ok"
}
```
**Response Degraded (200)** :
```json
{
"status": "degraded",
"database": "ok",
"redis": "unavailable",
"rabbitmq": "unavailable"
}
```
**Note** : `/readyz` retourne toujours 200 (même en mode dégradé) pour permettre au service de démarrer même si Redis/RabbitMQ sont indisponibles.
### GET `/status`
Status détaillé (DB, Redis, Chat Server, Stream Server).
```bash
curl -X GET http://localhost:8080/api/v1/status
```
**Response** :
```json
{
"status": "ok",
"database": {
"status": "ok",
"latency_ms": 5
},
"redis": {
"status": "ok",
"latency_ms": 2
},
"chat_server": {
"status": "ok",
"url": "http://localhost:8081"
},
"stream_server": {
"status": "ok",
"url": "http://localhost:8082"
}
}
```
---
## Notes Importantes
### CORS
En production, `CORS_ALLOWED_ORIGINS` doit être configuré. Le Frontend doit être dans la liste des origines autorisées.
### Rate Limiting
- **Login** : 5 tentatives par minute
- **Global** : 100 requêtes par minute (configurable)
Les headers de rate limiting sont retournés :
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1234567890
```
### Pagination
Les listes (tracks, playlists, etc.) supportent la pagination :
```
GET /tracks?page=1&limit=20
```
**Response** :
```json
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"total_pages": 5,
"has_next": true,
"has_previous": false
}
}
```
### UUID Format
Tous les IDs sont des UUID v4 :
```
550e8400-e29b-41d4-a716-446655440000
```
---
## Support
- **Documentation API** : `/swagger/index.html` (Swagger UI)
- **Issues** : [GitHub Issues](https://github.com/veza/veza-backend-api/issues)
---
**Last Updated**: 2025-01-27