# AMC Backend v2 — Guide complet

API REST Laravel pour la plateforme AmplifyMyCom.
Ce guide est écrit pour un développeur qui installe le projet pour la première fois.

---

## Prérequis

| Outil | Version minimale | Vérification |
|-------|-----------------|--------------|
| PHP | 8.2 | `php -v` |
| Composer | 2.x | `composer -V` |
| MySQL | 8.x | `mysql --version` |

---

## Installation complète — étape par étape

### Étape 1 — Installer les dépendances PHP

```bash
cd amc-backend-v2
composer install
```

### Étape 2 — Créer le fichier de configuration

```bash
cp .env.example .env
```

Ouvre `.env` et renseigne au minimum :

```dotenv
APP_URL=http://localhost:8000
APP_FRONTEND_URL=http://localhost:5173

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=amc_database_v2
DB_USERNAME=root
DB_PASSWORD=

OPENAI_API_KEY=sk-...

LINKEDIN_CLIENT_ID=...
LINKEDIN_CLIENT_SECRET=...
LINKEDIN_REDIRECT_URI=http://localhost:8000/api/linkedin/callback
```

### Étape 3 — Générer la clé d'application

```bash
php artisan key:generate
```

Cette commande remplit automatiquement `APP_KEY` dans le `.env`.
Cette clé sert à chiffrer les sessions, les cookies et les données Laravel.

### Étape 4 — Créer la base de données MySQL

```sql
CREATE DATABASE amc_database_v2;
```

### Étape 5 — Lancer les migrations

```bash
php artisan migrate
```

Crée toutes les tables en base de données.

### Étape 6 — Insérer les données de base

```bash
php artisan db:seed
```

Crée les données initiales :
- 3 comptes utilisateurs de test
- Les thèmes de communication
- Les définitions de tonalité

Comptes créés :

| Email | Mot de passe | Rôle |
|-------|-------------|------|
| superadmin@amc.com | password123 | super-admin |
| admin@amc.com | password123 | admin |
| consultant@amc.com | password123 | consultant |
| collecteur@amc.com | password123 | collecteur |

### Étape 7 — Migration des données depuis l'ancienne BDD (si applicable)

> Uniquement si tu migres depuis la v1. Si tu pars d'une base vide, passe à l'étape 8.

```bash
# Entre dans le dossier de migration
cd migrate-data

# Lance le script de migration
php migrate.php

# Reviens à la racine
cd ..
```

Le script lit l'ancienne base et transforme les données selon le mapping documenté
dans `migrate-data/MIGRATION_MAPPING.md`.

### Restauration locale depuis la base de recette

Pour repartir d'une base locale identique au dump recette présent dans
`migrate-data/Migrate_local_data/jtwutebrecuser.sql`, utilise la commande :

```bash
php artisan local:restore-recette-data --local
```

La commande est volontairement protégée :
- elle refuse de tourner si `APP_ENV` n'est pas `local` ;
- elle refuse de tourner sans l'option explicite `--local` ;
- elle demande une confirmation avant de vider la base ;
- elle met automatiquement dans `.env` la clé de chiffrement compatible avec les données recette.

Pour une exécution non interactive :

```bash
php artisan local:restore-recette-data --local --yes
```

Ce que fait la commande :
- met à jour les variables `ENCRYPTION_*` locales ;
- vide complètement la base locale avec `db:wipe` ;
- importe le dump SQL de recette ;
- lance `php artisan migrate --force` pour appliquer d'éventuelles migrations locales manquantes.

Ne jamais utiliser cette commande sur recette ou production.

### Étape 8 — Configurer le chiffrement dans le .env

Avant de lancer le chiffrement, tu dois avoir ces variables dans ton `.env`.
La clé `ENCRYPTION_KEY_CURRENT` doit être la même valeur que `APP_KEY` :

```dotenv
ENCRYPTION_ENABLED=true
ENCRYPTION_VERSION_CURRENT=v1
ENCRYPTION_KEY_CURRENT=base64:MEME_VALEUR_QUE_APP_KEY
ENCRYPTION_KEY_PREVIOUS=
```

Exemple concret — si ton `APP_KEY` est :
```
APP_KEY=base64:VOTRE_CLE_APP_KEY
```

Alors tu mets :
```dotenv
ENCRYPTION_KEY_CURRENT=base64:VOTRE_CLE_APP_KEY
```

### Étape 9 — Chiffrer les données existantes (RGPD)

