# Plan détaillé de migration RCWS vers API standard

## Objectif

Migrer progressivement le code historique `rcws` vers ce projet, sans casser les clients existants comme `facts`, tout en construisant une API HTTP versionnée, testable et maintenable.

Le principe directeur est le suivant :

```text
client legacy facts
  -> /serveruser/index.php
  -> adaptateur legacy
  -> service métier cible
  -> réponse au format historique si nécessaire

client REST
  -> /api/v1/...
  -> route REST
  -> même service métier cible
  -> réponse JSON standard data/error/meta
```

La branche legacy doit être une couche de compatibilité temporaire. Elle ne doit pas redevenir un dispatch libre vers des fonctions PHP.

## État de départ

Le projet contient déjà :

- un front controller REST : `api/v1/index.php` ;
- un noyau HTTP minimal : `Request`, `Response`, `Router`, `Auth`, `ApiError` ;
- une configuration d'authentification par variables d'environnement ;
- un mapping legacy : `api/src/Legacy/LegacyMethodMap.php` ;
- un adaptateur legacy initial pour `serveruser` ;
- un stub `DomainService`.

L'adaptateur legacy accepte actuellement :

```text
/serveruser/index.php
/api/legacy/serveruser/index.php
```

Il lit :

```text
methode=<nom_legacy>
param=<payload_json>
apikey=<cle_legacy>
```

Il route uniquement `getUsersByDomain` et `getUsersApi` vers le service cible. Les autres méthodes déclarées dans la liste blanche retournent `501 not_implemented`.

## Architecture cible

Arborescence cible recommandée :

```text
api/
  v1/
    index.php
  legacy/
    serveruser/
      index.php
    serverfacts/
      index.php
    servermedia/
      index.php
    serverstats/
      index.php
    serverutils/
      index.php
  src/
    Auth/
    Http/
    Legacy/
    Repository/
    Service/
    Validator/
legacy-rcws/
  serveruser/
  serverfacts/
  servermedia/
  serverstats/
  serverutils/
  model/
```

Le dossier `legacy-rcws/` doit rester isolé. Il sert de source de vérité temporaire pendant l'extraction. Le nouveau code ne doit pas dépendre durablement de variables globales ou de fichiers inclus dynamiquement depuis ce dossier.

## Règles de migration

1. Ne pas importer de secrets réels.
2. Ne pas conserver `call_user_func_array($_REQUEST['methode'], ...)` dans le nouveau chemin d'exécution.
3. Toute méthode legacy doit passer par une liste blanche explicite.
4. Tout accès à une base doit être déplacé dans un repository.
5. Toute logique métier doit être déplacée dans un service.
6. Les routes REST et legacy doivent appeler les mêmes services.
7. Les réponses REST doivent rester au format `data/error/meta`.
8. Les réponses legacy peuvent conserver temporairement l'ancien format, par exemple `param1`, `param2`, `param3`.
9. Les champs sensibles ne doivent jamais sortir des services publics : `password`, `salt`, `password_requested_at`.
10. Chaque migration de méthode doit être validée par un test manuel `curl` et, dès que possible, par un test automatisé.

## Phase 1 - Stabiliser la compatibilité legacy

### 1.1 Importer le code RCWS historique

Importer le code depuis :

```text
/mnt/One/web/beta/rcws
```

vers :

```text
legacy-rcws/
```

Exclusions recommandées :

- `.git/`
- `vendor/` si non indispensable au premier lot ;
- logs ;
- dumps ou bases de données de production ;
- fichiers de configuration contenant des secrets ;
- caches et fichiers temporaires.

Ajouter un fichier de configuration d'exemple pour chaque composant qui en a besoin :

```text
legacy-rcws/serverfacts/config/config.example.ini
```

### 1.2 Conserver les URLs legacy

Créer ou maintenir les chemins suivants :

```text
/serveruser/index.php
/serverfacts/index.php
/servermedia/index.php
/serverstats/index.php
/serverutils/index.php
```

Ces fichiers ne doivent contenir qu'un bootstrap court vers un contrôleur legacy dédié.

### 1.3 Implémenter les contrôleurs legacy par domaine

Créer :

```text
api/src/Legacy/ServerUserLegacyController.php
api/src/Legacy/ServerFactsLegacyController.php
api/src/Legacy/ServerMediaLegacyController.php
api/src/Legacy/ServerStatsLegacyController.php
api/src/Legacy/ServerUtilsLegacyController.php
```

Chaque contrôleur doit :

- lire `methode`, `param`, `apikey` ;
- valider la clé via configuration ;
- valider `methode` via `LegacyMethodMap` ;
- décoder `param` en JSON ;
- appeler un service cible ;
- formater la réponse legacy attendue.

### 1.4 Journaliser les usages legacy

Ajouter un logger minimal :

```text
api/src/Legacy/LegacyLogger.php
```

Format recommandé :

