diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md new file mode 100644 index 0000000..1ddd203 --- /dev/null +++ b/.claude/rules/architecture.md @@ -0,0 +1,66 @@ +# Architecture — Modular Monolith DDD + +## Principe fondamental +Le **backend est la source de verite unique**. Il dicte : +- Quels modules sont actifs (`config/modules.php`) +- L'organisation de la sidebar (`config/sidebar.php`, decouplee des modules) + +Le frontend scanne `modules/*/` comme layers Nuxt et consomme l'API pour la navigation. Il ne decide rien. + +## Endpoints API cles + +- `GET /api/version` (public) — version de l'app +- `GET /api/modules` (public) — IDs des modules actifs +- `GET /api/sidebar` (public) — sections filtrees par modules actifs + `disabledRoutes` (items dont le module owner est inactif) +- `GET /api/me` (auth) — user courant + +## Arborescence minimale (detail complet : @README.md) + +``` +src/ + Shared/ # Noyau technique partage (Domain/, Application/Bus/, Infrastructure/ApiPlatform/) + Module/ + Core/ # Module obligatoire (auth, users) + CoreModule.php # ID, LABEL, REQUIRED, permissions() + Domain/ Application/ Infrastructure/ + Commercial/ # Exemple d'autre module +frontend/ + app/ # Shell (layouts, middlewares) + shared/ # Code inter-modules (composables, stores, utils) + modules/ # Layers Nuxt auto-detectes + core/ commercial/ +``` + +## Declaration d'un module + +Chaque module expose un `*Module.php` avec : +- `ID` (snake_case, ex: `commercial`, `gestion_rh`) +- `LABEL` +- `REQUIRED` (bool) +- Methode statique `permissions()` retournant les RBAC du module + +## Activer / desactiver un module + +Editer uniquement `config/modules.php` (commenter la ligne). Cascade automatique : +1. `/api/modules` ne retourne plus l'ID +2. `/api/sidebar` filtre les items `module: ''` et supprime les sections vides +3. Middleware front `modules.global.ts` redirige toute navigation vers une route desactivee +4. Le code reste dans le bundle (layer auto-detecte) → reactivation instantanee sans rebuild + +## Reorganiser la sidebar + +Editer uniquement `config/sidebar.php`. Le code des modules n'est pas touche — seule la place des items change. Chaque item reference son module owner via la cle `module`. + +## Communication inter-modules + +**Interdit** : import direct d'une classe d'un autre module. +**Autorise** : +- Via `Shared/Domain/Contract/` (interfaces : `UserResolverInterface`, `TenantAwareInterface`...) +- Via domain events (`Shared/Domain/Event/DomainEventInterface`) + +## Migrations + +- **Par defaut** : `src/Module//Infrastructure/Doctrine/Migrations/` (namespace modulaire) +- **Exception** : les migrations d'initialisation critiques (setup user, RBAC, seed de base) vivent au namespace racine `DoctrineMigrations` dans `migrations/`. + - Raison : avec plusieurs `migrations_paths`, Doctrine Migrations 3.x trie par FQCN alphabetique et non par version timestamp → ordre incorrect entre namespaces sur base vide. + - A supprimer quand un `MigrationsComparator` custom ou un upgrade Doctrine resoudra le tri. diff --git a/.claude/rules/backend.md b/.claude/rules/backend.md new file mode 100644 index 0000000..40c6a05 --- /dev/null +++ b/.claude/rules/backend.md @@ -0,0 +1,55 @@ +# Backend — Regles PHP / Symfony / API Platform + +## Structure de fichier + +- Toujours `declare(strict_types=1);` en tete de tout fichier PHP +- PHP CS Fixer : regles Symfony + PSR-12 + strict types (commande : `make php-cs-fixer-allow-risky`) +- Commentaires (docblock, inline, bloc) **en francais** ; code (classes, methodes, variables) en anglais + +## API Platform (pas de controllers) + +- Toujours utiliser `#[ApiResource]` + Providers + Processors — pas de controllers Symfony classiques +- Routes prefixees `/api` (via `config/routes/api_platform.yaml`) +- Le login `/login_check` est **hors** prefix `/api` (nginx reecrit `REQUEST_URI` vers `/login_check`) +- **Exception** : si tu dois creer un controller custom sous `/api/`, mettre `priority: 1` sur `#[Route]` pour eviter le conflit avec API Platform `{id}` + +## Repositories + +- Interface : `*RepositoryInterface` dans `Domain/Repository/` +- Implementation Doctrine : `Doctrine*Repository` dans `Infrastructure/Doctrine/` +- Le domaine garde les attributs ORM (approche pragmatique) + +## RBAC (permissions) + +Format obligatoire : `module.resource[.subresource].action` en snake_case. +- Exemples : `core.users.view`, `commercial.clients.contacts.edit`, `core.audit_log.view` +- Declarees via la methode statique `permissions()` des `*Module.php` +- Synchronisation : `app:sync-permissions` +- Verification API Platform : `is_granted('module.resource.action')` +- Verification front : `usePermissions()` + +## Roles + +- Hierarchie dans `config/packages/security.yaml` : `ROLE_ADMIN`, `ROLE_USER` +- Le role ne remplace pas la permission RBAC — deux niveaux complementaires + +## Audit (obligatoire) + +- Toute entite metier (nouvelle ou existante) : `#[Auditable]` (de `Shared/Domain/Attribute/`) +- Champs sensibles (password, token, secret) : `#[AuditIgnore]` +- Audit ManyToMany : trace automatiquement `{fieldName: {added: [ids], removed: [ids]}}` — aucune action supplementaire +- Spec complete : @doc/audit-log.md + +## Serialization + +Pour embarquer une relation dans le JSON (au lieu d'un IRI Hydra), ajouter le groupe du parent sur les proprietes de l'entite cible. + +Exemple : pour qu'`User.profile` soit embarque au lieu d'un lien IRI sous le groupe `user:read`, annoter `Profile.$firstName` avec `#[Groups(['user:read'])]`. + +## Upload de fichiers + +- Valider cote serveur avec `$file->getMimeType()` — **jamais** `getClientMimeType()` (spoofable par le client) + +## PostgreSQL + +- Noms de colonnes toujours en **minuscules** dans le SQL brut (commun a tous les projets MALIO) diff --git a/.claude/rules/frontend.md b/.claude/rules/frontend.md new file mode 100644 index 0000000..6c34164 --- /dev/null +++ b/.claude/rules/frontend.md @@ -0,0 +1,69 @@ +# Frontend — Regles Nuxt 4 / Vue 3 / @malio/layer-ui + +## Base + +- TypeScript strict +- 4 espaces d'indentation +- Commentaires (JSDoc, inline, bloc) **en francais** ; code (variables, types) en anglais +- Chaque module front = un layer Nuxt auto-detecte (`frontend/modules/*/nuxt.config.ts` minimal) + +## Appels API + +- Toujours `useApi()` — jamais `$fetch`, `ofetch`, `axios` en direct +- `useApi()` gere : cookies JWT, erreurs, toasts i18n, parsing Hydra + +## Stores (Pinia) + +- `useAuthStore` pour l'authentification +- `useUiStore` pour l'etat UI global (sidebar, modales, etc.) +- Composables avec state singleton (refs module-level) : exposer une fonction `reset*()` et la rappeler au logout (ex: `useSidebar().resetSidebar()`) + +## Middlewares globaux + +- `auth.global.ts` protege les routes + charge la sidebar apres login +- `modules.global.ts` redirige si la route demandee est dans `disabledRoutes` + +## i18n et sidebar + +- Labels de sidebar = cles i18n `sidebar..*`, jamais du texte brut +- Le layout `default.vue` applique `t()` sur les labels retournes par `/api/sidebar` +- Traductions dans `frontend/i18n/locales/` + +## Composants formulaires — @malio/layer-ui obligatoire + +Tout champ de formulaire / filtre doit utiliser les composants `Malio*` plutot que `` / `` / `` / `