7.4 KiB
7.4 KiB
Lesstime
Application de gestion de projet. Monorepo Symfony 8 (API Platform 4) + Nuxt 4.
Stack
- Backend : PHP 8.4, Symfony 8.0, API Platform 4, Doctrine ORM, PostgreSQL 16
- Frontend : Nuxt 4 (SSR off / SPA), Vue 3, Pinia, Tailwind CSS, @malio/layer-ui, nuxt-toast, @nuxtjs/i18n, @nuxt/icon
- Auth : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login à
/login_check, cookieBEARER - Docker : PHP-FPM + Node 24, Nginx (port 8082), PostgreSQL (port 5435)
Structure
src/Entity/ # Entités Doctrine (User, Client, Project, Task, TaskStatus, TaskEffort, TaskPriority, TaskTag, TaskGroup, TimeEntry, GiteaConfiguration, ClientTicket, Notification, TaskDocument, BookStackConfiguration, TaskBookStackLink)
src/ApiResource/ # Ressources API Platform (si découplées des entités)
src/State/ # Providers et Processors API Platform (MeProvider, AppVersionProvider, ActiveTimeEntryProvider, UserPasswordHasherProcessor, TaskNumberProcessor, ClientTicket*Provider/Processor, NotificationProvider, Gitea*Provider, Gitea*Processor)
src/Service/ # Services métier (NotificationService)
src/Controller/ # Controllers custom Symfony (NotificationUnreadCountController, MarkAllReadController, UserAvatarController, TaskDocumentDownloadController)
src/Mcp/Tool/ # MCP tools organisés par domaine (Project/, Task/, TaskMeta/, TimeEntry/, Reference/)
src/Security/ # Authenticators custom (ApiTokenAuthenticator pour MCP HTTP)
src/Command/ # Commandes console (GenerateApiTokenCommand)
src/Repository/ # Repositories Doctrine
src/DataFixtures/ # Fixtures
config/ # Config Symfony (security, api_platform, lexik_jwt, nelmio_cors, doctrine)
config/jwt/ # Clés JWT (private.pem, public.pem)
migrations/ # Migrations Doctrine
docs/plans/ # Plans d'implémentation
docs/superpowers/ # Plans et specs superpowers
frontend/ # App Nuxt 4
frontend/pages/ # Pages (index, login, my-tasks, profile, projects, projects/[id], projects/[id]/groups, projects/[id]/archives, time-tracking, admin, portal/, portal/projects/[id], portal/projects/[id]/new-ticket)
frontend/layouts/ # Layouts (default, portal)
frontend/components/ # Composants Vue organisés en sous-dossiers (ui/, client/, project/, task/, user/, admin/, time-tracking/, client-ticket/, notification/)
frontend/composables/# Composables (useApi, useAppVersion, useNotifications, useClientTicketHelpers, useAvatarService)
frontend/stores/ # Stores Pinia (auth, ui, timer)
frontend/services/ # Services API (auth, clients, gitea, projects, tasks, task-statuses, task-efforts, task-groups, task-priorities, task-tags, users, time-entries, client-tickets, notifications, task-documents)
frontend/services/dto/ # Types TypeScript
frontend/i18n/locales/ # Fichiers de traduction (langDir résolu depuis i18n/)
Commandes
make start # Démarrer les containers
make stop # Arrêter les containers
make restart # Redémarrer les containers
make install # Install complet (composer, migrations, fixtures, build Nuxt)
make reset # Tout supprimer et réinstaller (supprime la BDD)
make dev-nuxt # Dev server Nuxt (hot reload, port 3002)
make shell # Shell dans le container PHP
make shell-root # Shell root dans le container PHP
make cache-clear # Vider le cache Symfony
make migration-migrate # Lancer les migrations
make fixtures # Charger les fixtures
make db-reset # Reset BDD + migrations + fixtures
make test # PHPUnit
make php-cs-fixer-allow-risky # Fix code style PHP
make logs-dev # Tail logs Symfony
Conventions
Commits
Format : <type>(<scope optionnel>) : <message> (espace avant et après :)
Types autorisés (minuscules) : build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test
Exemples : feat : add login page, fix(auth) : prevent null token crash
Backend
- Toujours
declare(strict_types=1)en haut des fichiers PHP - API Platform : utiliser ApiResource, Providers (
src/State/), Processors — pas de controllers - Routes API préfixées
/api(viaconfig/routes/api_platform.yaml) - Le login (
/login_check) est hors prefix/api, nginx réécritREQUEST_URIvers/login_check - PHP CS Fixer : règles Symfony + PSR-12 + strict types
- Rôles :
ROLE_ADMIN,ROLE_USER,ROLE_CLIENT— hiérarchie danssecurity.yaml User::getRoles()n'ajoute PASROLE_USERsi l'user aROLE_CLIENT(isolation)- PostgreSQL :
LIKEsur colonne JSON ne marche pas → utiliserroles::text LIKEvia native SQL - Controllers custom sous
/api/: ajouterpriority: 1sur#[Route]pour éviter le conflit avec API Platform{id} - Serialization : pour embarquer une relation (pas IRI), ajouter le groupe du parent aux propriétés de l'entité cible
- Upload fichiers : utiliser
$file->getMimeType()(pasgetClientMimeType()) pour valider côté serveur — nécessitesymfony/mime - Auth endpoints mixtes (ROLE_USER + ROLE_CLIENT) : utiliser
#[IsGranted('IS_AUTHENTICATED_FULLY')]au lieu d'un rôle spécifique
Frontend
- TypeScript strict
- Composable
useApi()pour tous les appels API (gère cookies, erreurs, toasts, i18n) - Stores Pinia :
useAuthStore(auth),useUiStore(ui),useTimerStore(timer) - Middleware global
auth.global.tsprotège les routes - Traductions dans
frontend/i18n/locales/(le module résoutlangDirdepuisi18n/) - 4 espaces d'indentation
- MalioSelect : options
{ label: string, value: number | null }uniquement — pas de string values, utiliser<select>natif pour les enums string - Portal client : pages sous
/portal/, layoutportal.vue, middleware redirigeROLE_CLIENT(sansROLE_ADMIN) vers/portal - Users admin+client : ne pas bloquer — vérifier
ROLE_CLIENT && !ROLE_ADMINpour les restrictions
MCP Server
- 22 tools MCP exposant projets, tâches, métadonnées, et time tracking
- Transport STDIO (local) :
docker exec -i php-lesstime-fpm php bin/console mcp:server - Transport HTTP (réseau) :
POST /_mcpavec headerAuthorization: Bearer <token> - Auth HTTP :
ApiTokenAuthenticatorvérifie le champapiTokende l'entitéUser - Générer un token :
php bin/console app:generate-api-token <username> - Config :
config/packages/mcp.yaml, firewall dansconfig/packages/security.yaml - Attribut
#[McpTool]doit être sur la classe (pas la méthode__invoke) pour la discovery SDK
Nginx
/_mcp→ Symfony (MCP HTTP transport)/api/*→ Symfony (via try_files + index.php)/api/login_check→ location exact match, fastcgi direct avec REQUEST_URI réécrit en/login_check/→ SPA frontend (frontend/dist/)
Docker
- Container PHP :
php-lesstime-fpm - Container Nginx :
nginx-lesstime - Container DB : PostgreSQL sur port 5435 (interne et externe)
- Config Docker :
docker/.env.docker(override local :docker/.env.docker.local) - Après modif nginx :
docker restart nginx-lesstime
Fixtures
- User admin :
admin/admin(ROLE_ADMIN) - Users internes :
alice/alice,bob/bob,charlie/charlie(ROLE_USER) - Users client :
client-liot/client(ROLE_CLIENT, client LIOT → SIRH),client-acme/client(ROLE_CLIENT, client ACME → CRM) - API token admin (dev) :
dev-mcp-token-for-testing-only-do-not-use-in-production