```text
[deprecated-api] component=serveruser methode=getUsersByDomain route=/api/v1/domains/1/users client=facts
```

Les logs ne doivent jamais contenir :

- clé API ;
- mot de passe ;
- token ;
- payload complet si sensible.

## Phase 2 - Migrer le socle users/domains

### 2.1 Créer les repositories

Créer :

```text
api/src/Repository/PdoFactory.php
api/src/Repository/UserRepository.php
api/src/Repository/DomainRepository.php
api/src/Repository/ProfileRepository.php
```

Responsabilités :

- ouvrir les connexions SQLite/PDO ;
- encapsuler les requêtes SQL ;
- retourner des tableaux métier normalisés ;
- ne pas formatter de réponse HTTP ou legacy.

### 2.2 Créer les services

Créer :

```text
api/src/Service/UserService.php
api/src/Service/DomainService.php
api/src/Service/AuthService.php
```

Responsabilités :

- validation métier ;
- orchestration des repositories ;
- masquage des champs sensibles ;
- exceptions métier typées via `ApiError`.

### 2.3 Migrer `getUsersByDomain`

Méthode legacy :

```text
getUsersByDomain
```

Entrée actuelle :

```json
{
  "domaine_id": 1
}
```

Service cible :

```text
UserService::listByDomain(int $domainId)
```

Réponse REST :

```json
{
  "data": {
    "users": [],
    "domains": [],
    "profiles": []
  },
  "error": null,
  "meta": {
    "domain_id": 1
  }
}
```

Réponse legacy temporaire :

```json
{
  "param1": [],
  "param2": [],
  "param3": []
}
```

### 2.4 Migrer `getUsersApi`

`getUsersApi` doit appeler le même service que `getUsersByDomain`.

Différence attendue :

- vérifier si le payload historique diffère ;
- documenter l'écart ;
- conserver un test manuel distinct.

### 2.5 Migrer `getUser`

Service cible :

```text
UserService::get(int $userId)
```

Route REST :

```text
GET /api/v1/users/{id}
```

Points de vigilance :

- supprimer `password` ;
- supprimer `salt` ;
- supprimer `password_requested_at` ;
- retourner `404` si l'utilisateur n'existe pas.

### 2.6 Migrer `postUser`

Service cible :

```text
UserService::create(array $payload)
```

Route REST :

```text
POST /api/v1/users
```

Validation minimale :

- `username` obligatoire ;
- `email` valide si présent ;
- `domain_id` entier ;
- `profile_id` entier ;
- pas de mot de passe en clair dans les logs.

### 2.7 Migrer `updateUserApi`

Service cible :

```text
UserService::update(int $userId, array $payload)
```

Route REST :

```text
PATCH /api/v1/users/{id}
```

La mise à jour doit être partielle. Les champs absents ne doivent pas écraser les valeurs existantes.

### 2.8 Migrer `delUser` et `delThisUser`

`facts` appelle `delThisUser`, tandis que `rcws` expose historiquement `delUser`.

Les deux méthodes doivent pointer vers :

```text
UserService::delete(int $userId)
```

Route REST :

```text
DELETE /api/v1/users/{id}
```

Réponse REST possible :

```json
{
  "data": {
    "deleted": true
  },
  "error": null,
  "meta": {}
}
```

Réponse legacy temporaire :

```json
{
  "deleted": true
}
```

### 2.9 Migrer `setGroups`

Service cible :

```text
UserService::setRolesForDomain(int $domainId, int $userId, array $roles)
```

Route REST :

```text
PUT /api/v1/domains/{id}/users/{userId}/roles
```

Valider strictement la liste des rôles autorisés.

## Phase 3 - Migrer l'authentification

### 3.1 Remplacer les clés codées en dur

Les clés doivent venir de :

```text
RCWS_API_KEYS='facts:<secret>,batch:<secret>'
RCWS_BEARER_TOKENS='client:<token>'
```

À terme, préférer un fichier de configuration non versionné ou un secret manager.

### 3.2 Migrer `testLog`

Service cible :

```text
AuthService::login(string $login, string $password, int $domainId)
```

Route REST :

```text
POST /api/v1/auth/login
```

Points de vigilance :

- ne jamais logger le mot de passe ;
- vérifier le hash existant avant toute évolution ;
- retourner une erreur uniforme en cas d'échec ;
- prévoir une transition vers `Authorization: Bearer`.

### 3.3 Ajouter `me` et `logout`

Routes cibles :

```text
GET  /api/v1/auth/me
POST /api/v1/auth/logout
```

Ces routes peuvent rester non implémentées tant que les clients legacy n'en ont pas besoin.

## Phase 4 - Migrer serverfacts

### 4.1 Cartographier les méthodes

Lister toutes les méthodes exposées par :

```text
legacy-rcws/serverfacts/controller/server.php
```

Créer un mapping explicite :

```text
api/src/Legacy/ServerFactsMethodMap.php
```

