Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/nginx-strangler/conf.d/routing.conf
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/v1/admin/roles {
proxy_pass http://server-nestjs;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# ── Routes par défaut (pour l'instant dans le legacy) ─────────────────────────
location /api/ {
proxy_pass http://server-legacy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,14 +427,14 @@ et `user` sont des pre-requis pour la Vague 3 (cluster, project-member).
- `GET /api/v1/admin/users` - Liste complete des utilisateurs (admin)
- `PATCH /api/v1/admin/users` - Modification utilisateurs (admin)

**Dependances sortantes** : `queries-index` (getMatchingUsers, getUsers), hooks (adminRole.upsert)
**Dependances sortantes** : `queries-index` (getMatchingUsers, getUsers), EventEmitter (`adminRole.upsert`)
**Dependances entrantes** : `project-member` (importe `logViaSession`), `utils/controller.ts` (importe `logViaSession`, `logViaToken`)

**Points d'attention** :
- Module critique : `logViaSession` et `logViaToken` sont consommes par le
systeme d'auth. Ces fonctions auront ete portees dans l'AuthGuard (Couche 0),
donc la dependance entrante est deja resolue
- Hooks utilises (adminRole.upsert) : necessite l'EventEmitter
- Evenement utilise (`adminRole.upsert`) : necessite `@nestjs/event-emitter`
- Pre-requis pour `project-member` (Vague 3)

**Estimation** : 2 jours
Expand All @@ -457,11 +457,11 @@ et `user` sont des pre-requis pour la Vague 3 (cluster, project-member).
- `GET /api/v1/admin/roles/member-counts` - Comptage des membres par role
- `DELETE /api/v1/admin/roles/:roleId` - Suppression d'un role

**Dependances sortantes** : `queries-index` (getAdminRoleById, listAdminRoles), hooks (adminRole.upsert, adminRole.delete)
**Dependances sortantes** : `queries-index` (getAdminRoleById, listAdminRoles), EventEmitter (`adminRole.upsert`, `adminRole.delete`)
**Dependances entrantes** : Aucune

**Points d'attention** :
- Valide le pattern hooks + EventEmitter dans un contexte admin
- Valide le pattern EventEmitter dans un contexte admin
- Permissions admin (bitmask) : valide `UserGuard`
- Queries a internaliser dans le module

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@

## 🎯 Progression globale

![Progress](https://progress-bar.dev/7/?title=modularisation&width=400)
![Progress](https://progress-bar.dev/24/?title=modularisation&width=400)

**~7%** complété (1/18 modules métier migrés, 5/75 routes)
**~24%** complété (4/18 modules métier migrés, 21/75 routes)

---

## 📊 Vue d'ensemble

| Statut | Nombre de modules | % du total |
|--------|-------------------|------------|
| ✅ Migré | 1 (ServiceChain) | ~6% |
| ✅ Migré | 4 (ServiceChain, Project, ProjectMembers, AdminRole) | ~22% |
| 🚧 En cours | 0 | 0% |
| 📅 Planifié | 17 | ~94% |
| 📅 Planifié | 14 | ~78% |
| ⏳ En attente de cartographie | 0 | 0% |

---
Expand Down Expand Up @@ -48,17 +48,107 @@ profitant de son isolement complet vis-à-vis du reste du codebase.
| `POST` | `/api/v1/service-chains/:id/retry` | `ManageSystem` |
| `POST` | `/api/v1/service-chains/validate/:id` | `ManageSystem` |

### Project — migré le 2026-05-28

Module cœur de gestion des projets (CRUD, secrets, bulk actions).
Regroupe les sous-modules `project-core`, `project-secrets` et `project-bulk`
découpés dans la cartographie Vague 4, implémentés ici en un seul module
unifié pour accélérer la migration.

- **Routes** : 7 (`/api/v1/projects/...`)
- **Auth** : Token (`x-dso-token`) + guards projet (`ProjectContextGuard`, `ProjectStatusGuard`, `ProjectLockedGuard`)
- **Validation** : Contrats Zod via `projectContract` de `@cpn-console/shared` avec `ZodValidationPipe`
- **Tests** : Controller + Service couverts (Vitest)

| Méthode | Route | Permission |
|---------|-------|------------|
| `GET` | `/api/v1/projects/data` | `Manage` (admin) |
| `GET` | `/api/v1/projects` | Authentifié |
| `POST` | `/api/v1/projects` | `ManageProjects` (admin) + type `human` |
| `POST` | `/api/v1/projects-bulk` | `Manage` (admin) |
| `GET` | `/api/v1/projects/:projectId` | Authentifié + projet |
| `GET` | `/api/v1/projects/:projectId/secrets` | Authentifié + projet (statut ≠ archived) |
| `PUT` | `/api/v1/projects/:projectId` | Authentifié + projet (statut ≠ archived) |

**Infrastructure créée/mise à jour** :
- `ProjectContextGuard` + `Project` decorator : chargement du projet par id/slug, résolution des permissions via bitmask
- `ProjectStatusGuard` + `@RequireProjectStatus()` : filtrage par statut du projet
- `ProjectLockedGuard` : protection contre les modifications de projets verrouillés
- `UserGuard` + `User` decorator : authentification token + injection du contexte utilisateur

**Différences avec le legacy** :
- Utilisation de `projectContract` de `@cpn-console/shared` pour les schemas de validation (cohérence client/serveur)
- ZodValidationPipe au lieu de la validation ts-rest implicite
- Les routes `secrets` et `bulk` sont fusionnées dans le module Project (pas de sous-modules séparés)
- Pas de `DELETE /api/v1/projects/:projectId` (archivage) dans cette version initiale

### ProjectMembers — migré le 2026-05-28

Module de gestion des membres projet (ajout, modification, suppression, liste).
Dédié aux routes `/api/v1/projects/:projectId/members/...`.

- **Routes** : 4 (`/api/v1/projects/:projectId/members/...`)
- **Auth** : Token (`x-dso-token`) + guards projet (`ProjectContextGuard`, `ProjectStatusGuard`, `ProjectLockedGuard`)
- **Validation** : Contrats Zod via `projectMemberContract` de `@cpn-console/shared`

| Méthode | Route | Permission |
|---------|-------|------------|
| `GET` | `/api/v1/projects/:projectId/members` | `ListMembers` (admin) |
| `POST` | `/api/v1/projects/:projectId/members` | `ManageMembers` (admin) + statut non archivé |
| `PATCH` | `/api/v1/projects/:projectId/members` | `ManageMembers` (admin) + statut non archivé |
| `DELETE` | `/api/v1/projects/:projectId/members/:userId` | `ManageMembers` (admin) ou auto-suppression + statut non archivé |

**Fichiers** :
- `src/modules/project/project-members.controller.ts`
- Méthodes ajoutées dans `src/modules/project/project.service.ts`

**Différences avec le legacy** :
- Recherche par email non disponible (nécessite le hook Keycloak `user.retrieveUserByEmail`, non migré)
- Retour de la liste complète des membres après chaque mutation (cohérence avec le legacy)
- Événements `projectMember.upsert` et `projectMember.delete` via EventEmitter (remplacement du hook `projectMember.upsert`/`projectMember.delete`)

### AdminRole — migré le 2026-06-12

Module de gestion des rôles administrateurs (CRUD + tri + comptage membres).
Dédié aux routes `/api/v1/admin/roles/...`.

- **Routes** : 5 (`/api/v1/admin/roles/...`)
- **Auth** : Guards admin (`AdminGuard`) + décorateur `@RequireAdminPermission()`
- **Validation** : Contrats Zod via `adminRoleContract` de `@cpn-console/shared` avec `ZodValidationPipe`
- **Tests** : Controller + Service couverts (Vitest)

| Méthode | Route | Permission |
|---------|-------|------------|
| `GET` | `/api/v1/admin/roles` | `ListRoles` |
| `POST` | `/api/v1/admin/roles` | `ManageRoles` |
| `PATCH` | `/api/v1/admin/roles` | `ManageRoles` |
| `GET` | `/api/v1/admin/roles/member-counts` | `ManageRoles` |
| `DELETE` | `/api/v1/admin/roles/:roleId` | `ManageRoles` |

**Infrastructure réutilisée** :
- `AdminGuard` + `@RequireAdminPermission()` : validation des permissions admin
- `AdminService` : validation bitmask + typage utilisateur

**Fichiers** :
- `src/modules/admin-role/admin-role.controller.ts`
- `src/modules/admin-role/admin-role.service.ts`
- `src/modules/admin-role/admin-role.utils.ts`

**Différences avec le legacy** :
- Déport de la logique de permissions côté NestJS plutôt que dans le legacy Fastify
- Comptage des membres via query Prisma dédiée au lieu d'agrégat

### Infrastructure transverse déployée

En support de cette migration, les éléments d'infrastructure suivants ont été
En support de ces migrations, les éléments d'infrastructure suivants ont été
créés :

- **AuthModule** (`infrastructure/auth/`) : `AuthService` (validation token
SHA256 via Prisma + bearer JWT Keycloak), `DsoTokenModule`,
`KeycloakJwtModule` et décorateur `@AuthUser()`
- **PermissionModule** (`infrastructure/permission/`) : `UserModule`
(`UserGuard`, `UserService`, `UserPolicy`) + `ProjectModule`
(`ProjectGuard`, `ProjectLoaderService`, `ProjectService`, `ProjectPolicy`)
SHA256 via Prisma) + `AdminPermissionGuard` + décorateur
`@RequireAdminPermission()`
- **Guards projet** (`infrastructure/auth/`) : `ProjectContextGuard`,
`ProjectStatusGuard`, `ProjectLockedGuard` + décorateurs `@Project()` et
`@RequireProjectStatus()`
- **Nginx strangler** : Reverse proxy configuré pour router les routes migrées
vers server-nestjs, le reste vers le legacy
- **Docker** : Build order corrigé (shared avant server-nestjs)
Expand All @@ -78,7 +168,7 @@ créés :

## 📅 Modules planifiés

> Ces informations seront affinées après la cartographie (fin S2)
> Ces informations seront affinées après la cartographie (fin S2).

### Sprint 3-4 (27 janvier - 9 février)
- **Module** : Auth
Expand Down Expand Up @@ -140,9 +230,9 @@ créés :
### Routes par statut

- **Total** : ~75 routes métier
- **Migrés** : 5 (~7%)
- **Migrés** : 21 (~28%)
- **En cours** : 0 (0%)
- **Restants** : ~70 (~93%)
- **Restants** : ~54 (~72%)

---

Expand All @@ -154,16 +244,17 @@ créés :
| 26/01/2026 | Fin de la cartographie (S2) |
| 27/01/2026 | Début modularisation Auth (S3) |
| 09/02/2026 | Fin modularisation Auth (S4) - 20% complété |
| 09/03/2026 | Point mi-parcours - 60% complété |
| 26/03/2026 | Migration ServiceChain (OpenCDS) finalisée — 1er module métier migré |
| 28/05/2026 | Migration du module **Project** (7 routes) — CRUD + secrets + bulk |
| 28/05/2026 | Migration du module **ProjectMembers** (4 routes) — membres projet |
| 12/06/2026 | Migration du module **AdminRole** (5 routes) — gestion des rôles admin |
| 06/04/2026 | Fin de modularisation - 100% complété |

---

## 📞 Contacts & Communication

### Canaux
- **Slack** : #backend-modularisation
- **Mattermost** : #backend-modularisation
- **Meeting hebdo** : Vendredi 16h (30min)
- **Lead technique** : @stephane.trebel

Expand All @@ -188,6 +279,19 @@ créés :

## 🔄 Historique des changements

### 2026-06-12
- ✅ Migration du module **AdminRole** — 5 routes : gestion des rôles admin (list, create, patch, member-counts, delete)
- ✅ Mise à jour du contrôleur pour utiliser `AdminGuard` + `@RequireAdminPermission()`
- ✅ Création des utilitaires de mapping `AdminRole` Prisma → `@cpn-console/shared`

### 2026-05-28
- ✅ Migration du module **Project** — 7 routes : CRUD projets, secrets, bulk actions, export CSV
- ✅ Migration du module **ProjectMembers** — 4 routes : membres projet (list, add, patch, delete)
- ✅ Création des guards projet : `ProjectContextGuard`, `ProjectStatusGuard`, `ProjectLockedGuard`
- ✅ Création des décorateurs `@Project()` et `@RequireProjectStatus()`
- ✅ Utilisation de `projectContract` de `@cpn-console/shared` pour la validation Zod
- ✅ Build et lint verts (server-nestjs)

### 2026-04-09
- ✅ Migration du module **ServiceChain (OpenCDS)** — 5 routes, proxy HTTP vers API externe
- ✅ Création de l'**AuthModule** (infrastructure/auth/) : auth par token `x-dso-token`
Expand All @@ -210,5 +314,5 @@ créés :

---

**Version du fichier** : 1.1
**Version du fichier** : 1.2
**Responsable de mise à jour** : Lead technique backend
2 changes: 2 additions & 0 deletions apps/server-nestjs/src/main.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common'
import { EventEmitterModule } from '@nestjs/event-emitter'
import { ScheduleModule } from '@nestjs/schedule'
import { AdminRoleModule } from './modules/admin-role/admin-role.module'
import { DeploymentModule } from './modules/deployment/deployment.module'
import { HealthzModule } from './modules/healthz/healthz.module'
import { KeycloakModule } from './modules/keycloak/keycloak.module'
Expand All @@ -18,6 +19,7 @@ import { VersionModule } from './modules/version/version.module'
SystemSettingsModule,
ServiceChainModule,
ProjectModule,
AdminRoleModule,
DeploymentModule,
VersionModule,
],
Expand Down
58 changes: 58 additions & 0 deletions apps/server-nestjs/src/modules/admin-role/admin-role.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { AdminRole } from '@cpn-console/shared'
import { adminRoleContract } from '@cpn-console/shared'
import { Body, Controller, Delete, Get, HttpCode, Inject, Param, ParseUUIDPipe, Patch, Post, UseGuards } from '@nestjs/common'
import { RequireAdminPermission } from '../infrastructure/permission/user/user-admin-permission.decorator'
import { UserGuard } from '../infrastructure/permission/user/user.guard'
import { ZodValidationPipe } from '../infrastructure/pipe/zod-validation.pipe'
import { AdminRoleService } from './admin-role.service'

@Controller('api/v1/admin/roles')
export class AdminRoleController {
constructor(
@Inject(AdminRoleService) private readonly adminRoleService: AdminRoleService,
) {}

@Get('')
@UseGuards(UserGuard)
@RequireAdminPermission('ListRoles')
async listAdminRoles(): Promise<AdminRole[]> {
return this.adminRoleService.list()
}

@Post('')
@HttpCode(201)
@UseGuards(UserGuard)
@RequireAdminPermission('ManageRoles')
async createAdminRole(
@Body(new ZodValidationPipe(adminRoleContract.createAdminRole.body)) body: typeof adminRoleContract.createAdminRole.body._type,
): Promise<AdminRole> {
return this.adminRoleService.create(body)
}

@Patch('')
@HttpCode(200)
@UseGuards(UserGuard)
@RequireAdminPermission('ManageRoles')
async patchAdminRoles(
@Body(new ZodValidationPipe(adminRoleContract.patchAdminRoles.body)) body: typeof adminRoleContract.patchAdminRoles.body._type,
): Promise<AdminRole[]> {
return this.adminRoleService.patch(body)
}

@Get('member-counts')
@UseGuards(UserGuard)
@RequireAdminPermission('ManageRoles')
async adminRoleMemberCounts(): Promise<Record<string, number>> {
return this.adminRoleService.memberCounts()
}

@Delete(':roleId')
@HttpCode(204)
@UseGuards(UserGuard)
@RequireAdminPermission('ManageRoles')
async deleteAdminRole(
@Param('roleId', ParseUUIDPipe) roleId: string,
): Promise<void> {
await this.adminRoleService.delete(roleId)
}
}
13 changes: 13 additions & 0 deletions apps/server-nestjs/src/modules/admin-role/admin-role.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common'
import { AuthModule } from '../infrastructure/auth/auth.module'
import { InfrastructureModule } from '../infrastructure/infrastructure.module'
import { AdminRoleController } from './admin-role.controller'
import { AdminRoleService } from './admin-role.service'

@Module({
imports: [InfrastructureModule, AuthModule],
controllers: [AdminRoleController],
providers: [AdminRoleService],
exports: [AdminRoleService],
})
export class AdminRoleModule {}
Loading
Loading