matthieu 8313c759c6
Auto Tag Develop / tag (push) Successful in 9s
Migration modular monolith DDD (0.1 → 3.3) (#17)
## Migration modular monolith DDD — Lesstime (0.1 → 3.3)

Cette MR regroupe l'intégralité de la refonte en monolithe modulaire (strangler progressif, additif). Elle remplace les MR stackées de Phase 1 (#12–#16), désormais incluses ici.

**Ne pas merger avant validation fonctionnelle** : branche destinée à être testée telle quelle.

### Périmètre — 9 modules sous `src/Module/`
| Phase | Module | Contenu |
|------|--------|---------|
| 0.1 | (socle) | infrastructure modulaire, `ModuleInterface`, mapping Doctrine par module |
| 0.2 | (socle front) | auto-détection des layers Nuxt sous `frontend/modules/*` |
| 1.1 | **Core** | Identité (User/Auth), Notifications, Notifier |
| 1.2 | Core | RBAC fin (permissions `module.resource.action`, sidebar gated) |
| 1.3 | Core | Audit log (`#[Auditable]`, listener, provider DBAL) |
| 2.1 | **TimeTracking** | TimeEntry + MCP + export |
| 2.2 | **ProjectManagement** | cœur métier Projets/Tâches + 38 MCP tools |
| 2.3 | **Absence** | demandes, soldes, policies, justificatifs |
| 2.4 | **Directory** | Clients (migrés) + **Prospects** (nouveau, conversion → Client) |
| 2.5 | **Mail** | intégration IMAP OVH + liens tâches |
| 2.6 | **Integration** | Gitea / BookStack / Zimbra / Share |
| 3.1 | **Reporting** | rapports transverses (DBAL read-only, 0 import inter-module) |
| 3.2 | **ClientPortal** | portail client (ROLE_CLIENT cloisonné, tickets, notifications) |
| 3.3 | (finition) | nettoyage legacy — `src/Entity` vide, app 100% modulaire |

### Architecture
- Découplage inter-modules par **contrats** (`UserInterface`, `ProjectInterface`, `TaskInterface`, `TaskTagInterface`, `ClientInterface`, `ClientTicketInterface`, `LeaveProfileInterface`) + `resolve_target_entities` 100% modulaire (aucune cible legacy).
- Repositories : interface `Domain/Repository` + implémentation `Infrastructure/Doctrine`, bindées.
- Reporting en DBAL read-only pur (aucun import d'entité d'un autre module).
- Chaque migration de module : déplacement à comportement préservé (API publique et noms d'outils MCP inchangés), migrations **additives** uniquement (zéro destructif).

### Sécurité
- ROLE_CLIENT cloisonné : un utilisateur client n'accède qu'à `/portal` et à ses propres tickets (filtrés par `allowedProjects`), interdit sur toute l'API interne.
- Correctif : interdiction pour un client de créer un lien vers le partage SMB (upload uniquement).

### QA non-régression (branche reconstruite from scratch)
- Migrations from scratch + fixtures : OK.
- Compilation dev + prod : OK.
- **180 tests PHPUnit verts**, php-cs-fixer clean, ~96 routes, **66 outils MCP** tous sous `App\Module\*`.
- Smoke test runtime multi-rôles (admin / ROLE_USER / ROLE_CLIENT) : 44 vérifications HTTP, **0 écart**, cloisonnement client étanche.
- Build Nuxt OK, 9 layers, 0 import legacy résiduel.

### Points à arbitrer (hors périmètre de cette migration)
- Durcissement MCP/IDOR pré-existant (`userId` explicite sans scoping sur certains tools TimeTracking/Absence/TaskDocument) — ticket dédié recommandé.
- Validation fonctionnelle de **Prospect** et **ClientPortal** (conçus depuis les specs disque).
- **Harmonisation visuelle Malio finale** (3.3) — finition esthétique inter-modules laissée au PO.

---

## ⚠️ Déploiement / migration des données — à ne pas oublier

### 1. Resynchroniser les séquences PostgreSQL après tout import/restore de dump
Si la prod (ou tout environnement) est **montée depuis un dump** (`pg_restore` / `COPY`), les lignes sont chargées avec leurs `id` explicites **sans avancer les séquences** → au premier `INSERT` : `duplicate key value violates unique constraint "..._pkey"` (constaté en local sur `notification`, `task`, `time_entry`…).

À lancer **juste après chaque restore/import** :

```sql
DO $$
DECLARE r RECORD; maxid BIGINT; seq TEXT;
BEGIN
  FOR r IN SELECT table_name, column_name FROM information_schema.columns WHERE table_schema='public'
  LOOP
    seq := pg_get_serial_sequence(quote_ident(r.table_name), r.column_name);
    IF seq IS NOT NULL THEN
      EXECUTE format('SELECT COALESCE(MAX(%I),0) FROM %I', r.column_name, r.table_name) INTO maxid;
      PERFORM setval(seq, GREATEST(maxid,1), maxid > 0);
    END IF;
  END LOOP;
END $$;
```

> Ne concerne **pas** une prod qui tourne déjà (séquences avancées organiquement) — uniquement le cas restore/import. Idempotent, sans risque.

### 2. Fix dénormalisation des collections typées-contrat (code, inclus dans la branche)
Les relations **to-many** typées par une interface `Shared\Domain\Contract\*` (`TimeEntry::tags` → `TaskTagInterface`, `Task::collaborators` → `UserInterface`) étaient **indénormalisables par API Platform** (mono-valué OK via IRI, collection KO) → **tout POST/PATCH portant une telle collection renvoyait 400/500**. Corrigé par un dénormaliseur générique `ContractRelationDenormalizer` (réutilise `resolve_target_entities`, zéro couplage par-entité) + test fonctionnel de non-régression.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #17
2026-06-23 13:50:42 +00:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00
2026-03-08 19:47:19 +01:00

Lesstime

Application de gestion de projet avec suivi du temps et portail client.

Stack

Couche Technologies
Backend PHP 8.4, Symfony 8, API Platform 4, Doctrine ORM
Frontend Nuxt 4 (SPA), Vue 3, Pinia, Tailwind CSS
Base de données PostgreSQL 16
Auth JWT HTTP-only cookie (lexik/jwt-authentication-bundle)
Infrastructure Docker (PHP-FPM, Nginx, PostgreSQL)

Fonctionnalités

  • Gestion de projets et tâches (kanban, groupes, priorités, tags, efforts)
  • Suivi du temps (timer, calendrier, vue liste)
  • Portail client avec tickets (bug, amélioration, autre)
  • Gestion de documents (upload, prévisualisation, téléchargement)
  • Profil utilisateur avec avatar (crop circulaire)
  • Notifications temps réel
  • Intégration Gitea (issues, repos)
  • Intégration Mail IMAP (boîte partagée OVH, voir docs/mail-integration.md)
  • Serveur MCP pour assistants IA
  • Multi-langue (i18n)

Prérequis

  • Docker & Docker Compose
  • Git

Installation

# 1. Cloner le repo
git clone <url> && cd lesstime

# 2. Démarrer les containers
make start

# 3. Installation complète (composer, migrations, fixtures, build Nuxt)
make install

L'application est accessible sur http://localhost:8082.

Les valeurs par défaut du .env committé suffisent pour démarrer en local. Pour la prod (et pour activer la messagerie), surcharger les variables sensibles dans .env.local — voir « Variables d'environnement » ci-dessous.

Comptes de test (fixtures)

Utilisateur Mot de passe Rôle Détails
admin admin ROLE_ADMIN Administrateur
alice alice ROLE_USER Utilisateur interne
bob bob ROLE_USER Utilisateur interne
charlie charlie ROLE_USER Utilisateur interne
client-liot client ROLE_CLIENT Client LIOT (projet SIRH)
client-acme client ROLE_CLIENT Client ACME (projet CRM)

Variables d'environnement

Les variables sont définies dans .env (committé, valeurs par défaut pour le dev) et peuvent être surchargées dans .env.local (jamais committé). En prod, elles vont dans le .env du serveur (/var/www/lesstime/.env, voir infra/prod/.env.example).

Variable Rôle Défaut dev À fixer en prod
APP_SECRET Secret Symfony placeholder (hex 32)
JWT_PASSPHRASE Passphrase des clés JWT placeholder
DATABASE_URL Connexion PostgreSQL container db (host.docker.internal)
CORS_ALLOW_ORIGIN Origines CORS autorisées localhost (domaine prod)
ENCRYPTION_KEY Clé hex 32 bytes chiffrant les credentials IMAP/SMTP (feature mail) placeholder — doit rester stable, sinon les credentials mail stockés deviennent illisibles
LOCK_DSN Store de verrous Symfony pour la sync mail (anti-chevauchement) flock flock suffit

Messagerie : ENCRYPTION_KEY et LOCK_DSN sont introduites par l'intégration mail. Détails de config et cron de synchronisation : docs/mail-integration.md et docs/mail-cron-setup.md. Générer une clé : php -r "echo bin2hex(random_bytes(32));".

Commandes

Docker

make start              # Démarrer les containers
make stop               # Arrêter les containers
make restart            # Redémarrer les containers
make shell              # Shell dans le container PHP
make shell-root         # Shell root dans le container PHP

Développement

make dev-nuxt           # Dev server Nuxt (hot reload, port 3002)
make cache-clear        # Vider le cache Symfony
make logs-dev           # Tail logs Symfony
make mail-sync          # Synchroniser la boîte mail IMAP (voir docs/mail-cron-setup.md)

Base de données

make migration-migrate  # Lancer les migrations
make fixtures           # Charger les fixtures
make db-reset           # Reset BDD + migrations + fixtures (⚠️ supprime les données)

Tests & Qualité

make test                       # PHPUnit
make php-cs-fixer-allow-risky   # Fix code style PHP (Symfony + PSR-12)

Installation complète

make install            # Composer + migrations + fixtures + build Nuxt
make reset              # Tout supprimer et réinstaller (⚠️ supprime la BDD)

Architecture

src/
├── Entity/             # Entités Doctrine
├── ApiResource/        # Ressources API Platform (découplées)
├── State/              # Providers et Processors API Platform
├── Controller/         # Controllers custom Symfony
├── Service/            # Services métier
├── EventListener/      # Listeners Doctrine
├── Exception/          # Exceptions custom
├── Security/           # Authenticators custom
├── Repository/         # Repositories Doctrine
├── Command/            # Commandes console
├── DataFixtures/       # Fixtures
└── Mcp/Tool/           # MCP tools par domaine
    ├── Project/
    ├── Task/
    ├── TaskMeta/
    ├── TimeEntry/
    └── Reference/

frontend/
├── pages/              # Pages Nuxt (routing auto)
│   ├── portal/         # Pages portail client
│   └── projects/       # Pages projets
├── layouts/            # Layouts (default, portal)
├── components/         # Composants Vue
│   ├── ui/             # Composants génériques
│   ├── task/           # Tâches
│   ├── user/           # Utilisateur (avatar, etc.)
│   ├── project/        # Projets
│   ├── client/         # Clients
│   ├── client-ticket/  # Tickets client
│   ├── admin/          # Administration
│   ├── notification/   # Notifications
│   └── time-tracking/  # Suivi du temps
├── composables/        # Composables (useApi, useNotifications, etc.)
├── stores/             # Stores Pinia (auth, ui, timer)
├── services/           # Services API
│   └── dto/            # Types TypeScript
├── plugins/            # Plugins Nuxt
├── utils/              # Utilitaires
├── i18n/locales/       # Traductions
└── middleware/          # Middleware auth

config/                 # Config Symfony
migrations/             # Migrations Doctrine
docker/                 # Dockerfiles et config Nginx

Docker

Container Port Description
php-lesstime-fpm 3002 (dev Nuxt) PHP-FPM + Node 24
nginx-lesstime 8082 Nginx reverse proxy
PostgreSQL 5435 Base de données

Configuration : infra/dev/.env.docker (override local : infra/dev/.env.docker.local)

API

Toutes les routes API sont préfixées /api (API Platform).

  • Documentation auto-générée : http://localhost:8082/api
  • Auth : POST /login_check avec { username, password } → cookie JWT BEARER

Serveur MCP

Lesstime expose un serveur MCP (Model Context Protocol) permettant aux assistants IA d'interagir avec les données.

Tools disponibles (22)

Domaine Tools
Reference list-users, list-clients
Project list-projects, get-project, create-project, update-project
Task list-tasks, get-task, create-task, update-task, delete-task
TaskMeta list-statuses, list-priorities, list-efforts, list-tags, list-groups, create-group, update-group
TimeEntry list-time-entries, create-time-entry, update-time-entry, delete-time-entry

Configuration locale (STDIO)

{
  "mcpServers": {
    "lesstime": {
      "command": "docker",
      "args": ["exec", "-i", "php-lesstime-fpm", "php", "bin/console", "mcp:server"]
    }
  }
}

Configuration réseau (HTTP)

{
  "mcpServers": {
    "lesstime": {
      "type": "url",
      "url": "http://<ip-serveur>:8082/_mcp",
      "headers": {
        "Authorization": "Bearer <api-token>"
      }
    }
  }
}

Gestion des tokens API

docker exec -u www-data php-lesstime-fpm php bin/console app:generate-api-token <username>

Déploiement

La prod tourne en Docker : l'image est buildée par la CI Gitea sur push de tag v* (gitea.malio.fr/malio-dev/lesstime:<tag>), puis déployée par le script deploy.sh sur le serveur (dossier /var/www/lesstime, container lesstime-app).

# Sur le serveur, depuis /var/www/lesstime
sudo ./deploy.sh            # déploie la dernière image (latest)
sudo ./deploy.sh v0.4.2     # déploie une version précise

Le script active la maintenance, pull l'image, redémarre le container, lance les migrations et vide le cache. Guide complet (première installation, BDD, Nginx, JWT, rollback) : doc/deployment-docker.md.

Licence

Propriétaire — Tous droits réservés.

S
Description
No description provided
Readme 6 MiB
v0.3.18 Latest
2026-04-02 10:14:43 +00:00
Languages
PHP 61%
Vue 31.1%
TypeScript 6.8%
Dockerfile 0.3%
CSS 0.3%
Other 0.4%