```bash
php artisan encryption:migrate-existing-data
```

Cette commande chiffre toutes les données personnelles déjà en base :
emails, prénoms, noms, téléphones, tokens LinkedIn, etc.

Elle est **idempotente** : tu peux la relancer sans risque si elle a été interrompue.
Elle ignore automatiquement les données déjà chiffrées.

### Étape 10 — Démarrer le serveur

```bash
php artisan serve
# API disponible sur http://localhost:8000
```

Pour executer les jobs Laravel en local (analyses IA, traitements en arriere-plan, etc.),
ouvre un **deuxieme terminal** dans `amc-backend-v2` et lance :

```bash
php artisan queue:work --tries=3 --timeout=600
```

> `php artisan serve` lance uniquement le serveur HTTP. Les jobs dispatches avec
> `dispatch()` ne sont traites que si un worker de queue tourne, sauf si
> `QUEUE_CONNECTION=sync` est configure dans `.env`.

---

## Résumé des commandes dans l'ordre

```bash
composer install
cp .env.example .env
# → éditer .env avec tes valeurs

php artisan key:generate
# → créer la base MySQL amc_database_v2

php artisan migrate
php artisan db:seed

# Si migration depuis v1 :
cd migrate-data && php migrate.php && cd ..

# Configurer ENCRYPTION_KEY_CURRENT = APP_KEY dans .env, puis :
php artisan encryption:migrate-existing-data

# Terminal 1 : serveur API
php artisan serve

# Terminal 2 : worker de queue pour executer les jobs
php artisan queue:work --tries=3 --timeout=600
```

---

## Architecture du projet

```
amc-backend-v2/
├── app/
│   ├── Console/Commands/     # Commandes artisan personnalisées
│   ├── Http/
│   │   ├── Controllers/Api/  # Contrôleurs REST (un par domaine)
│   │   ├── Middleware/       # Auth, rôles, sécurité HTTP
│   │   └── Resources/        # Transformateurs de réponse JSON
│   ├── Models/               # Modèles Eloquent (clés UUID)
│   ├── Services/             # Logique métier
│   ├── Jobs/                 # Jobs asynchrones (analyse IA)
│   ├── Mail/                 # Emails transactionnels
│   └── Enums/                # Énumérations (rôles, statuts...)
├── database/
│   ├── migrations/           # Toutes les migrations (50+)
│   └── seeders/              # Données initiales
├── routes/
│   └── api.php               # Toutes les routes API
├── migrate-data/
│   ├── migrate.php           # Script de migration v1 → v2
│   └── MIGRATION_MAPPING.md  # Documentation du mapping
└── .env.example              # Template de configuration
```

---

## Authentification

L'API utilise **Laravel Sanctum** avec deux tokens :

- `access-token` : durée de vie **75 minutes**, utilisé pour toutes les requêtes
- `refresh-token` : durée de vie **30 jours**, utilisé uniquement pour renouveler l'access token

Toutes les requêtes protégées nécessitent le header :
```
Authorization: Bearer <access-token>
```

Pour renouveler l'access token avant expiration :
```
POST /api/auth/refresh
Authorization: Bearer <refresh-token>
```

**Limite de sessions** : un même compte peut être connecté sur **3 appareils simultanément**.
Si une 4ème connexion arrive, la session la plus ancienne est automatiquement révoquée.

---

## Rôles utilisateurs

| Rôle | Description |
|------|-------------|
| `super-admin` | Super administrateur — accès total à tout |
| `admin` | Administrateur — accès total à tout mais avec une restricition |
| `referent` | Responsable d'une entreprise — gère les ambassadeurs et valide les posts |
| `ambassadeur` | Crée et soumet des posts pour validation |
| `consultant` | Accès lecture/analyse sur plusieurs entreprises |
| `collecteur` | Collecte et importe les métriques LinkedIn |

---

## Middlewares — comment ça fonctionne

Les middlewares sont des couches qui s'exécutent **avant** que la requête atteigne le contrôleur.
Chaque requête passe par ces filtres dans l'ordre.

### 1. `SecurityHeaders` — appliqué à toutes les requêtes

Ce middleware ajoute automatiquement des headers de sécurité HTTP à chaque réponse.
Il protège contre les attaques web courantes.