### 4.2 Créer les services facts

Créer :

```text
api/src/Service/SiteService.php
api/src/Service/DnsService.php
api/src/Service/HostService.php
api/src/Service/CertificateService.php
api/src/Service/OsService.php
api/src/Service/ToolService.php
api/src/Service/ActionService.php
```

### 4.3 Routes REST cibles

```text
GET    /api/v1/facts/sites
POST   /api/v1/facts/sites
GET    /api/v1/facts/sites/{id}
PATCH  /api/v1/facts/sites/{id}
DELETE /api/v1/facts/sites/{id}
GET    /api/v1/facts/dns
GET    /api/v1/facts/hosts
GET    /api/v1/facts/certificates
GET    /api/v1/facts/os
GET    /api/v1/facts/tools
GET    /api/v1/facts/actions
```

## Phase 5 - Migrer media, stats et utils

### 5.1 servermedia

Créer :

```text
MediaService
MovieService
BookService
PodcastService
MediaSearchService
```

Routes cibles :

```text
GET    /api/v1/media/movies
GET    /api/v1/media/movies/{id}
PATCH  /api/v1/media/movies/{id}
GET    /api/v1/media/books
POST   /api/v1/media/books
PATCH  /api/v1/media/books/{id}
DELETE /api/v1/media/books/{id}
GET    /api/v1/media/podcasts
GET    /api/v1/media/search
```

### 5.2 serverstats

Créer :

```text
StatsService
CounterRepository
```

Routes cibles :

```text
GET  /api/v1/stats/counters
POST /api/v1/stats/counters
GET  /api/v1/stats/counters/all
```

### 5.3 serverutils

Découper les responsabilités :

- chiffrement ;
- upload ;
- zip/unzip ;
- génération PDF ;
- listes transverses.

Ne pas exposer les utilitaires dangereux en REST sans authentification forte et contrôle d'autorisation.

## Phase 6 - Tests et validation

### 6.1 Tests manuels minimaux par méthode

Pour chaque méthode migrée :

```bash
curl -i \
  -X POST \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'methode=getUsersByDomain' \
  --data-urlencode 'param={"domaine_id":1}' \
  --data-urlencode 'apikey=<VOTRE_CLE_API>' \
  http://127.0.0.1:8080/serveruser/index.php
```

Et son équivalent REST :

```bash
curl -i \
  -H 'Accept: application/json' \
  -H 'X-API-Key: <VOTRE_CLE_API>' \
  http://127.0.0.1:8080/api/v1/domains/1/users
```

### 6.2 Tests automatisés

Ajouter progressivement :

- tests unitaires des parsers legacy ;
- tests unitaires des services ;
- tests d'intégration avec SQLite de test ;
- tests de non-régression des formats legacy.

### 6.3 Critères d'acceptation

Une méthode est considérée migrée si :

- elle n'utilise plus de dispatch dynamique ;
- elle passe par un service cible ;
- la route legacy fonctionne ;
- la route REST fonctionne si elle existe ;
- les champs sensibles sont masqués ;
- les erreurs ont un code HTTP cohérent ;
- la méthode est documentée dans `openapi.yaml` si elle est exposée en REST.

## Phase 7 - Décommissionnement legacy

Quand tous les clients auront migré :

1. désactiver les logs legacy pour vérifier l'absence d'usage ;
2. annoncer une date de retrait ;
3. retourner `410 Gone` sur les endpoints legacy pendant une période courte ;
4. supprimer `legacy-rcws/` ;
5. supprimer les contrôleurs legacy ;
6. conserver uniquement les routes REST et les services.

## Ordre de travail recommandé

1. Finaliser `serveruser` car c'est le chemin critique de `facts`.
2. Migrer `getUsersByDomain` et `getUsersApi`.
3. Migrer `postUser`, `updateUserApi`, `delThisUser`, `delUser`.
4. Migrer `setGroups`.
5. Migrer `testLog`.
6. Importer puis découper `serverfacts`.
7. Migrer `serverstats`.
8. Migrer `servermedia`.
9. Migrer `serverutils` en dernier, avec revue sécurité dédiée.

## Risques principaux

- divergence entre format legacy et format REST ;
- oubli de `delThisUser`, utilisé par le front `facts` ;
- fuite de champs sensibles ;
- dépendance durable aux globals historiques ;
- comportement différent entre SQLite de test et données réelles ;
- logs contenant des secrets ;
- erreurs HTML ou XML résiduelles côté API.

## Prochaine action concrète

Brancher `DomainService::listUsersByDomain()` sur les données importées de `serveruser/model/serverGetUsersByDomain.php`, tout en conservant la réponse legacy :

```json
{
  "param1": [],
  "param2": [],
  "param3": []
}
```

et la réponse REST :

```json
{
  "data": {
    "users": [],
    "domains": [],
    "profiles": []
  },
  "error": null,
  "meta": {
    "domain_id": 1
  }
}
```
