1dd7053ebd
Backend : sentry/sentry-symfony branché en prod uniquement (bundle prod-only, exceptions seules, 4xx ignorés, release = app.version), DSN via SENTRY_DSN (runtime, infra/prod/.env). Frontend : @sentry/nuxt chargé seulement si NUXT_PUBLIC_SENTRY_DSN présent (donc au build prod), upload des source maps gated sur les secrets. DSN front et secrets passés en build-args (Dockerfile) depuis les secrets Gitea (CI). Doc README (section Error tracking) + .env.example. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
322 lines
12 KiB
Markdown
322 lines
12 KiB
Markdown
# 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
|
|
- Error tracking centralisé back + front (GlitchTip / SDK Sentry, prod uniquement — voir « Error tracking »)
|
|
- Multi-langue (i18n)
|
|
|
|
## Prérequis
|
|
|
|
- Docker & Docker Compose
|
|
- Git
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# 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 |
|
|
| `SENTRY_DSN` | Error tracking **backend** → GlitchTip (projet `lesstime-api`) | _(vide)_ | ⚪ optionnel — active le tracking (voir « Error tracking ») |
|
|
|
|
> **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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
make migration-migrate # Lancer les migrations
|
|
make fixtures # Charger les fixtures
|
|
make db-reset # Reset BDD + migrations + fixtures (⚠️ supprime les données)
|
|
```
|
|
|
|
### Tests & Qualité
|
|
|
|
```bash
|
|
make test # PHPUnit
|
|
make php-cs-fixer-allow-risky # Fix code style PHP (Symfony + PSR-12)
|
|
```
|
|
|
|
### Installation complète
|
|
|
|
```bash
|
|
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)
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"lesstime": {
|
|
"command": "docker",
|
|
"args": ["exec", "-i", "php-lesstime-fpm", "php", "bin/console", "mcp:server"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Configuration réseau (HTTP)
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"lesstime": {
|
|
"type": "url",
|
|
"url": "http://<ip-serveur>:8082/_mcp",
|
|
"headers": {
|
|
"Authorization": "Bearer <api-token>"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Gestion des tokens API
|
|
|
|
```bash
|
|
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`).
|
|
|
|
```bash
|
|
# 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`**.
|
|
|
|
## Error tracking (GlitchTip)
|
|
|
|
Les erreurs **backend** et **frontend** sont remontées vers **GlitchTip** (instance auto-hébergée
|
|
interne, compatible SDK Sentry) qui les **groupe par projet** et compte les occurrences. Activé
|
|
**uniquement en prod** : en dev, sans DSN, le SDK est inerte (zéro impact). Ticket de référence :
|
|
INFRA #146.
|
|
|
|
### Pourquoi back et front se configurent différemment
|
|
|
|
| | Backend (Symfony) | Frontend (Nuxt SPA) |
|
|
|---|---|---|
|
|
| Nature | process PHP qui tourne en continu | fichiers JS/HTML **statiques** (`nuxt generate`) |
|
|
| Quand le DSN est lu | au **runtime** | **figé au build** (baké dans le JS) |
|
|
| Où mettre le DSN | `infra/prod/.env` (runtime) | **secrets Gitea** → build-args de la CI |
|
|
|
|
> Les erreurs partent **toujours vers GlitchTip**, jamais vers la CI. La CI ne sert qu'à *écrire*
|
|
> le DSN front dans le bundle au moment du build (il n'y a aucun process front en prod qui
|
|
> pourrait lire une variable d'environnement).
|
|
|
|
### Variables
|
|
|
|
**Backend — fichier `infra/prod/.env` du serveur** (chargé via `env_file`) :
|
|
```env
|
|
SENTRY_DSN=http://<clé>@glitchtip.interne:<port>/<id-projet-api>
|
|
```
|
|
|
|
**Frontend — secrets Gitea** (repo → Settings → Actions → Secrets), consommés par
|
|
`.gitea/workflows/build-docker.yml` :
|
|
|
|
| Secret Gitea | Rôle |
|
|
|---|---|
|
|
| `SENTRY_FRONT_DSN` | DSN du projet `lesstime-front` (public, baké dans le JS) |
|
|
| `SENTRY_URL` | URL de l'instance GlitchTip |
|
|
| `SENTRY_ORG` | slug de l'organisation GlitchTip |
|
|
| `SENTRY_FRONT_PROJECT` | slug du projet front |
|
|
| `SENTRY_AUTH_TOKEN` | token d'upload des **source maps** (vrai secret) |
|
|
|
|
> Sans source maps, seul `SENTRY_FRONT_DSN` est requis (les stacktraces front seront sur du JS
|
|
> minifié). Le build n'échoue pas si les autres secrets sont absents.
|
|
|
|
### Fichiers concernés
|
|
|
|
| Fichier | Rôle |
|
|
|---|---|
|
|
| `config/packages/sentry.yaml` | conf backend (prod-only, exceptions, 4xx ignorés, release = `app.version`) |
|
|
| `config/bundles.php` | `SentryBundle` enregistré `['prod' => true]` |
|
|
| `frontend/nuxt.config.ts` | module Sentry chargé **uniquement si DSN présent** + upload source maps |
|
|
| `frontend/sentry.client.config.ts` | init du SDK client (no-op si DSN vide) |
|
|
| `infra/prod/Dockerfile` | build-args front (`NUXT_PUBLIC_SENTRY_DSN`, `SENTRY_*`) |
|
|
| `.gitea/workflows/build-docker.yml` | injection des secrets Gitea en build-args |
|
|
|
|
### Activation (résumé)
|
|
|
|
1. Dans GlitchTip : créer les projets `lesstime-api` et `lesstime-front`, récupérer les 2 DSN
|
|
(+ un auth token pour les source maps).
|
|
2. Backend : ajouter `SENTRY_DSN` dans `infra/prod/.env` du serveur.
|
|
3. Frontend : ajouter les secrets Gitea ci-dessus.
|
|
4. Tagger une version (`v*`) → la CI build l'image avec le DSN front baké → `deploy.sh`.
|
|
|
|
## Licence
|
|
|
|
Propriétaire — Tous droits réservés.
|