| Header ajouté | Valeur | Protection |
|---------------|--------|------------|
| `X-Frame-Options` | `DENY` | Bloque le **clickjacking** — empêche d'intégrer le site dans une iframe |
| `X-Content-Type-Options` | `nosniff` | Bloque le **MIME sniffing** — le navigateur ne devine pas le type de fichier |
| `Strict-Transport-Security` | `max-age=31536000` | Force **HTTPS** pendant 1 an (HSTS) |
| `Content-Security-Policy` | voir ci-dessous | Bloque les injections **XSS** |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Contrôle les infos envoyées dans le header Referer |
| `Permissions-Policy` | `geolocation=(), microphone=(), camera=()` | Désactive l'accès à la géolocalisation, micro et caméra |

La **Content-Security-Policy (CSP)** définit précisément quelles sources sont autorisées :
- Scripts JS : uniquement depuis le même domaine (`'self'`)
- Images : même domaine + data URIs (base64)
- Iframes : complètement interdites (`'none'`)
- Formulaires : soumission uniquement vers le même domaine

### 2. `auth:sanctum` — protège les routes privées

Vérifie que le header `Authorization: Bearer <token>` est présent et valide.
Si le token est absent ou expiré → réponse `401 Unauthenticated`.

```php
// Dans routes/api.php — toutes les routes dans ce groupe sont protégées
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/me', ...);
    Route::get('/posts', ...);
    // etc.
});
```

### 3. `auth.token` — authentification flexible (pour les médias)

Utilisé uniquement pour la route de médias des tickets :
```
GET /api/tickets/media/{uuid}
```

Ce middleware accepte l'authentification de deux façons :
- Via le header `Authorization: Bearer <token>` (méthode standard)
- Via un paramètre dans l'URL `?token=<token>` (pour les balises `<img>` HTML qui ne peuvent pas envoyer de headers)

### 4. `role` — vérification des permissions

Vérifie que l'utilisateur connecté a le bon rôle pour accéder à la ressource.

```php
// Exemple d'utilisation dans les routes
Route::middleware(['auth:sanctum', 'role:super-admin,admin,referent'])->group(function () {
    // Accessible uniquement aux superadmin, admin et referent
});
```

Comportement :
- Utilisateur non connecté → `401 Unauthenticated`
- Compte désactivé (`is_active = false`) → `403 Account inactive`
- Rôle insuffisant → `403 Insufficient permissions`

---

## Chiffrement des données (RGPD)

### Pourquoi chiffrer ?

Les données personnelles (emails, noms, téléphones, tokens LinkedIn) sont stockées
**chiffrées en base de données**. Même si quelqu'un accède directement à MySQL,
il ne verra que des données illisibles.

### Comment ça fonctionne

Le chiffrement est **transparent** pour l'application. Les modèles Eloquent
chiffrent automatiquement à l'écriture et déchiffrent à la lecture via des accesseurs/mutateurs.

```
Écriture :  valeur en clair  →  [EncryptionService::encrypt()]  →  stocké chiffré en base
Lecture  :  valeur chiffrée  →  [EncryptionService::decrypt()]  →  retourné en clair
```

L'algorithme utilisé est **AES-256-CBC** (standard militaire, 256 bits).

### Le problème de la recherche par email

On ne peut pas faire `WHERE email = 'user@example.com'` sur des données chiffrées,
car la même valeur chiffrée deux fois donne deux résultats différents.

