Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0de63d3136 | |||
| 35f3feb59c | |||
| e0ac8e75be | |||
| 7cad0f933f | |||
| b8272eb43e | |||
| add6129ee5 | |||
| 746578c2cf | |||
| d3f1e95711 | |||
| 517aefcd9b |
@@ -40,3 +40,10 @@ DEFAULT_URI=http://localhost
|
||||
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||
###< nelmio/cors-bundle ###
|
||||
|
||||
###> sentry/sentry-symfony ###
|
||||
# Error tracking backend → GlitchTip (projet "inventory-api"). Prod only, vide => inerte.
|
||||
# À définir dans l'env de prod (PAS ici, pas de secret commité). Format :
|
||||
# SENTRY_DSN=http://<clé>@<host-ou-IP>:<port>/<id-projet>
|
||||
# SENTRY_DSN=
|
||||
###< sentry/sentry-symfony ###
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ jobs:
|
||||
run: |
|
||||
docker build \
|
||||
-f infra/prod/Dockerfile \
|
||||
--build-arg NUXT_PUBLIC_SENTRY_DSN="${{ secrets.INVENTORY_SENTRY_DSN_FRONT }}" \
|
||||
--build-arg SENTRY_URL="${{ secrets.SENTRY_URL }}" \
|
||||
--build-arg SENTRY_ORG="${{ secrets.SENTRY_ORG }}" \
|
||||
--build-arg SENTRY_PROJECT="${{ secrets.SENTRY_PROJECT }}" \
|
||||
--build-arg SENTRY_AUTH_TOKEN="${{ secrets.SENTRY_AUTH_TOKEN }}" \
|
||||
-t gitea.malio.fr/malio-dev/inventory:${{ gitea.ref_name }} \
|
||||
-t gitea.malio.fr/malio-dev/inventory:latest \
|
||||
.
|
||||
|
||||
@@ -2,18 +2,11 @@
|
||||
"mcpServers": {
|
||||
"inventory": {
|
||||
"type": "http",
|
||||
"url": "http://inventory.malio-dev.fr/_mcp",
|
||||
"url": "https://inventory.malio-dev.fr/_mcp",
|
||||
"headers": {
|
||||
"X-Profile-Id": "admin-default-profile",
|
||||
"X-Profile-Password": "A123"
|
||||
}
|
||||
},
|
||||
"lesstime": {
|
||||
"type": "http",
|
||||
"url": "http://project.malio-dev.fr/_mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer b355c6cbf27d2a86d7eba1c3132c99bb3133f94cfd9e9243ffcc3c5ae1dc82c8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,434 +0,0 @@
|
||||
# Review complète — Projet Inventory
|
||||
|
||||
> Audit éducatif du projet. Chaque point explique le problème, pourquoi c'est un problème, et comment le corriger.
|
||||
> Document généré le 2026-06-11 (branche `develop`, v1.9.47).
|
||||
> Complément de `docs/REVIEW_ARCHITECTURE.md` (2026-03-23) — les findings d'architecture n'y sont pas dupliqués, seul leur **statut** est mis à jour ici (§4).
|
||||
|
||||
---
|
||||
|
||||
## Table des matières
|
||||
1. [Sécurité](#1-sécurité)
|
||||
2. [Bugs fonctionnels](#2-bugs-fonctionnels)
|
||||
3. [Code mort et violations des règles projet](#3-code-mort-et-violations-des-règles-projet)
|
||||
4. [Dette d'architecture — suivi REVIEW_ARCHITECTURE.md](#4-dette-darchitecture--suivi-review_architecturemd)
|
||||
5. [Documentation et configuration](#5-documentation-et-configuration)
|
||||
6. [Frontend et UX](#6-frontend-et-ux)
|
||||
7. [CI/CD et dépendances](#7-cicd-et-dépendances)
|
||||
8. [Hygiène git](#8-hygiène-git)
|
||||
9. [Bonnes pratiques à retenir](#9-bonnes-pratiques-à-retenir)
|
||||
|
||||
---
|
||||
|
||||
## 1. Sécurité
|
||||
|
||||
### 1.1 CRITIQUE — Credentials de production commités dans `.mcp.json`
|
||||
|
||||
**Fichier :** `.mcp.json` (tracké dans git, **pas** dans `.gitignore`)
|
||||
|
||||
Le fichier contient le mot de passe du profil admin MCP de production (`X-Profile-Password: A123` pour `inventory.malio-dev.fr`) **et** un bearer token Lesstime valide (`project.malio-dev.fr`). Toute personne ayant accès au dépôt (ou à son historique, même après suppression du fichier) peut s'authentifier sur les deux systèmes de production.
|
||||
|
||||
**Pourquoi c'est grave :** un secret commité reste dans l'historique git pour toujours. Le token Lesstime donne accès à tout le système de gestion de projet (tâches, temps, absences). Le mot de passe `A123` est en plus trivial.
|
||||
|
||||
**Correction :**
|
||||
1. **Révoquer/changer** les deux secrets (mot de passe du profil MCP + régénérer le token Lesstime) — c'est l'étape la plus importante, supprimer le fichier ne suffit pas.
|
||||
2. `git rm --cached .mcp.json` + ajouter `.mcp.json` au `.gitignore`.
|
||||
3. Conserver un `.mcp.json.example` avec des placeholders.
|
||||
|
||||
### 1.2 CRITIQUE — `create_test_user.php` tracké à la racine avec credentials admin en clair
|
||||
|
||||
**Fichier :** `create_test_user.php:16,59-61` (tracké dans git)
|
||||
|
||||
Script de debug qui crée un compte `ROLE_ADMIN` avec `admin@admin.com` / `admin123` (hardcodé et affiché en clair sur stdout), connexion PDO brute `root`/`root`. Posé à la racine du projet, il est embarquable dans une image de prod et exécutable partout où `vendor/` existe.
|
||||
|
||||
**Pourquoi c'est grave :** si ce script tourne (ou a tourné) en production, il existe un compte admin avec un mot de passe devinable en 3 essais. Les credentials sont aussi dans l'historique git.
|
||||
|
||||
**Correction :** supprimer le fichier du dépôt (`git rm`), vérifier **en prod** qu'aucun profil `admin@admin.com` actif n'existe. Le besoin légitime (seed d'un admin de dev) est déjà couvert par `fixtures/` ou peut devenir une commande Symfony `app:create-admin` qui demande le mot de passe en argument.
|
||||
|
||||
### 1.3 IMPORTANT — Mot de passe de la base de prod hardcodé dans 9 scripts trackés
|
||||
|
||||
**Fichiers :** `scripts/check-prod-values.php`, `scripts/fix-prod-all.php`, `scripts/restore-custom-field-values.php`, `scripts/migrate-orphaned-custom-fields.php`, `scripts/check-prod-audit-dates.php`, `scripts/check-prod-missing-piece-cfs.php`, `scripts/check-prod-orphaned-detail.php`, `scripts/fix-prod-recreate-and-migrate.php`, `scripts/verify-prod-health.php`
|
||||
|
||||
Neuf scripts de réparation one-shot contiennent le couple `ferme_user`/`fermerecette` en dur — des credentials de base de données de production, dans le dépôt.
|
||||
|
||||
**Pourquoi c'est grave :** même problème que 1.1 — secret en clair dans l'historique. En plus ces scripts contournent Doctrine et l'audit : les exécuter par erreur modifie la prod sans trace.
|
||||
|
||||
**Correction :** changer le mot de passe PG concerné, puis archiver/supprimer ces scripts (ils ont déjà servi). S'ils doivent rester, lire les credentials depuis l'environnement (`getenv('DATABASE_URL')`) et les déplacer dans `_archives/` (déjà gitignoré).
|
||||
|
||||
### 1.4 IMPORTANT — Aucune limite de taille d'upload (ni applicative, ni infra)
|
||||
|
||||
**Fichiers :** `src/State/DocumentUploadProcessor.php:55-116`, `src/Controller/CommentController.php:104-137`, `infra/dev/php.ini`, `infra/prod/nginx.conf`
|
||||
|
||||
Aucun des deux chemins d'upload ne vérifie `$file->getSize()`. Côté infra : `php.ini` ne définit ni `upload_max_filesize` ni `post_max_size` (défauts PHP : 2 Mo / 8 Mo), et `nginx.conf` prod n'a pas de `client_max_body_size` (défaut nginx : **1 Mo**).
|
||||
|
||||
**Pourquoi c'est un problème (double) :**
|
||||
- *Fonctionnel* : en prod, tout upload > 1 Mo est probablement rejeté par nginx avec une erreur 413 brute (non gérée par le front) — alors que l'app est censée stocker des PDF techniques.
|
||||
- *Sécurité* : aucune limite **choisie** n'existe ; le jour où quelqu'un monte les limites infra "pour faire passer un gros PDF", plus rien ne protège le disque (un `ROLE_VIEWER` peut uploader via les commentaires, cf. 1.9).
|
||||
|
||||
**Correction :** décider d'une limite métier (ex. 50 Mo), puis l'appliquer aux 3 niveaux :
|
||||
1. Check applicatif `if ($file->getSize() > self::MAX_UPLOAD_BYTES)` dans les deux chemins (erreur 400 propre).
|
||||
2. `upload_max_filesize = 50M` / `post_max_size = 55M` dans `infra/dev/php.ini` **et** l'image prod.
|
||||
3. `client_max_body_size 55m;` dans `infra/prod/nginx.conf`.
|
||||
|
||||
### 1.5 MOYEN — Garde anti path-traversal incomplet dans `DocumentStorageService`
|
||||
|
||||
**Fichier :** `src/Service/DocumentStorageService.php:28-42`
|
||||
|
||||
`getAbsolutePath()` vérifie `str_contains($relativePath, '..')` puis compare `realpath()` au répertoire de stockage — mais `realpath()` renvoie `false` pour un fichier inexistant, donc le second contrôle est **sauté** dans ce cas. Un chemin absolu (`/etc/passwd`) passe le premier contrôle (pas de `..`) : `$this->storageDir.'/'.'/etc/passwd'`… ne résout pas vers `/etc/passwd`, mais un chemin via symlink dans le storage le pourrait. L'exploitation exige d'écrire `document.path` en base (pas d'input direct utilisateur), donc le risque actuel est faible — c'est du **hardening**.
|
||||
|
||||
**Correction :** valider sur le répertoire parent, qui existe toujours :
|
||||
|
||||
```php
|
||||
$absolutePath = $this->storageDir.'/'.$relativePath;
|
||||
$realParent = realpath(dirname($absolutePath));
|
||||
if (false === $realParent || !str_starts_with($realParent.'/', realpath($this->storageDir).'/')) {
|
||||
throw new RuntimeException(sprintf('Path traversal detected: "%s"', $relativePath));
|
||||
}
|
||||
```
|
||||
|
||||
### 1.6 MOYEN — Pas de protection CSRF : `SameSite=Lax` est la seule barrière
|
||||
|
||||
**Fichiers :** `config/packages/framework.yaml:8`, `config/packages/nelmio_cors.yaml`
|
||||
|
||||
L'auth est par cookie de session, et aucun endpoint d'écriture ne vérifie de token CSRF ni de header custom. La protection repose à 100 % sur `cookie_samesite: lax` (et le CORS pour les lectures cross-origin). C'est la posture courante pour une SPA en 2026, mais c'est une **défense à un seul étage** : un navigateur ancien/exotique ou une config future en sous-domaine partagé la ferait tomber. À noter : `csrfToken` existe dans `nuxt.config.ts:59` mais n'est branché nulle part (config morte, cf. 3.1).
|
||||
|
||||
**Correction (peu coûteuse) :** exiger un header `X-Requested-With: XMLHttpRequest` sur les méthodes non-GET du firewall `api` (un listener de 15 lignes) — un formulaire HTML cross-site ne peut pas envoyer ce header. L'ajouter dans `useApi.ts` côté front. Supprimer le `csrfToken` mort.
|
||||
|
||||
### 1.7 MOYEN — `session_fixation_strategy: none` désactive la protection globalement
|
||||
|
||||
**Fichier :** `config/packages/security.yaml:5`, mitigé par `src/Controller/SessionProfileController.php:96`
|
||||
|
||||
Le choix est documenté (le `migrate` par défaut casse les requêtes concurrentes de la SPA) et le login appelle bien `$session->migrate(true)` manuellement — le flux actuel est correct. Le risque est **futur** : tout nouveau chemin d'authentification (reset de mot de passe, impersonation…) n'aura pas la régénération d'ID de session, silencieusement.
|
||||
|
||||
**Correction :** garder le réglage, mais l'encadrer : un test fonctionnel qui vérifie que l'ID de session change au login (échouera si quelqu'un retire le `migrate(true)`), et un commentaire dans `SessionProfileController` pointant vers `security.yaml`.
|
||||
|
||||
### 1.8 MOYEN — MCP HTTP : mot de passe en clair dans les headers à chaque requête
|
||||
|
||||
**Fichier :** `src/Mcp/Security/McpHeaderAuthenticator.php:43-44`, `infra/prod/nginx-proxy.conf`
|
||||
|
||||
Chaque requête MCP porte `X-Profile-Password` en clair. Les headers transitent par le proxy nginx et peuvent finir dans des logs (proxy, APM, outils de debug). Le rate-limiting et le hash côté serveur sont bien faits, mais le secret circule en permanence — et l'URL configurée dans `.mcp.json` est en **`http://`** (pas de TLS).
|
||||
|
||||
**Correction :** passer à un token d'API dédié (longue chaîne aléatoire, stockée hashée, comparée via le hasher existant), transmis en `Authorization: Bearer` — comme le fait déjà Lesstime. Et servir `/_mcp` uniquement en HTTPS.
|
||||
|
||||
### 1.9 MINEUR — Création de commentaires + upload de fichiers ouverte à `ROLE_VIEWER`
|
||||
|
||||
**Fichier :** `src/Controller/CommentController.php:33`
|
||||
|
||||
Convention du projet : lecture = `ROLE_VIEWER`, écriture = `ROLE_GESTIONNAIRE`. La création de commentaire (avec pièces jointes !) est la seule écriture accessible aux viewers. Si c'est un choix métier (« tout le monde peut commenter »), OK — mais combiné à 1.4, un compte en lecture seule peut remplir le disque.
|
||||
|
||||
**Correction :** confirmer le choix métier et le documenter dans `docs/BACKEND.md` ; appliquer la limite de taille de 1.4 dans tous les cas.
|
||||
|
||||
### 1.10 MINEUR — `download()` sans les headers de protection de `serve()`
|
||||
|
||||
**Fichier :** `src/Controller/DocumentServeController.php:105-116`
|
||||
|
||||
`serve()` envoie `X-Content-Type-Options: nosniff` + `Content-Security-Policy: sandbox` (très bien) ; `download()` n'envoie ni l'un ni l'autre pour les fichiers disque. La disposition `attachment` protège déjà beaucoup, mais l'asymétrie est gratuite.
|
||||
|
||||
**Correction :** copier les deux headers dans `download()`.
|
||||
|
||||
### 1.11 MINEUR — Pagination max très élevée sur certaines ressources
|
||||
|
||||
**Fichiers :** `src/Entity/Constructeur.php:47` (2000), `src/Entity/ConstructeurCategorie.php:43` (1000), `src/Entity/Document.php:55` (500)
|
||||
|
||||
Un `?itemsPerPage=2000` charge 2000 entités + sérialisation en une requête. Pour des catalogues internes c'est sans doute volontaire (dropdowns sans pagination), mais c'est aussi un vecteur de charge facile.
|
||||
|
||||
**Correction :** vérifier ce que le front demande réellement et redescendre au besoin réel (ou documenter pourquoi 2000).
|
||||
|
||||
### 1.12 MINEUR — `infra/dev/.env.docker.local` tracké malgré le `.gitignore`
|
||||
|
||||
**Fichier :** `infra/dev/.env.docker.local:27,34`
|
||||
|
||||
Le fichier est dans `.gitignore` (ligne 23) **mais déjà tracké** (ajouté avant la règle — gitignore n'agit pas sur les fichiers déjà suivis). Secrets de dev faibles (`changeme_…`) : sans gravité en soi, mais le fichier est censé être local et chaque dev qui le modifie crée du diff.
|
||||
|
||||
**Correction :** `git rm --cached infra/dev/.env.docker.local`, fournir `infra/dev/.env.docker.local.example` (référencé par le README au passage).
|
||||
|
||||
---
|
||||
|
||||
## 2. Bugs fonctionnels
|
||||
|
||||
### 2.1 IMPORTANT — Fallback `http://localhost:3000` dans `useApi.ts`
|
||||
|
||||
**Fichier :** `frontend/app/composables/useApi.ts:18`
|
||||
|
||||
```ts
|
||||
const API_BASE_URL = (publicConfig.apiBaseUrl as string) || 'http://localhost:3000'
|
||||
```
|
||||
|
||||
Si `NUXT_PUBLIC_API_BASE_URL` est vide au build/runtime de prod, **tous** les appels API partent silencieusement vers `localhost:3000` (le port du dev server, qui ne sert même pas l'API). Échec garanti mais difficile à diagnostiquer.
|
||||
|
||||
**Correction :** fallback relatif `'/api'`… attention, `useApi` préfixe déjà `/api` lui-même — le bon fallback est donc `''` (origine courante) :
|
||||
|
||||
```ts
|
||||
const API_BASE_URL = (publicConfig.apiBaseUrl as string) || ''
|
||||
```
|
||||
|
||||
Même nettoyage pour `nuxt.config.ts:49` (`http://localhost/api`, valeur SSR jamais utilisée puisque `ssr: false`).
|
||||
|
||||
### 2.2 IMPORTANT — Uploads de prod probablement plafonnés à 1 Mo par nginx
|
||||
|
||||
Voir 1.4 — c'est le versant fonctionnel : sans `client_max_body_size`, nginx rejette en 413 tout body > 1 Mo, et le front n'affiche pas d'erreur claire (le toast générique de `useApi` au mieux). À tester en prod avec un PDF de 5 Mo ; si les uploads passent, c'est qu'une config existe ailleurs et il faut l'aligner dans le dépôt.
|
||||
|
||||
### 2.3 MOYEN — Deux mécanismes de maintenance déconnectés
|
||||
|
||||
**Fichiers :** `src/Controller/MaintenanceController.php:56` (flag `var/maintenance`), `infra/prod/nginx.conf:6` (flag `maintenance.on`), `infra/prod/deploy.sh:47`
|
||||
|
||||
Le toggle admin (`PUT /api/admin/maintenance`) écrit `var/maintenance`, lu par le middleware front — maintenance **applicative**. Le déploiement crée `maintenance.on`, lu par nginx — maintenance **infra**. Les deux coexistent volontairement mais rien ne le documente : un admin qui active la maintenance via l'UI ne bloque pas les appels API directs (le flag n'est vérifié que par le middleware front), et inversement.
|
||||
|
||||
**Correction :** documenter les deux niveaux dans `DEPLOY.md` ; idéalement faire vérifier le flag applicatif côté backend (listener kernel.request qui renvoie 503 pour les non-admins) plutôt que de ne compter que sur le middleware front (contournable).
|
||||
|
||||
### 2.4 MOYEN — Provider Symfony par `email` alors que `email` est nullable
|
||||
|
||||
**Fichiers :** `config/packages/security.yaml:18`, `src/Entity/Profile.php:59-61`, `src/Controller/AdminProfileController.php:66`
|
||||
|
||||
On peut créer un profil sans email (profils « kiosque »), mais le user provider charge par `property: email`. Ça marche aujourd'hui parce que `SessionProfileAuthenticator` charge par ID — le provider n'est jamais utilisé pour ces profils. Incohérence latente : tout futur usage du provider standard (remember-me, impersonation, commande console) cassera sur ces profils.
|
||||
|
||||
**Correction :** soit rendre l'email obligatoire et générer un email technique pour les kiosques, soit écrire un `UserProviderInterface` custom qui charge par id **ou** email, et le déclarer dans `security.yaml`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Code mort et violations des règles projet
|
||||
|
||||
### 3.1 MINEUR — Config morte dans `nuxt.config.ts`
|
||||
|
||||
**Fichier :** `frontend/nuxt.config.ts:56-59`
|
||||
|
||||
`csrfToken`, `requestTimeout`, `enableDebug`, `enableAnalytics`, `logLevel` : définis, jamais consommés (seul `apiTimeout` est lu par `useApi.ts`). Du code mort en config laisse croire que des fonctionnalités existent (un lecteur pense que le CSRF est géré — il ne l'est pas, cf. 1.6).
|
||||
|
||||
**Correction :** supprimer ces 5 clés.
|
||||
|
||||
### 3.2 MINEUR — Variables JWT dans un projet 100 % session
|
||||
|
||||
**Fichier :** `infra/dev/.env.docker.local:28-30`
|
||||
|
||||
`JWT_SECRET_KEY`, `JWT_PUBLIC_KEY`, `JWT_PASSPHRASE` — copiées d'un autre projet (Lesstime/Starseed utilisent JWT, pas Inventory). Le CLAUDE.md martèle « pas JWT » ; ces variables sèment le doute.
|
||||
|
||||
**Correction :** supprimer les 3 lignes.
|
||||
|
||||
### 3.3 MOYEN — `@nuxtjs/tailwindcss` : dépendance inutilisée et conflictuelle
|
||||
|
||||
**Fichier :** `frontend/package.json:21`
|
||||
|
||||
Le projet utilise Tailwind 4 via `@tailwindcss/vite` (`nuxt.config.ts:1,64`). `@nuxtjs/tailwindcss` n'est référencé nulle part et installe son propre Tailwind **v3** dans `node_modules` — bloat + risque de résolution ambiguë.
|
||||
|
||||
**Correction :** `npm uninstall @nuxtjs/tailwindcss` puis vérifier `npm run build`.
|
||||
|
||||
### 3.4 MINEUR — Dockerfile dev pollué par un template générique
|
||||
|
||||
**Fichier :** `infra/dev/Dockerfile:48-53,82-100`
|
||||
|
||||
Blocs commentés Oracle OCI8, IMAP/Kerberos, PDO MySQL/SQLite — aucun rapport avec un projet PostgreSQL. Bruit pur.
|
||||
|
||||
**Correction :** supprimer les blocs commentés.
|
||||
|
||||
### 3.5 MINEUR — `node_modules/` orphelin à la racine backend
|
||||
|
||||
Pas de `package.json` à la racine, mais un `node_modules/` (untracked) y traîne, et `node_modules/` n'est pas dans le `.gitignore` racine — seul le hasard l'empêche d'être commité un jour.
|
||||
|
||||
**Correction :** `rm -rf node_modules/` à la racine + ajouter `/node_modules/` au `.gitignore`.
|
||||
|
||||
### 3.6 MINEUR — 3 fichiers utils restés en `.js` non typés
|
||||
|
||||
**Fichiers :** `frontend/app/utils/documentPreview.js`, `frontend/app/utils/fileIcons.js`, `frontend/app/utils/printTemplates/machineReport.js`
|
||||
|
||||
Importés depuis du `.ts` sans aucune sécurité de type — incohérent avec la règle « TypeScript, 0 erreur typecheck ».
|
||||
|
||||
**Correction :** renommer en `.ts` et typer les signatures (mécanique, bon candidat Codex).
|
||||
|
||||
---
|
||||
|
||||
## 4. Dette d'architecture — suivi REVIEW_ARCHITECTURE.md
|
||||
|
||||
État des 10 chantiers identifiés le 2026-03-23, vérifié ce jour : **1 corrigé sur 10**, et le God controller a grossi.
|
||||
|
||||
| # | Source de complexité | Statut 2026-06-11 |
|
||||
|---|---------------------|-------------------|
|
||||
| 1 | `smartMatch` dupliqué dans les Sync Strategies | ❌ Toujours dupliqué (`ComposantSyncStrategy.php:380`, `PieceSyncStrategy.php:244`) |
|
||||
| 2 | Custom Fields : 4-6 FK nullable (polymorphisme pauvre) | ❌ Inchangé, pas de contrainte CHECK |
|
||||
| 3 | Composables géants | ⚠️ Partiel : `useComponentEdit.ts` 539 LOC, `usePieceEdit.ts` 404, `useComponentCreate.ts` 366 |
|
||||
| 4 | Triple duplication utils custom fields | ✅ **Corrigé** — fusionné dans `shared/utils/customFields.ts` |
|
||||
| 5 | `pendingStructure` canal caché | ❌ Toujours sans `try/finally` (`ModelTypeProcessor.php`) |
|
||||
| 6 | `PieceProductSyncSubscriber` legacy | ❌ Inchangé (`recomputeSingleEntityChangeSet` toujours là) |
|
||||
| 7 | Double flush dans les processors | ❌ Inchangé (`ComposantProcessor.php:45,132`) |
|
||||
| 8 | `MachineStructureController` God controller | ❌ **Aggravé** : 300+ → **1121 lignes** |
|
||||
| 9 | Dépendance circulaire `useMachineDetailData` | ❌ Proxy ref toujours en place (`:133`) |
|
||||
| 10 | Typage `any` systématique | ❌ **179 occurrences** dans 26 composables |
|
||||
|
||||
**Le point clé :** la Phase 1 « quick wins » du plan (items 1, 5, 6, 7 — effort S chacun, sans impact d'interface) n'a pas été entamée en presque 3 mois, alors que ce sont les corrections au meilleur ratio risque/bénéfice du projet. Le `MachineStructureController` qui grossit de +150 lignes confirme la trajectoire : sans extraction de services, chaque feature l'alourdit.
|
||||
|
||||
---
|
||||
|
||||
## 5. Documentation et configuration
|
||||
|
||||
### 5.1 IMPORTANT — Toute la doc décrit encore un submodule qui n'existe plus
|
||||
|
||||
**Fichiers :** `README.md:49-50,291-296`, `DEPLOY.md:58,62,208`, `RELEASE.md:49,117`, `frontend/README.md:150-154`
|
||||
|
||||
Le frontend a été intégré au monorepo (cf. CLAUDE.md « plus de submodule »), mais le README explique `git clone --recurse-submodules`, un workflow de commit en deux temps, et DEPLOY/RELEASE font des `git submodule update`. Un nouveau dev qui suit le README perd du temps sur des commandes sans effet ; un déploiement scripté depuis DEPLOY.md exécute des étapes mortes.
|
||||
|
||||
**Correction :** purger toute mention de submodule des 4 fichiers, décrire le workflow monorepo (1 commit racine).
|
||||
|
||||
### 5.2 IMPORTANT — `DEPLOY.md` utilise l'utilisateur PG d'un autre projet
|
||||
|
||||
**Fichier :** `DEPLOY.md` (6 occurrences de `ferme_user`)
|
||||
|
||||
Les commandes psql/backup de DEPLOY.md utilisent `ferme_user`/`fermerecette` — copié-collé du projet **Ferme**. Le vrai user prod est `inventory_user` (cf. `infra/prod/.env.example`). Quelqu'un qui suit la doc en incident de prod tape des commandes qui échouent (ou pire, sur la mauvaise base si les deux co-habitent sur l'instance partagée).
|
||||
|
||||
**Correction :** remplacer les 6 occurrences par `inventory_user` et des placeholders de mot de passe.
|
||||
|
||||
### 5.3 MOYEN — `RELEASE.md` et `CLAUDE.md` référencent un fichier `VERSION` inexistant
|
||||
|
||||
**Fichiers :** `RELEASE.md:17,50,80,82`, `CLAUDE.md` (arbre projet)
|
||||
|
||||
La version vit dans `config/version.yaml` (1.9.47) — le fichier `VERSION` n'existe plus. `scripts/release.sh` et la CI (`auto-tag-develop.yml`) sont à jour, mais la doc décrit l'ancien système, y compris « `frontend/nuxt.config.ts` lit `VERSION` au build ».
|
||||
|
||||
**Correction :** mettre à jour RELEASE.md et la ligne d'arbre dans CLAUDE.md vers `config/version.yaml`.
|
||||
|
||||
### 5.4 MINEUR — Pas de `.env.example` backend ni de `.env.docker.local.example`
|
||||
|
||||
Un nouveau dev n'a aucun modèle des variables attendues côté backend (le README pointe vers `infra/dev/.env.docker.local`… qui est censé être local/ignoré, cf. 1.12).
|
||||
|
||||
**Correction :** créer `infra/dev/.env.docker.local.example` avec placeholders.
|
||||
|
||||
### 5.5 MINEUR — Fichiers de données métier à la racine du projet
|
||||
|
||||
`Company (1).json`, `customer.json`, `customer.original.json`, `Ensemble simple rotor.pdf`, `inventory_prod (2).sql.gz` traînent à la racine (untracked — le `.sql.gz` est protégé par le gitignore, **pas les `.json` ni le `.pdf`** : un `git add .` distrait les commiterait, et `customer.json` ressemble à des données client réelles → RGPD).
|
||||
|
||||
**Correction :** déplacer hors du dépôt (ex. `~/imports/`), et ajouter une règle défensive au `.gitignore` (`/*.json` racine, `/*.pdf`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend et UX
|
||||
|
||||
### 6.1 MOYEN — Erreurs avalées : `console.error` sans feedback utilisateur
|
||||
|
||||
**Fichiers :** 57+ occurrences ; ex. `useMachineDetailData.ts:372,385`, `useProfileSession.ts:27`
|
||||
|
||||
Le pattern dominant en cas d'échec API est `console.error(...)` puis on continue avec `null`/`[]`. `useApi` toaste déjà les erreurs HTTP, donc une partie est couverte — mais les erreurs réseau/parsing et les branches qui catchent **avant** le toast laissent l'utilisateur devant une page partiellement vide sans explication, et créent du double-reporting ailleurs.
|
||||
|
||||
**Correction :** définir une règle unique : `useApi` est le seul à toaster ; les composables ne re-loggent pas, mais positionnent un état d'erreur (`error.value = ...`) que la page affiche (bandeau « Impossible de charger X — Réessayer »).
|
||||
|
||||
### 6.2 MOYEN — Appel `/maintenance/check` à chaque navigation
|
||||
|
||||
**Fichier :** `frontend/app/middleware/profile.global.ts:34-39`
|
||||
|
||||
Chaque changement de route d'un non-admin déclenche un aller-retour API. Latence ajoutée à **toutes** les navigations pour un état qui change une fois par an.
|
||||
|
||||
**Correction :** cacher le résultat dans un `useState` avec TTL (ex. 60 s) ; le 503 éventuel d'un appel API normal peut aussi servir de signal.
|
||||
|
||||
### 6.3 Rappels (déjà au §4)
|
||||
|
||||
`any` ×179, composables géants, dépendance circulaire — voir tableau §4 et `docs/REVIEW_ARCHITECTURE.md` pour les solutions détaillées.
|
||||
|
||||
---
|
||||
|
||||
## 7. CI/CD et dépendances
|
||||
|
||||
### 7.1 IMPORTANT — Aucun garde-fou automatisé : la CI ne lance ni tests ni lint
|
||||
|
||||
**Fichiers :** `.gitea/workflows/auto-tag-develop.yml`, `.gitea/workflows/build-docker.yml`, workflow de commit (CLAUDE.md : « committer avec `--no-verify` »)
|
||||
|
||||
La chaîne actuelle : push sur `develop` → auto-tag → build Docker → image prod. **Aucune étape ne lance PHPUnit, php-cs-fixer, ESLint ou `nuxi typecheck`.** Et comme le hook pre-commit (qui devait jouer ce rôle) est trop lent, la convention projet est de le contourner avec `--no-verify`. Résultat : il est possible de tagger et construire une image de prod avec des tests rouges sans qu'aucun système ne le signale.
|
||||
|
||||
**Pourquoi c'est le finding process le plus important de cette review :** la suite de tests est bonne (48 fichiers, DAMA, factories) — mais une suite de tests qui ne tourne pas en CI ne protège rien.
|
||||
|
||||
**Correction :** ajouter un workflow `ci.yml` déclenché sur PR + push develop :
|
||||
1. `composer install` + `php-cs-fixer --dry-run` + PHPUnit (avec un service PG 16).
|
||||
2. `npm ci` + `eslint` + `npx nuxi typecheck` + `npm run build` dans `frontend/`.
|
||||
3. Faire dépendre `auto-tag-develop` du succès de la CI (ou au minimum bloquer les PR).
|
||||
Le hook pre-commit lent peut alors être assumé comme optionnel.
|
||||
|
||||
### 7.2 MOYEN — Le build Docker de prod n'est pas testé avant le push
|
||||
|
||||
`build-docker.yml` build + push `latest` dès qu'un tag est posé — sans health-check de l'image (la CI de 7.1 règle l'essentiel ; un `docker run --rm image php bin/console about` est un bon smoke test bon marché).
|
||||
|
||||
### 7.3 Dépendances
|
||||
|
||||
- Backend : propre, à jour (Symfony 8.0.*, AP ^4.2, ORM ^3.6). `symfony/twig-bundle` à vérifier : aucun template Twig dans `templates/` — si seul le MCP bundle le requiert, le laisser en dépendance transitive.
|
||||
- Frontend : `@nuxtjs/tailwindcss` à supprimer (cf. 3.3). Le reste est à jour et utilisé.
|
||||
|
||||
---
|
||||
|
||||
## 8. Hygiène git
|
||||
|
||||
Synthèse des fichiers trackés à tort (détails en §1/§5) :
|
||||
|
||||
| Fichier | Problème | Action |
|
||||
|---------|----------|--------|
|
||||
| `.mcp.json` | Credentials prod | `git rm --cached` + gitignore + **rotation** |
|
||||
| `create_test_user.php` | Script debug + credentials | `git rm` |
|
||||
| `scripts/{check,fix,restore,migrate,verify}-prod-*.php` (9) | Mot de passe PG prod | rotation + archive/suppression |
|
||||
| `infra/dev/.env.docker.local` | Censé être local, déjà tracké | `git rm --cached` + `.example` |
|
||||
| `.claude/settings.json` | Inoffensif (config plugins) | OK, peut rester |
|
||||
|
||||
Gaps `.gitignore` racine : `/node_modules/`, `.mcp.json`, `/*.json` (défensif), `/*.pdf`.
|
||||
|
||||
> Note : contrairement au premier rapport d'agent, `.claude/settings.local.json` n'est **pas** tracké — pas d'action nécessaire.
|
||||
|
||||
---
|
||||
|
||||
## 9. Bonnes pratiques à retenir
|
||||
|
||||
### Ce qui est bien fait dans le projet
|
||||
|
||||
- **`declare(strict_types=1)` partout**, attributs PHP 8 modernes, code backend homogène et lisible.
|
||||
- **Sécurité API systématique** : chaque opération API Platform porte son `security:`, chaque controller custom ouvre par `denyAccessUnlessGranted()` — aucun endpoint accidentellement public trouvé.
|
||||
- **Rate limiting** sur le login **et** sur l'auth MCP (souvent oublié ailleurs).
|
||||
- **IDs CUID** : pas d'énumération séquentielle possible.
|
||||
- **Upload : validation MIME par contenu** (`finfo`), nom de fichier régénéré (CUID), stockage hors de `public/` — et SVG exclu de l'allowlist (XSS évité).
|
||||
- **`serve()` documents** : `nosniff` + `CSP: sandbox` — au-dessus du standard.
|
||||
- **Système d'audit** propre (`AbstractAuditSubscriber` en template method, flag `skipAudit` réfléchi).
|
||||
- **Tests solides** : 48 fichiers, `AbstractApiTestCase` avec factories, DAMA rollback, coûts de hash réduits en test.
|
||||
- **Le refacto `customFields.ts` a été fait** — la dette de REVIEW_ARCHITECTURE n'est pas ignorée, juste lente.
|
||||
- **Docker prod multi-stage** propre, page maintenance nginx, volumes nommés pour le storage.
|
||||
- **Zéro `console.log`**, zéro TODO/FIXME oublié dans `src/` et `frontend/app/`.
|
||||
|
||||
### Les règles à graver
|
||||
|
||||
1. **Un secret commité est un secret grillé** : la suppression du fichier ne suffit jamais, il faut **révoquer** (cf. `.mcp.json`, scripts prod).
|
||||
2. **`.gitignore` n'agit pas sur les fichiers déjà trackés** — `git rm --cached` d'abord.
|
||||
3. **Une suite de tests qui ne tourne pas en CI ne protège rien** : si le hook est trop lent pour être vécu, le garde-fou doit vivre en CI.
|
||||
4. **Toute limite (taille, pagination, timeout) doit être choisie, pas héritée d'un défaut** — sinon c'est le défaut le plus bas de la chaîne qui décide (nginx 1 Mo).
|
||||
5. **Un fallback doit échouer bruyamment ou être correct** — `|| 'http://localhost:3000'` en prod est le pire des deux mondes.
|
||||
6. **La doc copiée d'un autre projet est pire que pas de doc** (`ferme_user` dans DEPLOY.md).
|
||||
7. **Config morte = mensonge** : un `csrfToken` non branché fait croire qu'une protection existe.
|
||||
8. **Les scripts one-shot ont une date de péremption** : après usage → `_archives/` ou suppression.
|
||||
9. **Quand un God controller existe, chaque feature le fait grossir** : extraire tôt (1121 lignes et ça monte).
|
||||
10. **Les quick wins planifiés perdent leur valeur s'ils ne sont jamais faits** : la Phase 1 de REVIEW_ARCHITECTURE (4×effort S) attend depuis 3 mois.
|
||||
|
||||
---
|
||||
|
||||
## Résumé par priorité
|
||||
|
||||
| Priorité | # | Problème | Fichier |
|
||||
|----------|---|----------|---------|
|
||||
| **P0** | 1.1 | Credentials prod + token Lesstime commités (rotation requise) | `.mcp.json` |
|
||||
| **P0** | 1.2 | Script admin avec mot de passe en clair tracké | `create_test_user.php` |
|
||||
| **P0** | 1.3 | Mot de passe PG prod dans 9 scripts trackés | `scripts/*-prod-*.php` |
|
||||
| **P1** | 7.1 | CI sans tests ni lint + hook contourné | `.gitea/workflows/` |
|
||||
| **P1** | 1.4/2.2 | Aucune limite d'upload choisie (et défaut nginx 1 Mo) | `DocumentUploadProcessor.php`, `CommentController.php`, `infra/` |
|
||||
| **P1** | 2.1 | Fallback API `localhost:3000` en prod | `frontend/app/composables/useApi.ts:18` |
|
||||
| **P1** | 5.1 | Doc submodule obsolète (4 fichiers) | `README.md`, `DEPLOY.md`, `RELEASE.md`, `frontend/README.md` |
|
||||
| **P1** | 5.2 | `ferme_user` dans DEPLOY.md | `DEPLOY.md` |
|
||||
| **P1** | 1.5 | Garde path-traversal incomplet | `DocumentStorageService.php:28-42` |
|
||||
| **P2** | 1.6 | CSRF : une seule barrière (SameSite) | `framework.yaml`, `useApi.ts` |
|
||||
| **P2** | 1.7 | Session fixation : protection désactivée globalement | `security.yaml:5` |
|
||||
| **P2** | 1.8 | Mot de passe MCP dans headers + HTTP sans TLS | `McpHeaderAuthenticator.php` |
|
||||
| **P2** | 2.3 | Double mécanisme maintenance non documenté | `MaintenanceController.php`, `nginx.conf` |
|
||||
| **P2** | 2.4 | Provider `email` vs email nullable | `security.yaml:18`, `Profile.php` |
|
||||
| **P2** | 3.3 | `@nuxtjs/tailwindcss` inutilisé (TW3 vs TW4) | `frontend/package.json:21` |
|
||||
| **P2** | 5.3 | Doc `VERSION` obsolète | `RELEASE.md`, `CLAUDE.md` |
|
||||
| **P2** | 5.5 | Données client à la racine (`customer.json`…) | racine projet |
|
||||
| **P2** | 6.1 | Erreurs avalées sans feedback UI | composables (57+) |
|
||||
| **P2** | 6.2 | Maintenance check à chaque navigation | `profile.global.ts:34-39` |
|
||||
| **P2** | 1.12 | `.env.docker.local` tracké malgré gitignore | `infra/dev/.env.docker.local` |
|
||||
| **P3** | 1.9 | Commentaires+upload en ROLE_VIEWER (à confirmer métier) | `CommentController.php:33` |
|
||||
| **P3** | 1.10 | Pagination max 2000/1000/500 | `Constructeur.php`, `Document.php` |
|
||||
| **P3** | 1.11/3.2 | Secrets dev faibles + vars JWT mortes | `infra/dev/.env.docker.local` |
|
||||
| **P3** | 3.1 | Config morte nuxt.config (5 clés) | `frontend/nuxt.config.ts:56-59` |
|
||||
| **P3** | 3.4 | Dockerfile dev : blocs commentés Oracle/MySQL | `infra/dev/Dockerfile` |
|
||||
| **P3** | 3.5 | `node_modules/` orphelin racine + gitignore | racine projet |
|
||||
| **P3** | 3.6 | 3 fichiers `.js` non typés | `frontend/app/utils/` |
|
||||
| **P3** | 1.10 | `download()` sans nosniff/CSP | `DocumentServeController.php:105-116` |
|
||||
| **P3** | 5.4 | Pas de `.env.docker.local.example` | `infra/dev/` |
|
||||
| **P3** | 7.2 | Image Docker poussée sans smoke test | `build-docker.yml` |
|
||||
|
||||
> La dette d'architecture (§4) a son propre plan dans `docs/REVIEW_ARCHITECTURE.md` — recommandation : exécuter enfin sa **Phase 1** (4 corrections effort S, sans impact d'interface).
|
||||
-343
@@ -1,343 +0,0 @@
|
||||
# Tickets correctifs — Projet Inventory
|
||||
|
||||
> Liste de tâches issues de la review du 2026-06-11 (`REVIEW.md`).
|
||||
> Chaque ticket est autonome : contexte, ce qu'il faut faire, fichiers concernés.
|
||||
> Commence par les P0, puis P1, etc. Convention de commit : `fix(T-XXX) : description courte`.
|
||||
|
||||
---
|
||||
|
||||
## P0 — Urgents (sécurité)
|
||||
|
||||
### T-001 — Révoquer et retirer les credentials de `.mcp.json`
|
||||
**Pourquoi :** le fichier `.mcp.json` est dans git et contient le mot de passe admin MCP de production (`A123`) et un token Lesstime valide. Toute personne avec accès au dépôt (ou à son historique) peut se connecter aux deux systèmes. Supprimer le fichier ne suffit pas : git garde l'historique — il faut **changer les secrets**.
|
||||
**À faire :**
|
||||
1. Changer le mot de passe du profil `admin-default-profile` sur `inventory.malio-dev.fr` (et choisir un vrai mot de passe, pas `A123`).
|
||||
2. Régénérer le bearer token Lesstime côté Lesstime.
|
||||
3. Sortir le fichier de git sans le supprimer du disque :
|
||||
```bash
|
||||
git rm --cached .mcp.json
|
||||
echo ".mcp.json" >> .gitignore
|
||||
```
|
||||
4. Créer `.mcp.json.example` avec des placeholders :
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"inventory": {
|
||||
"type": "http",
|
||||
"url": "https://inventory.malio-dev.fr/_mcp",
|
||||
"headers": {
|
||||
"X-Profile-Id": "<PROFILE_ID>",
|
||||
"X-Profile-Password": "<PASSWORD>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
5. Remettre les nouveaux secrets dans ton `.mcp.json` local (désormais ignoré).
|
||||
**Fichiers :** `.mcp.json`, `.gitignore`, `.mcp.json.example` (nouveau)
|
||||
|
||||
### T-002 — Supprimer `create_test_user.php` et vérifier la prod
|
||||
**Pourquoi :** ce script de debug, commité à la racine, crée un compte `ROLE_ADMIN` avec `admin@admin.com` / `admin123` — un mot de passe devinable en quelques essais. S'il a déjà tourné en production, un compte admin faible existe peut-être en ce moment.
|
||||
**À faire :**
|
||||
1. Vérifier en prod qu'aucun profil `admin@admin.com` n'est actif :
|
||||
```sql
|
||||
SELECT id, email, is_active, roles FROM profiles WHERE email = 'admin@admin.com';
|
||||
```
|
||||
S'il existe : le désactiver ou changer son mot de passe immédiatement.
|
||||
2. Supprimer le script :
|
||||
```bash
|
||||
git rm create_test_user.php
|
||||
```
|
||||
3. (Optionnel) Si le besoin « créer un admin de dev » existe encore, créer une commande Symfony `app:create-admin` qui prend le mot de passe en argument — ne jamais le hardcoder.
|
||||
**Fichiers :** `create_test_user.php`
|
||||
|
||||
### T-003 — Changer le mot de passe PG prod et archiver les scripts `*-prod-*.php`
|
||||
**Pourquoi :** 9 scripts dans `scripts/` contiennent le mot de passe de la base de production en clair (`fermerecette`). Ce sont des scripts de réparation one-shot qui ont déjà servi : ils n'ont plus de raison d'être dans le dépôt avec des secrets dedans.
|
||||
**À faire :**
|
||||
1. Changer le mot de passe de l'utilisateur PG concerné sur le serveur de prod.
|
||||
2. Archiver les scripts (le dossier `_archives/` est déjà dans le `.gitignore`) :
|
||||
```bash
|
||||
mkdir -p _archives/scripts-prod
|
||||
git rm scripts/check-prod-values.php scripts/check-prod-audit-dates.php \
|
||||
scripts/check-prod-missing-piece-cfs.php scripts/check-prod-orphaned-detail.php \
|
||||
scripts/fix-prod-all.php scripts/fix-prod-recreate-and-migrate.php \
|
||||
scripts/migrate-orphaned-custom-fields.php scripts/restore-custom-field-values.php \
|
||||
scripts/verify-prod-health.php
|
||||
# (les copies locales peuvent aller dans _archives/scripts-prod si tu veux les garder)
|
||||
```
|
||||
3. Si l'un d'eux doit rester utilisable : remplacer les credentials en dur par `getenv('DATABASE_URL')`.
|
||||
**Fichiers :** `scripts/*-prod-*.php`, `scripts/migrate-orphaned-custom-fields.php`, `scripts/restore-custom-field-values.php`
|
||||
|
||||
---
|
||||
|
||||
## P1 — Importants
|
||||
|
||||
### T-004 — Ajouter une CI qui lance tests et lint
|
||||
**Pourquoi :** aujourd'hui, rien ne lance les tests automatiquement : la CI Gitea ne fait que tagger et builder l'image Docker, et le hook pre-commit est contourné avec `--no-verify` (trop lent). On peut donc livrer en prod avec des tests rouges sans aucune alerte.
|
||||
**À faire :**
|
||||
1. Créer `.gitea/workflows/ci.yml` :
|
||||
```yaml
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [develop]
|
||||
jobs:
|
||||
backend:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: root
|
||||
POSTGRES_DB: inventory_test
|
||||
ports: ["5432:5432"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with: { php-version: '8.4', extensions: 'pdo_pgsql, intl' }
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: vendor/bin/php-cs-fixer fix --dry-run --diff --allow-risky=yes
|
||||
- run: php bin/console doctrine:schema:create --env=test
|
||||
env: { DATABASE_URL: "postgresql://root:root@localhost:5432/inventory_test" }
|
||||
- run: vendor/bin/phpunit
|
||||
env: { DATABASE_URL: "postgresql://root:root@localhost:5432/inventory_test" }
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: 22 }
|
||||
- run: npm ci
|
||||
working-directory: frontend
|
||||
- run: npx eslint .
|
||||
working-directory: frontend
|
||||
- run: npx nuxi typecheck
|
||||
working-directory: frontend
|
||||
- run: npm run build
|
||||
working-directory: frontend
|
||||
```
|
||||
> Adapter les détails (version PHP exacte, env de test) au premier run — l'important est que les 4 vérifications (cs-fixer, PHPUnit, ESLint, typecheck) tournent.
|
||||
2. Dans Gitea, marquer ce workflow comme requis pour merger une PR vers `develop`.
|
||||
**Fichiers :** `.gitea/workflows/ci.yml` (nouveau)
|
||||
|
||||
### T-005 — Définir une limite de taille d'upload à tous les niveaux
|
||||
**Pourquoi :** aucune limite n'est choisie nulle part. Conséquence double : en prod, nginx applique son défaut de **1 Mo** (les gros PDF sont sans doute rejetés avec une erreur brute), et côté application rien n'empêcherait de remplir le disque si les limites infra étaient relevées. Décision à prendre : 50 Mo max (à ajuster au métier).
|
||||
**À faire :**
|
||||
1. Check applicatif dans `DocumentUploadProcessor::handleMultipartUpload()` (après la validation MIME, ligne ~79) :
|
||||
```php
|
||||
private const MAX_UPLOAD_BYTES = 50 * 1024 * 1024; // 50 Mo
|
||||
|
||||
if ($file->getSize() > self::MAX_UPLOAD_BYTES) {
|
||||
throw new BadRequestHttpException('Fichier trop volumineux (max 50 Mo).');
|
||||
}
|
||||
```
|
||||
2. Même check dans `CommentController::create()` dans la boucle `foreach ($files as $file)` (ligne ~106) — renvoyer `$this->json(['message' => 'Fichier trop volumineux (max 50 Mo).'], 400)`.
|
||||
3. `infra/dev/php.ini` — ajouter :
|
||||
```ini
|
||||
upload_max_filesize = 50M
|
||||
post_max_size = 55M
|
||||
```
|
||||
Et vérifier que l'image prod (infra/prod/Dockerfile) reçoit la même config.
|
||||
4. `infra/prod/nginx.conf` — dans le bloc `server` :
|
||||
```nginx
|
||||
client_max_body_size 55m;
|
||||
```
|
||||
(idem dans `nginx-proxy.conf` si le proxy frontal est aussi versionné ici).
|
||||
5. Tester : upload d'un PDF de ~5 Mo en local, et vérifier le message d'erreur propre au-delà de 50 Mo.
|
||||
**Fichiers :** `src/State/DocumentUploadProcessor.php`, `src/Controller/CommentController.php`, `infra/dev/php.ini`, `infra/prod/nginx.conf`, `infra/prod/Dockerfile`
|
||||
|
||||
### T-006 — Corriger le fallback d'URL API du frontend
|
||||
**Pourquoi :** si la variable `NUXT_PUBLIC_API_BASE_URL` est vide en prod, tous les appels API partent vers `http://localhost:3000` — c'est-à-dire vers la machine de l'utilisateur. L'app casse silencieusement. Un fallback vide = « même origine », ce qui est le comportement correct.
|
||||
**À faire :**
|
||||
1. `frontend/app/composables/useApi.ts` ligne 18 :
|
||||
```ts
|
||||
// AVANT
|
||||
const API_BASE_URL = (publicConfig.apiBaseUrl as string) || 'http://localhost:3000'
|
||||
// APRÈS (chaîne vide = même origine ; useApi ajoute déjà /api)
|
||||
const API_BASE_URL = (publicConfig.apiBaseUrl as string) || ''
|
||||
```
|
||||
2. `frontend/nuxt.config.ts` ligne ~49 : remplacer le fallback `'http://localhost/api'` par `''` (valeur SSR jamais utilisée, SSR off — autant ne pas mentir).
|
||||
3. Vérifier en dev que tout fonctionne encore (la variable est définie en dev, donc rien ne doit changer), puis `npx nuxi typecheck`.
|
||||
**Fichiers :** `frontend/app/composables/useApi.ts`, `frontend/nuxt.config.ts`
|
||||
|
||||
### T-007 — Purger la doc « submodule » (le frontend est dans le monorepo)
|
||||
**Pourquoi :** le frontend a été rapatrié dans le repo principal, mais README, DEPLOY, RELEASE et le README frontend décrivent encore le clonage `--recurse-submodules` et le workflow de commit en deux temps. Un nouveau dev (ou toi en incident de prod) suit des étapes qui n'existent plus.
|
||||
**À faire :**
|
||||
1. `README.md` : lignes 49-50 → `git clone <url-du-repo>` ; section « workflow » lignes ~291-296 → décrire « un seul commit depuis la racine couvre backend + frontend ».
|
||||
2. `DEPLOY.md` : supprimer les `git submodule update --init --recursive` (lignes 58, 62, 208).
|
||||
3. `RELEASE.md` : supprimer les étapes submodule (lignes 49, 117).
|
||||
4. `frontend/README.md` : remplacer la section « submodule » (lignes 150-154) par « ce dossier fait partie du monorepo Inventory ».
|
||||
**Fichiers :** `README.md`, `DEPLOY.md`, `RELEASE.md`, `frontend/README.md`
|
||||
|
||||
### T-008 — Corriger l'utilisateur PG dans `DEPLOY.md`
|
||||
**Pourquoi :** les commandes de DEPLOY.md utilisent `ferme_user` / `fermerecette` — copié-collé du projet Ferme. Le vrai utilisateur est `inventory_user`. En situation d'incident, suivre la doc ferait taper des commandes qui échouent ou visent la mauvaise base.
|
||||
**À faire :**
|
||||
1. Remplacer les 6 occurrences de `ferme_user` par `inventory_user`.
|
||||
2. Remplacer le mot de passe en clair par un placeholder `<mot-de-passe-prod>` (le mot de passe n'a rien à faire dans la doc).
|
||||
**Fichiers :** `DEPLOY.md`
|
||||
|
||||
### T-009 — Durcir le garde anti path-traversal de `DocumentStorageService`
|
||||
**Pourquoi :** le contrôle `realpath()` (qui vérifie que le chemin final est bien dans le dossier de stockage) est sauté quand le fichier n'existe pas, car `realpath()` renvoie `false` dans ce cas. Le risque actuel est faible (le chemin vient de la base, pas de l'utilisateur), mais le contrôle se veut une protection — autant qu'il protège vraiment.
|
||||
**À faire :** dans `getAbsolutePath()` (`src/Service/DocumentStorageService.php:28-42`) :
|
||||
```php
|
||||
// AVANT
|
||||
$absolutePath = $this->storageDir.'/'.$relativePath;
|
||||
$realPath = realpath($absolutePath);
|
||||
|
||||
if (false !== $realPath && !str_starts_with($realPath, realpath($this->storageDir))) {
|
||||
throw new RuntimeException(sprintf('Path traversal detected: "%s"', $relativePath));
|
||||
}
|
||||
|
||||
// APRÈS — valider sur le dossier parent, qui existe toujours pour un fichier servi
|
||||
$absolutePath = $this->storageDir.'/'.$relativePath;
|
||||
$realParent = realpath(dirname($absolutePath));
|
||||
$realStorage = realpath($this->storageDir);
|
||||
|
||||
if (false === $realStorage || false === $realParent
|
||||
|| !str_starts_with($realParent.'/', $realStorage.'/')) {
|
||||
throw new RuntimeException(sprintf('Path traversal detected: "%s"', $relativePath));
|
||||
}
|
||||
```
|
||||
Garder le check `str_contains($relativePath, '..')` existant en première ligne. Ajouter un test unitaire avec un chemin contenant `../` et un chemin absolu.
|
||||
**Fichiers :** `src/Service/DocumentStorageService.php`, `tests/` (nouveau test)
|
||||
|
||||
---
|
||||
|
||||
## P2 — Consolidation
|
||||
|
||||
### T-010 — Hardening CSRF : header `X-Requested-With` obligatoire sur les écritures
|
||||
**Pourquoi :** l'auth est par cookie, et la seule protection contre le CSRF (un site malveillant qui fait faire des requêtes à ton navigateur connecté) est l'attribut `SameSite=Lax` du cookie. Une deuxième barrière peu coûteuse : exiger un header custom, qu'un formulaire HTML cross-site ne peut pas envoyer.
|
||||
**À faire :**
|
||||
1. Côté front, dans `useApi.ts`, ajouter `'X-Requested-With': 'XMLHttpRequest'` aux headers de toutes les requêtes.
|
||||
2. Côté back, créer un listener `kernel.request` qui renvoie 403 si la méthode n'est pas GET/HEAD/OPTIONS, que le chemin matche `^/api` (hors `/api/session/profile` pour le login) et que le header est absent.
|
||||
3. Supprimer la clé morte `csrfToken` de `nuxt.config.ts` (elle laisse croire qu'une protection CSRF existe).
|
||||
4. Adapter les tests API (`AbstractApiTestCase`) pour envoyer le header.
|
||||
**Fichiers :** `frontend/app/composables/useApi.ts`, `src/EventListener/` (nouveau), `frontend/nuxt.config.ts`, `tests/AbstractApiTestCase.php`
|
||||
|
||||
### T-011 — Test fonctionnel : l'ID de session change au login
|
||||
**Pourquoi :** la protection automatique contre la fixation de session est désactivée (`session_fixation_strategy: none`, choix documenté pour la SPA) et compensée par un `$session->migrate(true)` manuel au login. Si quelqu'un supprime ce `migrate` un jour, plus rien ne protège — un test doit le verrouiller.
|
||||
**À faire :** écrire un test API qui : récupère le cookie de session avant login → se loggue → vérifie que l'ID de session a changé. Ajouter un commentaire dans `SessionProfileController::activateProfile()` pointant vers `security.yaml:5`.
|
||||
**Fichiers :** `tests/Api/SessionProfileTest.php` (ou équivalent), `src/Controller/SessionProfileController.php`
|
||||
|
||||
### T-012 — MCP : remplacer le mot de passe par un token Bearer + HTTPS
|
||||
**Pourquoi :** le mot de passe du profil circule en clair dans les headers de chaque requête MCP (et l'URL configurée est en `http://`). Les headers finissent dans les logs des proxys. Un token dédié révocable, transmis en `Authorization: Bearer`, est le pattern déjà utilisé par Lesstime.
|
||||
**À faire :** ajouter un champ `mcpToken` (hashé) sur Profile ou une table `api_tokens`, générer via une commande console, adapter `McpHeaderAuthenticator` pour valider le Bearer (garder le rate-limiting), mettre à jour `.mcp.json.example`, et servir `/_mcp` en HTTPS uniquement.
|
||||
**Fichiers :** `src/Mcp/Security/McpHeaderAuthenticator.php`, `src/Entity/Profile.php` ou nouvelle entité, migration, `.mcp.json.example`
|
||||
|
||||
### T-013 — Maintenance : faire respecter le flag côté backend
|
||||
**Pourquoi :** le toggle admin écrit `var/maintenance`, mais seul le **frontend** (middleware) le vérifie. Quelqu'un qui appelle l'API directement (curl, MCP, script) passe à travers. Et le second mécanisme (`maintenance.on` lu par nginx, posé par `deploy.sh`) n'est documenté nulle part.
|
||||
**À faire :**
|
||||
1. Listener `kernel.request` backend : si `var/maintenance` existe et que l'utilisateur n'est pas admin → 503 JSON (sauf `/api/maintenance/check` et `/api/session/*`).
|
||||
2. Documenter les deux niveaux (applicatif vs nginx/deploy) dans `DEPLOY.md`.
|
||||
**Fichiers :** `src/EventListener/` (nouveau), `DEPLOY.md`
|
||||
|
||||
### T-014 — Résoudre l'incohérence provider/email nullable
|
||||
**Pourquoi :** le user provider Symfony charge les profils par `email`, mais l'email est nullable (profils « kiosque »). Ça marche par chance (l'authenticator charge par ID), mais tout futur usage du provider standard cassera sur ces profils.
|
||||
**À faire :** décision à prendre — option A : email obligatoire + email technique généré pour les kiosques ; option B : provider custom qui charge par id ou email. Documenter le choix dans `docs/BACKEND.md`.
|
||||
**Fichiers :** `config/packages/security.yaml`, `src/Entity/Profile.php`, éventuellement `src/Security/`
|
||||
|
||||
### T-015 — Supprimer `@nuxtjs/tailwindcss`
|
||||
**Pourquoi :** dépendance non utilisée (le projet utilise `@tailwindcss/vite`, Tailwind 4) qui installe en plus un Tailwind 3 parallèle — bloat et risque de conflit de résolution.
|
||||
**À faire :**
|
||||
```bash
|
||||
cd frontend && npm uninstall @nuxtjs/tailwindcss && npm run build && npx nuxi typecheck
|
||||
```
|
||||
**Fichiers :** `frontend/package.json`, `frontend/package-lock.json`
|
||||
|
||||
### T-016 — Mettre à jour la doc de versioning (`VERSION` → `config/version.yaml`)
|
||||
**Pourquoi :** `RELEASE.md` (lignes 17, 50, 80, 82) et l'arbre projet de `CLAUDE.md` référencent un fichier `VERSION` qui n'existe plus — la version vit dans `config/version.yaml`.
|
||||
**À faire :** remplacer toutes les références ; vérifier au passage que la description du footer frontend (« lit VERSION au build ») correspond au mécanisme réel.
|
||||
**Fichiers :** `RELEASE.md`, `CLAUDE.md`
|
||||
|
||||
### T-017 — Sortir les données client de la racine + gitignore défensif
|
||||
**Pourquoi :** `customer.json`, `Company (1).json`, `Ensemble simple rotor.pdf` (données client réelles → enjeu RGPD) traînent à la racine, non protégés par le `.gitignore` : un `git add .` distrait les commiterait.
|
||||
**À faire :**
|
||||
1. Déplacer ces fichiers hors du dépôt (ex. `~/imports/inventory/`). Supprimer aussi `inventory_prod (2).sql.gz` et le `node_modules/` orphelin de la racine.
|
||||
2. Ajouter au `.gitignore` racine :
|
||||
```
|
||||
/node_modules/
|
||||
/*.json
|
||||
/*.pdf
|
||||
```
|
||||
(les `.json` légitimes du projet sont dans des sous-dossiers ou explicitement trackés — `composer.json` etc. restent suivis car déjà trackés ; pour les nouveaux, `git add -f` reste possible).
|
||||
**Fichiers :** `.gitignore`, racine du projet
|
||||
|
||||
### T-018 — Uniformiser la gestion d'erreur frontend (état d'erreur au lieu de `console.error`)
|
||||
**Pourquoi :** en cas d'échec de chargement, le pattern actuel est `console.error(...)` puis la page s'affiche à moitié vide, sans message. L'utilisateur ne sait pas que quelque chose a raté ni comment réessayer.
|
||||
**À faire :** règle : `useApi` est le seul à toaster ; les composables exposent un `error: Ref<string | null>` que la page affiche (bandeau avec bouton Réessayer). Commencer par les 3 pages principales : détail machine (`useMachineDetailData.ts:372,385`), détail composant, détail pièce. Étendre ensuite au reste.
|
||||
**Fichiers :** `frontend/app/composables/useMachineDetailData.ts`, `useComponentEdit.ts`, `usePieceEdit.ts`, pages correspondantes
|
||||
|
||||
### T-019 — Cacher le résultat de `/maintenance/check` (TTL)
|
||||
**Pourquoi :** chaque navigation d'un non-admin déclenche un appel API pour vérifier la maintenance — de la latence sur toutes les transitions de page pour un état qui ne change presque jamais.
|
||||
**À faire :** dans `profile.global.ts`, stocker le résultat dans un `useState` avec timestamp et ne re-fetcher que si > 60 s.
|
||||
**Fichiers :** `frontend/app/middleware/profile.global.ts`
|
||||
|
||||
### T-020 — Détracker `infra/dev/.env.docker.local` + fournir un `.example`
|
||||
**Pourquoi :** le fichier est dans le `.gitignore` mais a été commité avant l'ajout de la règle — git continue donc de le suivre. Chaque dev qui le modifie crée du diff, et ses secrets (même de dev) sont versionnés.
|
||||
**À faire :**
|
||||
```bash
|
||||
cp infra/dev/.env.docker.local infra/dev/.env.docker.local.example
|
||||
# Dans le .example : remplacer les valeurs par des placeholders <CHANGE_ME>
|
||||
# et supprimer les variables JWT_* (inutilisées, cf. T-023)
|
||||
git rm --cached infra/dev/.env.docker.local
|
||||
git add infra/dev/.env.docker.local.example
|
||||
```
|
||||
Mettre à jour le README (section installation) pour mentionner la copie du `.example`.
|
||||
**Fichiers :** `infra/dev/.env.docker.local`, `infra/dev/.env.docker.local.example` (nouveau), `README.md`
|
||||
|
||||
---
|
||||
|
||||
## P3 — Nice to have
|
||||
|
||||
### T-021 — Décision métier : commentaires (et pièces jointes) en `ROLE_VIEWER` ?
|
||||
**Pourquoi :** seule écriture accessible aux lecteurs. Si c'est voulu (« tout le monde commente »), le documenter dans `docs/BACKEND.md` ; sinon passer à `ROLE_GESTIONNAIRE` (`CommentController.php:33`).
|
||||
**Fichiers :** `src/Controller/CommentController.php`, `docs/BACKEND.md`
|
||||
|
||||
### T-022 — Revoir les pagination max (2000/1000/500)
|
||||
**Pourquoi :** `Constructeur` (2000), `ConstructeurCategorie` (1000), `Document` (500) — vérifier le besoin réel du front et redescendre, ou commenter pourquoi.
|
||||
**Fichiers :** `src/Entity/Constructeur.php`, `src/Entity/ConstructeurCategorie.php`, `src/Entity/Document.php`
|
||||
|
||||
### T-023 — Nettoyer les variables JWT et les secrets `changeme`
|
||||
**Pourquoi :** `JWT_SECRET_KEY`/`JWT_PUBLIC_KEY`/`JWT_PASSPHRASE` ne servent à rien (auth session). `APP_SECRET=changeme_…` mérite une vraie valeur aléatoire locale. (Fusionne naturellement avec T-020.)
|
||||
**Fichiers :** `infra/dev/.env.docker.local`
|
||||
|
||||
### T-024 — Supprimer la config morte de `nuxt.config.ts`
|
||||
**Pourquoi :** `csrfToken`, `requestTimeout`, `enableDebug`, `enableAnalytics`, `logLevel` ne sont consommés nulle part — de la config qui ment. (`csrfToken` est déjà traité par T-010.)
|
||||
**Fichiers :** `frontend/nuxt.config.ts:56-59`
|
||||
|
||||
### T-025 — Nettoyer le Dockerfile dev (blocs Oracle/IMAP/MySQL commentés)
|
||||
**Pourquoi :** restes d'un template générique sans rapport avec un projet PostgreSQL.
|
||||
**Fichiers :** `infra/dev/Dockerfile:48-53,82-100`
|
||||
|
||||
### T-026 — Renommer les 3 utils `.js` en `.ts`
|
||||
**Pourquoi :** `documentPreview.js`, `fileIcons.js`, `printTemplates/machineReport.js` sont importés depuis du TypeScript sans types. Tâche mécanique (bon candidat pour Codex).
|
||||
**Fichiers :** `frontend/app/utils/documentPreview.js`, `frontend/app/utils/fileIcons.js`, `frontend/app/utils/printTemplates/machineReport.js`
|
||||
|
||||
### T-027 — Ajouter `nosniff` + `CSP: sandbox` à `download()`
|
||||
**Pourquoi :** `serve()` envoie ces deux headers de protection, `download()` non — asymétrie gratuite.
|
||||
**À faire :** copier les deux `headers->set(...)` de `serve()` dans `download()` (`DocumentServeController.php:110-116`).
|
||||
**Fichiers :** `src/Controller/DocumentServeController.php`
|
||||
|
||||
### T-028 — Smoke test de l'image Docker avant push
|
||||
**Pourquoi :** `build-docker.yml` pousse `latest` sans vérifier que l'image démarre.
|
||||
**À faire :** entre build et push : `docker run --rm gitea.malio.fr/malio-dev/inventory:${{ gitea.ref_name }} php bin/console about`.
|
||||
**Fichiers :** `.gitea/workflows/build-docker.yml`
|
||||
|
||||
> **Hors tickets :** la dette d'architecture (smartMatch dupliqué, double flush, `pendingStructure`, God controller à 1121 lignes, `any` ×179…) a déjà son plan d'action chiffré dans `docs/REVIEW_ARCHITECTURE.md`. Recommandation forte : exécuter sa **Phase 1** (4 corrections effort S, sans impact d'interface) avant qu'elle ne prenne encore 3 mois — voir `REVIEW.md` §4.
|
||||
|
||||
---
|
||||
|
||||
## Résumé
|
||||
|
||||
| Priorité | Tickets | Estimation |
|
||||
|----------|---------|------------|
|
||||
| **P0** | T-001 à T-003 | ~2h (+ rotations de secrets) |
|
||||
| **P1** | T-004 à T-009 | ~1,5 j |
|
||||
| **P2** | T-010 à T-020 | ~2,5 j |
|
||||
| **P3** | T-021 à T-028 | ~0,5 j |
|
||||
| **Total** | 28 tickets | ~5 j |
|
||||
|
||||
> Commence par **T-001** — tant que les secrets ne sont pas révoqués, tout le reste est secondaire.
|
||||
> Pour chaque ticket, fais un commit dédié avec le numéro dans le message (ex. `fix(T-001) : retirer .mcp.json du dépôt`).
|
||||
@@ -16,6 +16,7 @@
|
||||
"nyholm/psr7": "^1.8",
|
||||
"phpdocumentor/reflection-docblock": "^5.6",
|
||||
"phpstan/phpdoc-parser": "^2.3",
|
||||
"sentry/sentry-symfony": "^5.10",
|
||||
"symfony/asset": "8.0.*",
|
||||
"symfony/console": "8.0.*",
|
||||
"symfony/dotenv": "8.0.*",
|
||||
|
||||
Generated
+419
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5c54b1589d9e815f4c9b7e5e1d2d69c7",
|
||||
"content-hash": "beb5fa2114e6597caebb6a098de494c1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "api-platform/doctrine-common",
|
||||
@@ -2361,6 +2361,185 @@
|
||||
},
|
||||
"time": "2025-10-26T09:35:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.12.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d",
|
||||
"reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"ralouphie/getallheaders": "^3.0",
|
||||
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
||||
"symfony/polyfill-php80": "^1.25"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"http-interop/http-factory-tests": "1.1.0",
|
||||
"jshttp/mime-db": "1.54.0.1",
|
||||
"phpunit/phpunit": "^8.5.52 || ^9.6.34"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://sagikazarmark.hu"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.12.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-06-23T15:21:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.1.0",
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
||||
"rector/rector": "^2.0",
|
||||
"vimeo/psalm": "^4.3 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jean85\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alessandro Lai",
|
||||
"email": "alessandro.lai85@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to get pretty versions strings of installed dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"package",
|
||||
"release",
|
||||
"versions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
|
||||
},
|
||||
"time": "2025-03-19T14:43:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mcp/sdk",
|
||||
"version": "v0.4.0",
|
||||
@@ -3701,6 +3880,245 @@
|
||||
},
|
||||
"time": "2024-09-11T13:17:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^5 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"support": {
|
||||
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sentry",
|
||||
"version": "4.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-php.git",
|
||||
"reference": "662cb7a01a342a7f33780fc955ff4a028d8b785a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/662cb7a01a342a7f33780fc955ff4a028d8b785a",
|
||||
"reference": "662cb7a01a342a7f33780fc955ff4a028d8b785a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
|
||||
"jean85/pretty-package-versions": "^1.5|^2.0.4",
|
||||
"php": "^7.2|^8.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
"symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"raven/raven": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"carthage-software/mago": "1.30.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.4",
|
||||
"guzzlehttp/promises": "^2.0.3",
|
||||
"monolog/monolog": "^1.6|^2.0|^3.0",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"open-telemetry/api": "^1.0",
|
||||
"open-telemetry/exporter-otlp": "^1.0",
|
||||
"open-telemetry/sdk": "^1.0",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpunit/phpunit": "^8.5.52|^9.6.34",
|
||||
"spiral/roadrunner-http": "^3.6",
|
||||
"spiral/roadrunner-worker": "^3.6"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-excimer": "Enable Sentry profiling with the Excimer PHP extension.",
|
||||
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Sentry\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "PHP SDK for Sentry (http://sentry.io)",
|
||||
"homepage": "http://sentry.io",
|
||||
"keywords": [
|
||||
"crash-reporting",
|
||||
"crash-reports",
|
||||
"error-handler",
|
||||
"error-monitoring",
|
||||
"log",
|
||||
"logging",
|
||||
"profiling",
|
||||
"sentry",
|
||||
"tracing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-php/issues",
|
||||
"source": "https://github.com/getsentry/sentry-php/tree/4.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2026-06-11T12:22:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sentry-symfony",
|
||||
"version": "5.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-symfony.git",
|
||||
"reference": "6f49255f4cdcfc43a3a283bd3a1f65d483e9192f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-symfony/zipball/6f49255f4cdcfc43a3a283bd3a1f65d483e9192f",
|
||||
"reference": "6f49255f4cdcfc43a3a283bd3a1f65d483e9192f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/psr7": "^2.1.1",
|
||||
"jean85/pretty-package-versions": "^1.5||^2.0",
|
||||
"php": "^7.2||^8.0",
|
||||
"sentry/sentry": "^4.23.0",
|
||||
"symfony/cache-contracts": "^1.1||^2.4||^3.0",
|
||||
"symfony/config": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/console": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/dependency-injection": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/event-dispatcher": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/http-kernel": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/polyfill-php80": "^1.22",
|
||||
"symfony/psr-http-message-bridge": "^1.2||^2.0||^6.4||^7.0||^8.0",
|
||||
"symfony/yaml": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/dbal": "^2.13||^3.3||^4.0",
|
||||
"doctrine/doctrine-bundle": "^2.6||^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.19||^3.40",
|
||||
"masterminds/html5": "^2.8",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "1.12.5",
|
||||
"phpstan/phpstan-phpunit": "1.4.0",
|
||||
"phpstan/phpstan-symfony": "1.4.10",
|
||||
"phpunit/phpunit": "^8.5.40||^9.6.21",
|
||||
"symfony/browser-kit": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/cache": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/dom-crawler": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/framework-bundle": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/http-client": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/messenger": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/monolog-bundle": "^3.4||^4.0",
|
||||
"symfony/phpunit-bridge": "^5.2.6||^6.0||^7.0||^8.0",
|
||||
"symfony/process": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/security-core": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/security-http": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"symfony/twig-bundle": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0",
|
||||
"vimeo/psalm": "^4.3||^5.16.0"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/doctrine-bundle": "Allow distributed tracing of database queries using Sentry.",
|
||||
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler.",
|
||||
"symfony/cache": "Allow distributed tracing of cache pools using Sentry.",
|
||||
"symfony/twig-bundle": "Allow distributed tracing of Twig template rendering using Sentry."
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/aliases.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Sentry\\SentryBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "Symfony integration for Sentry (http://getsentry.com)",
|
||||
"homepage": "http://getsentry.com",
|
||||
"keywords": [
|
||||
"errors",
|
||||
"logging",
|
||||
"sentry",
|
||||
"symfony"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-symfony/issues",
|
||||
"source": "https://github.com/getsentry/sentry-symfony/tree/5.10.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-01T14:50:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/asset",
|
||||
"version": "v8.0.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ use DAMA\DoctrineTestBundle\DAMADoctrineTestBundle;
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
||||
use Nelmio\CorsBundle\NelmioCorsBundle;
|
||||
use Sentry\SentryBundle\SentryBundle;
|
||||
use Symfony\AI\McpBundle\McpBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use Symfony\Bundle\MonologBundle\MonologBundle;
|
||||
@@ -24,4 +25,5 @@ return [
|
||||
DAMADoctrineTestBundle::class => ['test' => true],
|
||||
McpBundle::class => ['all' => true],
|
||||
MonologBundle::class => ['all' => true],
|
||||
SentryBundle::class => ['prod' => true],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Error tracking → GlitchTip (compatible SDK Sentry).
|
||||
# Actif uniquement en prod (bundle enregistré prod-only dans bundles.php).
|
||||
# Si SENTRY_DSN est vide/non défini, le SDK est inerte (rien n'est envoyé).
|
||||
when@prod:
|
||||
parameters:
|
||||
# Valeur par défaut : DSN vide => Sentry désactivé tant qu'il n'est pas fourni.
|
||||
env(SENTRY_DSN): ''
|
||||
|
||||
sentry:
|
||||
dsn: '%env(SENTRY_DSN)%'
|
||||
# Capture des erreurs fatales PHP via le handler. On DÉSACTIVE le listener
|
||||
# kernel pour éviter les doublons avec le handler Monolog (ci-dessous) : les
|
||||
# exceptions du kernel sont déjà logguées par Symfony => remontées via Monolog.
|
||||
register_error_listener: false
|
||||
register_error_handler: true
|
||||
options:
|
||||
environment: '%env(APP_ENV)%'
|
||||
release: '%app.version%'
|
||||
# Pas d'APM/tracing (DuckDB hors périmètre du ticket #146).
|
||||
traces_sample_rate: 0.0
|
||||
# Ne pas remonter les 4xx HTTP comme des erreurs (bruit).
|
||||
ignore_exceptions:
|
||||
- Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
- Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
|
||||
- Symfony\Component\Security\Core\Exception\AccessDeniedException
|
||||
|
||||
# Handler Monolog -> Sentry : remonte les logs niveau ERROR+ comme Issues GlitchTip
|
||||
# (en plus des erreurs fatales). Les $logger->error(...) métier deviennent des Issues.
|
||||
# Le filtre ignore_exceptions ci-dessus s'applique aussi à ces événements.
|
||||
services:
|
||||
Sentry\Monolog\Handler:
|
||||
arguments:
|
||||
$hub: '@Sentry\State\HubInterface'
|
||||
$level: !php/const Monolog\Level::Error
|
||||
$bubble: true
|
||||
@@ -4,6 +4,11 @@
|
||||
# Files in the packages/ subdirectory configure your dependencies.
|
||||
# See also https://symfony.com/doc/current/service_container/import.html
|
||||
|
||||
# Expose le paramètre app.version (source unique, bumpé par le script de release) au
|
||||
# container — utilisé notamment comme "release" Sentry/GlitchTip dans sentry.yaml.
|
||||
imports:
|
||||
- { resource: version.yaml }
|
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||
parameters:
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '1.9.49'
|
||||
app.version: '1.9.52'
|
||||
|
||||
+23
-2
@@ -41,7 +41,20 @@ export default defineNuxtConfig({
|
||||
lucide: () => import('@iconify-json/lucide/icons.json').then(i => i.default)
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
// Error tracking → GlitchTip. Module chargé uniquement si un DSN est fourni
|
||||
// (build prod) ; en dev sans DSN, aucun overhead Sentry. Les options d'upload des
|
||||
// source maps sont passées en ligne (fournies au build via secrets CI).
|
||||
...(process.env.NUXT_PUBLIC_SENTRY_DSN
|
||||
? [['@sentry/nuxt/module', {
|
||||
sourceMapsUploadOptions: {
|
||||
url: process.env.SENTRY_URL,
|
||||
org: process.env.SENTRY_ORG,
|
||||
project: process.env.SENTRY_PROJECT,
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN
|
||||
}
|
||||
}] as [string, Record<string, unknown>]]
|
||||
: [])
|
||||
],
|
||||
runtimeConfig: {
|
||||
apiBaseUrl: process.env.NUXT_API_BASE_URL
|
||||
@@ -57,9 +70,17 @@ export default defineNuxtConfig({
|
||||
enableDebug: process.env.NUXT_PUBLIC_ENABLE_DEBUG || 'false',
|
||||
enableAnalytics: process.env.NUXT_PUBLIC_ENABLE_ANALYTICS || 'false',
|
||||
csrfToken: process.env.NUXT_PUBLIC_CSRF_TOKEN || '',
|
||||
logLevel: process.env.NUXT_PUBLIC_LOG_LEVEL || 'warn'
|
||||
logLevel: process.env.NUXT_PUBLIC_LOG_LEVEL || 'warn',
|
||||
sentry: {
|
||||
// DSN du projet GlitchTip "inventory-front" (vide => SDK inerte).
|
||||
dsn: process.env.NUXT_PUBLIC_SENTRY_DSN || '',
|
||||
environment: process.env.NODE_ENV || 'development'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Source maps "hidden" : générées et uploadées vers GlitchTip pour des stacktraces
|
||||
// lisibles, sans exposer les .map au navigateur.
|
||||
sourcemap: { client: 'hidden' },
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
server: {
|
||||
|
||||
Generated
+839
-15
@@ -8,6 +8,7 @@
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@sentry/nuxt": "^10.61.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"daisyui": "^5.0.48",
|
||||
"nuxt": "^4.0.1",
|
||||
@@ -70,6 +71,64 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@apm-js-collab/code-transformer": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.15.0.tgz",
|
||||
"integrity": "sha512-XmXYVs8CzJ1Aj79noVbn2weUO/XWtRyURpGqx7aU7DOXlUQhR0WKOQNF0okh7PCeY37vxf7kU3v57OAkEPm3ww==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.8",
|
||||
"astring": "^1.9.0",
|
||||
"esquery": "^1.7.0",
|
||||
"meriyah": "^6.1.4",
|
||||
"semifies": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
},
|
||||
"bin": {
|
||||
"code-transformer": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@apm-js-collab/code-transformer-bundler-plugins": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.5.0.tgz",
|
||||
"integrity": "sha512-YxLBY5nGlurL7QeJLq6e5g0ouBpAp0pwgyA/5rHXEXwhiPLn9ZHbT+Y2LlP90GT872cSocfjWRYu/fnpuBudNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apm-js-collab/code-transformer": "^0.15.0",
|
||||
"es-module-lexer": "^2.1.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"module-details-from-path": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apm-js-collab/code-transformer-bundler-plugins/node_modules/es-module-lexer": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz",
|
||||
"integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@apm-js-collab/code-transformer/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apm-js-collab/tracing-hooks": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.10.0.tgz",
|
||||
"integrity": "sha512-2/Z3NTewJTruUkmsSnBC5bJlLNUd9keuD1OLlTEpim4FyLhm6m2Rnfv+wrFdUvFfhmH8CRdiDZBqBrn+wyaGuA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@apm-js-collab/code-transformer": "^0.15.0",
|
||||
"debug": "^4.4.1",
|
||||
"module-details-from-path": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
@@ -2133,6 +2192,103 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
|
||||
"integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api-logs": {
|
||||
"version": "0.214.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz",
|
||||
"integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/core": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz",
|
||||
"integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation": {
|
||||
"version": "0.214.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz",
|
||||
"integrity": "sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.214.0",
|
||||
"import-in-the-middle": "^3.0.0",
|
||||
"require-in-the-middle": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/resources": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz",
|
||||
"integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.8.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-trace-base": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz",
|
||||
"integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.8.0",
|
||||
"@opentelemetry/resources": "2.8.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz",
|
||||
"integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-minify/binding-android-arm64": {
|
||||
"version": "0.87.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.87.0.tgz",
|
||||
@@ -3747,6 +3903,602 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sentry/babel-plugin-component-annotate": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-5.3.0.tgz",
|
||||
"integrity": "sha512-p4q8gn8wcFqZGP/s2MnJCAAd8fTikaU6A0mM97RDHQgStcrYiaS0Sc5zUNfb1V+UOLPuvdEdL6MwyxfzjYJQTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.62.0.tgz",
|
||||
"integrity": "sha512-uJi0yPssB3Nt/cZ8/S8opW42gaM59/6IyNtPFYD7C0ciudi/nIo5QMVpCYBBI3jnKFOIQLlsMT4pDlOLuxxNuQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser-utils": "10.62.0",
|
||||
"@sentry/core": "10.62.0",
|
||||
"@sentry/feedback": "10.62.0",
|
||||
"@sentry/replay": "10.62.0",
|
||||
"@sentry/replay-canvas": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser-utils": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser-utils/-/browser-utils-10.62.0.tgz",
|
||||
"integrity": "sha512-mS9HVVuWIdye9o0xUGFmzNOBqktF4n5kugrF8NCOYYDrr5ZV8Cx7BlquHQn5UpCeViVhZtcDlEm4iOK7++Px7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-5.3.0.tgz",
|
||||
"integrity": "sha512-L5T60sWdAI3qWwdg3Ptwek/0TY59PERrxyqp4XMUkroayQvGd9r5dIW9Q1kSeXX9iJ442nXbFZKAOyCKV4Z13Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@sentry/babel-plugin-component-annotate": "5.3.0",
|
||||
"@sentry/cli": "^2.58.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"find-up": "^5.0.0",
|
||||
"glob": "^13.0.6",
|
||||
"magic-string": "~0.30.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/lru-cache": {
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
|
||||
"integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.58.6.tgz",
|
||||
"integrity": "sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"progress": "^2.0.3",
|
||||
"proxy-from-env": "^1.1.0",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"sentry-cli": "bin/sentry-cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@sentry/cli-darwin": "2.58.6",
|
||||
"@sentry/cli-linux-arm": "2.58.6",
|
||||
"@sentry/cli-linux-arm64": "2.58.6",
|
||||
"@sentry/cli-linux-i686": "2.58.6",
|
||||
"@sentry/cli-linux-x64": "2.58.6",
|
||||
"@sentry/cli-win32-arm64": "2.58.6",
|
||||
"@sentry/cli-win32-i686": "2.58.6",
|
||||
"@sentry/cli-win32-x64": "2.58.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-darwin": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.58.6.tgz",
|
||||
"integrity": "sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA==",
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-arm": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.58.6.tgz",
|
||||
"integrity": "sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-arm64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.58.6.tgz",
|
||||
"integrity": "sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-i686": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.58.6.tgz",
|
||||
"integrity": "sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg==",
|
||||
"cpu": [
|
||||
"x86",
|
||||
"ia32"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-x64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.58.6.tgz",
|
||||
"integrity": "sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-arm64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.58.6.tgz",
|
||||
"integrity": "sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-i686": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.58.6.tgz",
|
||||
"integrity": "sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg==",
|
||||
"cpu": [
|
||||
"x86",
|
||||
"ia32"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-x64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.6.tgz",
|
||||
"integrity": "sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli/node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli/node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cloudflare": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cloudflare/-/cloudflare-10.62.0.tgz",
|
||||
"integrity": "sha512-oHDpXXiO3XpBO2cHiTRQpSrtQOQrsU9JsO3TZ6ukdd24IUE6Tkc3l7hWdwzKqId3nTWP1Ef0Fr+offsrEGJ6UA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudflare/workers-types": "^4.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/conventions": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.12.0.tgz",
|
||||
"integrity": "sha512-z1JQrl/1SLY+8wpzvork6vl+fpsg/oCCxM7HWWhUnI/R+OGNyoIzieQuggX3uUMY7NBtp8UWCQx6FeFazzOF9g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.62.0.tgz",
|
||||
"integrity": "sha512-tV69fMg2sS5DUFmQSnS7Jd5qJAp0izxwcsvBVz2ieTM9VMRi99IfOSYW9UYr3p1yfuksk41kefN5PEbeedUE+A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/feedback": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/feedback/-/feedback-10.62.0.tgz",
|
||||
"integrity": "sha512-d0BVjJVny6qpBgGJgWL0fbcoQHjtD3z3R8EK/KzTS3RO92JX5n3A536n5D/rh0gZFgcIwiUzBXegmyPOSQn9ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.62.0.tgz",
|
||||
"integrity": "sha512-4hoU67bJY0o3irEDMZu2UIztAOsvEqFkLXA7EUKl1LXMA3Ba1Lb32OUVqlsTypiEInSDs/BtM+aAFKojZ3P3Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/instrumentation": "^0.214.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.6.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"@sentry/core": "10.62.0",
|
||||
"@sentry/node-core": "10.62.0",
|
||||
"@sentry/opentelemetry": "10.62.0",
|
||||
"@sentry/server-utils": "10.62.0",
|
||||
"import-in-the-middle": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node-core": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.62.0.tgz",
|
||||
"integrity": "sha512-V7rDgbxViiHU0OpcFEDp3l41IFvWTasKHfXw8SQ6yIgtZ8VpFqmz2TR5N7X85iIOmWIvK5HV0yp0eDdsly0+rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/conventions": "^0.12.0",
|
||||
"@sentry/core": "10.62.0",
|
||||
"@sentry/opentelemetry": "10.62.0",
|
||||
"import-in-the-middle": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/core": "^1.30.1 || ^2.1.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1",
|
||||
"@opentelemetry/instrumentation": ">=0.57.1 <1",
|
||||
"@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/exporter-trace-otlp-http": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/instrumentation": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/sdk-trace-base": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/nuxt": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/nuxt/-/nuxt-10.62.0.tgz",
|
||||
"integrity": "sha512-YM9N4mH/uOJP/zr3QmQgCpQFaLzoDRh0/SoMMuNq/EEtzFZLQT6+qd5tYERMAutU4ySHinIaKYC2Gq/hEs5LtA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "^3.13.2",
|
||||
"@sentry/browser": "10.62.0",
|
||||
"@sentry/cloudflare": "10.62.0",
|
||||
"@sentry/core": "10.62.0",
|
||||
"@sentry/node": "10.62.0",
|
||||
"@sentry/node-core": "10.62.0",
|
||||
"@sentry/rollup-plugin": "^5.3.0",
|
||||
"@sentry/vite-plugin": "^5.3.0",
|
||||
"@sentry/vue": "10.62.0",
|
||||
"local-pkg": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.19.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"nitro": "2.x || 3.x",
|
||||
"nuxt": ">=3.7.0 || 4.x || 5.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"nitro": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/opentelemetry": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.62.0.tgz",
|
||||
"integrity": "sha512-nFwBgtjfwgY8P5lAuQFWfAsQW1MXxuQ6kR/HtBs+A6julqwGGS2QnQ65OCWMzz6IqDEL/pRgT1405/gU+OXU3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/conventions": "^0.12.0",
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/core": "^1.30.1 || ^2.1.0",
|
||||
"@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/replay": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-10.62.0.tgz",
|
||||
"integrity": "sha512-rWp4hBhZOmdQhisxcKzAwTGiRk/LvWnNaElWe7nbRhjsM/usp2095yfjq4iJ47v9MtO7xxY6eUz++fLBycqXKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser-utils": "10.62.0",
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/replay-canvas": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay-canvas/-/replay-canvas-10.62.0.tgz",
|
||||
"integrity": "sha512-CzPAxmpe5US/ABGA1TzpjFKOFZN5uqlzrRh/uM9/daVuzLVKIAQ0XRNxo/PPEXvlDm/PoMdI5L0qIODuIKnyyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.62.0",
|
||||
"@sentry/replay": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/rollup-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/rollup-plugin/-/rollup-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-hgPGPYdQJ/G1cGYOxAb7d4z3V+/k/E5/P/5TFPEEBLuIbFFk+JG0CISUDJdzXJjO382Lb99PBJuXGbueBmO79w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "5.3.0",
|
||||
"magic-string": "~0.30.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": ">=3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/server-utils": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.62.0.tgz",
|
||||
"integrity": "sha512-S5szsj6kKBhxw97b2HA98fYp/PpWXvSizlisEzb2rnL4IH6RAJ8wP05/fnth8pSywTH+gtUu+i6Wn8e8rX5HvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apm-js-collab/code-transformer": "^0.15.0",
|
||||
"@apm-js-collab/code-transformer-bundler-plugins": "^0.5.0",
|
||||
"@apm-js-collab/tracing-hooks": "^0.10.0",
|
||||
"@sentry/conventions": "^0.12.0",
|
||||
"@sentry/core": "10.62.0",
|
||||
"magic-string": "~0.30.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vite-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-qcoSzo4n2MulVQ70UUPLq6dTleb2a2HwL2wuwvAgWhPChrYTuk6A6mDg6aQb9fairPAwFPiU9PzOANpoDJcz1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "5.3.0",
|
||||
"@sentry/rollup-plugin": "5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vue": {
|
||||
"version": "10.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-10.62.0.tgz",
|
||||
"integrity": "sha512-aK3E302Zx/g1dqtUU30Q0jblvCW8MsVXuzwnxM4JSgO47o0jW74zaFh1K3Ym2uQWhLvP1rV2D49BYwCMUc4ovQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "10.62.0",
|
||||
"@sentry/core": "10.62.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/vue-router": "^1.64.0",
|
||||
"pinia": "2.x || 3.x",
|
||||
"vue": "2.x || 3.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@tanstack/vue-router": {
|
||||
"optional": true
|
||||
},
|
||||
"pinia": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.0.tgz",
|
||||
@@ -5451,6 +6203,15 @@
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/astring": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
|
||||
"integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"astring": "bin/astring"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
@@ -5958,6 +6719,12 @@
|
||||
"consola": "^3.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/cjs-module-lexer": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz",
|
||||
"integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/clean-regexp": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
|
||||
@@ -7481,10 +8248,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
|
||||
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
|
||||
"devOptional": true,
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
|
||||
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"estraverse": "^5.1.0"
|
||||
@@ -7510,7 +8276,6 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
@@ -7732,7 +8497,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"locate-path": "^6.0.0",
|
||||
@@ -8385,6 +9149,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/import-in-the-middle": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.2.0.tgz",
|
||||
"integrity": "sha512-vR2B6HKIhaBjcZr2bLpFiJ1VbzOlRQ7aby4/gw5WPIzToLjqpfWw3VJ4sk1uDchoOODEirvO2jyrSPtUSL5CrQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-import-attributes": "^1.9.5",
|
||||
"cjs-module-lexer": "^2.2.0",
|
||||
"module-details-from-path": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/impound": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/impound/-/impound-1.0.0.tgz",
|
||||
@@ -9514,7 +10293,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-locate": "^5.0.0"
|
||||
@@ -9670,6 +10448,15 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/meriyah": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz",
|
||||
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@@ -9768,10 +10555,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"license": "ISC",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
@@ -9829,6 +10616,12 @@
|
||||
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/module-details-from-path": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
|
||||
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
@@ -10185,6 +10978,7 @@
|
||||
"resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.1.2.tgz",
|
||||
"integrity": "sha512-g5mwszCZT4ZeGJm83nxoZvtvZoAEaY65VDdn7p7UgznePbRaEJJ1KS1OIld4FPVkoDZ8TEVuDNqI9gUn12Exvg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@nuxt/cli": "^3.28.0",
|
||||
"@nuxt/devalue": "^2.0.2",
|
||||
@@ -10618,7 +11412,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"yocto-queue": "^0.1.0"
|
||||
@@ -10634,7 +11427,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
|
||||
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-limit": "^3.0.2"
|
||||
@@ -10729,7 +11521,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -11694,6 +12485,15 @@
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@@ -11720,6 +12520,12 @@
|
||||
"integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -12011,6 +12817,19 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-in-the-middle": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz",
|
||||
"integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.5",
|
||||
"module-details-from-path": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=9.3.0 || >=8.10.0 <9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
@@ -12296,6 +13115,12 @@
|
||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semifies": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz",
|
||||
"integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
@@ -14604,7 +15429,6 @@
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@sentry/nuxt": "^10.61.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"daisyui": "^5.0.48",
|
||||
"nuxt": "^4.0.1",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import * as Sentry from '@sentry/nuxt'
|
||||
|
||||
// Init Sentry côté client (SPA). Le DSN provient du build prod (NUXT_PUBLIC_SENTRY_DSN).
|
||||
// Si le DSN est vide (dev), Sentry.init est un no-op : rien n'est envoyé.
|
||||
const config = useRuntimeConfig()
|
||||
const dsn = config.public.sentry?.dsn
|
||||
|
||||
if (dsn) {
|
||||
Sentry.init({
|
||||
dsn,
|
||||
environment: config.public.sentry?.environment,
|
||||
// Pas d'APM/tracing (hors périmètre ticket #146) : on ne remonte que les erreurs.
|
||||
tracesSampleRate: 0,
|
||||
// Pas de session replay (volume).
|
||||
replaysSessionSampleRate: 0,
|
||||
replaysOnErrorSampleRate: 0,
|
||||
})
|
||||
}
|
||||
@@ -8,3 +8,7 @@ DATABASE_URL="postgresql://inventory_user:password@host.docker.internal:5432/inv
|
||||
|
||||
# CORS
|
||||
CORS_ALLOW_ORIGIN='^https?://inventory\.malio-dev\.fr$'
|
||||
|
||||
# Sentry / GlitchTip — error tracking backend (projet "inventory-api").
|
||||
# Runtime, prod only. Vide/absent => SDK inerte (rien envoyé).
|
||||
# SENTRY_DSN=http://<clé>@<host-ou-IP>:<port>/<id-projet>
|
||||
|
||||
+23
-2
@@ -31,21 +31,42 @@ RUN npm ci
|
||||
|
||||
COPY frontend/ ./
|
||||
COPY config/version.yaml /app/config/version.yaml
|
||||
|
||||
# Error tracking → GlitchTip (build-time). Vides par défaut => module Sentry inerte
|
||||
# et aucun upload de source maps. Fournis par la CI via --build-arg (secrets Gitea).
|
||||
# Passés en préfixe inline du RUN (pas en ENV) pour ne pas persister le token dans
|
||||
# une couche d'image.
|
||||
ARG NUXT_PUBLIC_SENTRY_DSN=""
|
||||
ARG SENTRY_URL=""
|
||||
ARG SENTRY_ORG=""
|
||||
ARG SENTRY_PROJECT=""
|
||||
ARG SENTRY_AUTH_TOKEN=""
|
||||
|
||||
ENV CI=1 \
|
||||
NUXT_TELEMETRY_DISABLED=1 \
|
||||
NUXT_PUBLIC_API_BASE_URL=/api \
|
||||
NUXT_PUBLIC_APP_BASE=/
|
||||
RUN npm run generate
|
||||
RUN NUXT_PUBLIC_SENTRY_DSN="$NUXT_PUBLIC_SENTRY_DSN" \
|
||||
SENTRY_URL="$SENTRY_URL" \
|
||||
SENTRY_ORG="$SENTRY_ORG" \
|
||||
SENTRY_PROJECT="$SENTRY_PROJECT" \
|
||||
SENTRY_AUTH_TOKEN="$SENTRY_AUTH_TOKEN" \
|
||||
npm run generate
|
||||
|
||||
# --- Stage 3: Production image ---
|
||||
FROM php:8.4-fpm AS production
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libicu-dev libpq-dev libpng-dev libzip-dev libxml2-dev \
|
||||
nginx supervisor qpdf \
|
||||
nginx supervisor qpdf ca-certificates \
|
||||
&& docker-php-ext-install -j$(nproc) intl pdo_pgsql zip gd opcache \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# CA racine interne MALIO (auto-signée) — permet au SDK Sentry/HttpClient de
|
||||
# joindre les services HTTPS internes (ex. GlitchTip sur logs.malio-dev.fr).
|
||||
COPY infra/prod/malio-dev-root-ca.crt /usr/local/share/ca-certificates/malio-dev-root-ca.crt
|
||||
RUN update-ca-certificates
|
||||
|
||||
# PHP production config
|
||||
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFZzCCA0+gAwIBAgIUOiZigxwgIgtLipnLnu4eSgItc5MwDQYJKoZIhvcNAQEL
|
||||
BQAwQzELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU1BTElPLURFVjEgMB4GA1UEAwwX
|
||||
TUFMSU8tREVWIExvY2FsIFJvb3QgQ0EwHhcNMjYwNjI1MTYxMjIwWhcNMzYwNjIy
|
||||
MTYxMjIwWjBDMQswCQYDVQQGEwJGUjESMBAGA1UECgwJTUFMSU8tREVWMSAwHgYD
|
||||
VQQDDBdNQUxJTy1ERVYgTG9jYWwgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggIPADCCAgoCggIBALqHXVWEae9aKtveLfSpxYy9RS0Aslw2Ls9+LWI33lpMRs02
|
||||
QssE9wquf3WGjz8NnHUWl5RM0QHC0DOCCddcbnRBciDRJeTaU43IGdNg+TSY+7aM
|
||||
3t/jysZrpc/eu/udlIs7npCPaOGnRiuGN68Fkf9Q70FtmaASpusUe7J3jKDinznr
|
||||
R2hARplO4OF01tFauu039A4yudLrZTUFTldicuZ6a5U3NhajgfNZA+pyJqvL3tLT
|
||||
lXG3KupPD9BsbWe4zSM96CmyHM22QNlcL+M5XG5+EtDtM07tkDcyxFOsREjQHvSQ
|
||||
NH+7h6G/QBHHKkYJhdyiuvpj6b5tEJBM2PVgy1T2JX5TuOBOLx6HvHLbNjUY/JI5
|
||||
0sIjnHbeybQCOfnKNAwidtnqjAfVg+XJ9UZCiGJOeRJOdN5isvvqEKydsX4ouCTj
|
||||
89kwBbfCJeCS6BiadvNFUwnM0PksV0ovnOiUEEAPHRiP74jZ3IvH95BEwiZzyLpy
|
||||
tXiJMW7cJMaqlT3jNwq3P00irfrpJNy4S1Mg2cBQh5ucv+PcMBfQT8YiarzlTQJo
|
||||
saksh/2C43WH+qIFAL2aeD+rKReVBZcGa1XOBI8FUJTu3rLd37+iS4N2BUKq4fWo
|
||||
FttuX5NOfeU3BRDLlCJ2AXau7o0czVy896R9iZTfBJC95QWD07PdHgoctuexAgMB
|
||||
AAGjUzBRMB0GA1UdDgQWBBRNU0WsMg/pqo5XF/WXx78GrAzD5TAfBgNVHSMEGDAW
|
||||
gBRNU0WsMg/pqo5XF/WXx78GrAzD5TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
|
||||
DQEBCwUAA4ICAQBFXsuT7Rm2oJBlWT/RsJtmWr95NoFLHovVDycgM8Vjm+E8hv/m
|
||||
AcSjPjZDmXQLOrN31T/XUAs0nURHxSFgVzdIKpq2gOlGgHkZRMAW/iTON9Cqjn81
|
||||
Arjp5fjAJyFkoCiT3eTOElpteF4NhL8xMFaOg1Y2CEfOYO9OZR7Z38HdB6IArVwr
|
||||
W3Dxq3DPtarCeo1k8SHJmJzUduYCltV8urB43gIiI2Hqd7aAlpkTfDhruKxxr7sJ
|
||||
3/TpemJDCN9m8XMv2QvxqpMwH6EXg/7oqit5k0MvD445f3xt9vZydmV/x6F7u/A/
|
||||
gJitN+ixA4AKv7Lw210vaupiChqdY+78TXgLoPJ2/l2QPWG/R7Fb4yNZ2rEd6lyt
|
||||
KLPxHDcdZetFnyqyaoB2SNtLx9hNUE5G3udU6DkNhDfQlDhqEG4f7GAInOu/cMWE
|
||||
2uiIUEjcGSLM+XrrTFRc1tdXy6hnu+sw5ckvhwJ+kjah/pVGz21/y5a0p42AUznI
|
||||
iN7HBV8YaSkeJLvBPnfakUAat1R98e0l72DucHe8RF44NmZCywpaUBsTpNy+bO2f
|
||||
atqp4/ZEGJJlJ38rLv9bAuwr6d8x6T+m0oHknqtJHcWfO0kr4l3Lxsd8mRpGgmBe
|
||||
zOjqjrat4vSc04Rqic4UV2IEoWCiSS/TSiBx8JAB6Ck0+YR9dUgXVQsFFg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -109,6 +109,15 @@
|
||||
"bin/phpunit"
|
||||
]
|
||||
},
|
||||
"sentry/sentry-symfony": {
|
||||
"version": "5.10",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "5.0",
|
||||
"ref": "aac2bc5220e9ab5b9e3838a7a4da90e7f74e6148"
|
||||
}
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "8.0",
|
||||
"recipe": {
|
||||
|
||||
Reference in New Issue
Block a user