Solution : chaque email est aussi stocké sous forme de **hash SHA-256** dans la colonne `email_hash`.
Ce hash est non réversible (on ne peut pas retrouver l'email depuis le hash) mais déterministe
(même email = même hash). La recherche se fait sur le hash.

```
email@example.com  →  SHA-256  →  a665a45920422f9d417e4867efdc4fb8...  (stocké dans email_hash)
```

### Le système de versions

Chaque donnée chiffrée est associée à une **version** (`v1`, `v2`, etc.) stockée dans
la colonne `encryption_version`. Cela permet de savoir avec quelle clé la donnée a été chiffrée.

```
encryption_version = NULL  →  donnée en clair (avant migration)
encryption_version = "v1"  →  chiffrée avec ENCRYPTION_KEY_CURRENT (clé v1)
encryption_version = "v2"  →  chiffrée avec la nouvelle clé (après rotation)
```

### Configuration requise dans le .env

```dotenv
# Active le chiffrement
ENCRYPTION_ENABLED=true

# Version actuelle (commence à v1, incrémente à chaque rotation)
ENCRYPTION_VERSION_CURRENT=v1

# Clé de chiffrement actuelle = même valeur que APP_KEY
ENCRYPTION_KEY_CURRENT=base64:VOTRE_CLE_APP_KEY

# Ancienne clé (vide au départ, remplie lors d'une rotation)
ENCRYPTION_KEY_PREVIOUS=
```

> La valeur de `ENCRYPTION_KEY_CURRENT` doit être identique à `APP_KEY`.
> Les deux sont générés par `php artisan key:generate`.

### Données chiffrées

| Table | Colonnes chiffrées |
|-------|-------------------|
| `users` | `firstname`, `lastname`, `email`, `telephone`, `linkedin_id` |
| `linkedin_accounts` | `access_token`, `refresh_token`, `first_name`, `last_name`, `headline`, `public_profile_url` |
| `linkedin_profiles` | `linkedin_objectif`, `expertise_themes`, `target_audience`, `professionnal_positioning`, `core_values`, `topic_id_avoid_communication_tone` |

### Rotation des clés

La rotation consiste à changer la clé de chiffrement périodiquement (tous les 15 jours).
Elle se fait en 5 étapes :

**1. Générer une nouvelle clé**
```bash
php artisan encryption:generate-key
# Affiche : base64:NOUVELLE_CLE_GENEREE
```

**2. Mettre à jour le .env**
```dotenv
# Incrémenter la version
ENCRYPTION_VERSION_CURRENT=v2

# Déplacer l'ancienne clé dans PREVIOUS
ENCRYPTION_KEY_PREVIOUS=base64:ANCIENNE_CLE

# Mettre la nouvelle clé dans CURRENT
ENCRYPTION_KEY_CURRENT=base64:NOUVELLE_CLE_GENEREE
```

**3. Vider le cache de config**
```bash
php artisan config:clear
```

**4. Lancer la rotation**
```bash
php artisan encryption:rotate
# Déchiffre avec l'ancienne clé, re-chiffre avec la nouvelle
```

La rotation est aussi **automatique** via le scheduler Laravel (1er et 15 de chaque mois).

---

## Routes API principales

### Publiques (sans authentification)

| Méthode | Route | Description |
|---------|-------|-------------|
| POST | `/api/auth/login` | Connexion |
| POST | `/api/auth/register` | Inscription |
| POST | `/api/password/reset-request` | Demande réinitialisation mot de passe |
| GET | `/api/linkedin/callback` | Callback OAuth LinkedIn |
| GET | `/api/public/news` | Actualités publiques |
| GET | `/api/public/testimonials` | Témoignages publics |
| GET | `/api/users/{uuid}/photo` | Photo de profil (publique) |
| GET | `/api/entreprises/{uuid}/logo` | Logo entreprise (public) |

### Protégées (Bearer token requis)

| Méthode | Route | Description |
|---------|-------|-------------|
| GET | `/api/auth/me` | Profil de l'utilisateur connecté |
| POST | `/api/auth/refresh` | Renouveler l'access token |
| POST | `/api/auth/logout` | Déconnexion |
| CRUD | `/api/users` | Gestion des utilisateurs |
| CRUD | `/api/entreprises` | Gestion des entreprises |
| CRUD | `/api/posts` | Bibliothèque de posts |
| POST | `/api/posts/{id}/approve` | Approuver un post |
| POST | `/api/posts/{id}/reject` | Rejeter un post |
| POST | `/api/posts/{id}/publish` | Publier un post sur LinkedIn |
| CRUD | `/api/competitors` | Gestion des concurrents |
| POST | `/api/competitor-metrics/import` | Import métriques concurrents (Excel) |
| POST | `/api/competitor-metrics/export` | Export métriques concurrents |
| GET | `/api/dashboard/referent-stats` | Stats dashboard référent |
| GET | `/api/dashboard/ambassador/*` | Stats dashboard ambassadeur |
| GET | `/api/reporting/*` | Reporting (overview, audience, engagement) |
| POST | `/api/ai/generate-content` | Génération de contenu IA |
| CRUD | `/api/media-items` | Médiathèque |
| CRUD | `/api/tickets` | Tickets de support |
| GET | `/api/activity-logs` | Historique des actions |

---

## Commandes artisan personnalisées

```bash
# Chiffrement
php artisan encryption:migrate-existing-data   # Chiffre les données existantes (1 seule fois)
php artisan encryption:rotate                  # Rotation des clés (tous les 15 jours)
php artisan encryption:generate-key            # Génère une nouvelle clé AES-256

# Queue / jobs
php artisan queue:work --tries=3 --timeout=600 # Execute les jobs en attente (analyses IA, etc.)

# Posts
php artisan posts:publish-scheduled            # Publie les posts programmés
php artisan posts:send-validation-reminders    # Envoie les rappels de validation
php artisan posts:analyze-company              # Analyse IA des posts entreprise
php artisan posts:analyze-competitors          # Analyse IA des posts concurrents
php artisan posts:cleanup-old                  # Supprime les anciens posts

# Utilisateurs
php artisan users:logout-inactive              # Déconnecte les utilisateurs inactifs

# Médias
php artisan medias:clean-orphans               # Supprime les médias orphelins
```

---

## Tâches planifiées (Cron)

| Fréquence | Tâche |
|-----------|-------|
| Toutes les minutes | Publication des posts programmés |
| Toutes les heures | Rappels de validation |
| Quotidien | Déconnexion des utilisateurs inactifs |
| Quotidien | Nettoyage des anciens posts |
| 1er et 15 du mois | Rotation automatique des clés de chiffrement |

**Sur un serveur avec accès cron système :**
```bash
* * * * * cd /chemin/vers/amc-backend-v2 && php artisan schedule:run >> /dev/null 2>&1
```

**Sur un hébergement mutualisé (OVH, etc.) :**
Configurer cron-job.org pour appeler chaque minute :
```
GET https://ton-domaine.com/api/cron/run?token=CRON_SECRET_TOKEN
```

---

## Variables d'environnement — référence complète

```dotenv
# Application
APP_NAME=AmplifyMyCom
APP_ENV=local                          # local | staging | production
APP_KEY=                               # généré par php artisan key:generate
APP_DEBUG=true                         # false en production
APP_URL=http://localhost:8000
APP_FRONTEND_URL=http://localhost:5173

# CORS — origines autorisées (séparer par virgule)
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000

# Base de données
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=amc_database_v2
DB_USERNAME=root
DB_PASSWORD=

# Email
MAIL_MAILER=smtp
MAIL_HOST=ssl0.ovh.net
MAIL_PORT=587
MAIL_USERNAME=support@ampliflymycom.com
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=support@ampliflymycom.com
TICKET_SUPPORT_EMAIL=support@ampliflymycom.com

# OpenAI (génération de contenu IA)
OPENAI_API_KEY=sk-...
OPENAI_VERIFY_SSL=true                 # false en local si problème SSL

# LinkedIn OAuth
LINKEDIN_CLIENT_ID=
LINKEDIN_CLIENT_SECRET=
LINKEDIN_REDIRECT_URI=http://localhost:8000/api/linkedin/callback
LINKEDIN_SCOPES="openid profile email w_member_social"
LINKEDIN_DEV_MODE=false                # true pour simuler LinkedIn en local

# Chiffrement RGPD — ENCRYPTION_KEY_CURRENT = même valeur que APP_KEY
ENCRYPTION_ENABLED=true
ENCRYPTION_VERSION_CURRENT=v1
ENCRYPTION_KEY_CURRENT=base64:...     # copier la valeur de APP_KEY ici
ENCRYPTION_KEY_PREVIOUS=              # vide au départ, rempli lors d'une rotation

# Cron HTTP sécurisé
CRON_SECRET_TOKEN=                    # généré par : php -r "echo bin2hex(random_bytes(32));"
```

---

## Problèmes fréquents

**`php artisan key:generate` ne fonctionne pas**
→ Vérifie que le fichier `.env` existe (`cp .env.example .env`)

**Erreur de connexion MySQL**
→ Vérifie que MySQL tourne et que la base `amc_database_v2` existe

**Erreur SSL avec OpenAI en local**
→ Ajoute `OPENAI_VERIFY_SSL=false` dans le `.env`

**Les emails ne partent pas**
→ En local, utilise `MAIL_MAILER=log` pour voir les emails dans `storage/logs/laravel.log`

**`encryption:migrate-existing-data` échoue avec "No encryption key found"**
→ Vérifie que `ENCRYPTION_KEY_CURRENT` est bien défini dans `.env` et que `php artisan config:clear` a été lancé

**`encryption:migrate-existing-data` échoue avec "Decryption error"**
→ Les données sont peut-être déjà chiffrées. La commande ignore automatiquement les données déjà chiffrées, ce n'est pas bloquant.
