Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6eda77e84c | |||
| c7019550c1 | |||
| 48995d2869 | |||
| 86d1850f9b | |||
| a419a9f42a | |||
| f348918b79 | |||
| 26914469d3 | |||
| 9c9956b068 | |||
| a7f82b1215 | |||
| 62bb12f3a7 | |||
| a9035357ec | |||
| 17a9dc7b22 | |||
| e480c7ee1e | |||
| 5eba6de570 | |||
| 83b74ee0c1 | |||
| 62388bd92d | |||
| 89625e16f4 | |||
| 7aa2ddefdf | |||
| dfe2dfa7ed | |||
| ae0da47ebb | |||
| a431a17e67 | |||
| 4ab63ad068 | |||
| 6da53c3058 | |||
| 69c410a4c7 | |||
| d4a8ff261d | |||
| 352b941570 | |||
| 9905049ca1 | |||
| 95fd91cced | |||
| b75f209c35 | |||
| 2eddd7aa1a | |||
| de9e4cd337 | |||
| 5e5e5c0a71 | |||
| 324cd6603c | |||
| 7f4d4c35be | |||
| c31480886c | |||
| 9d836eeaac | |||
| fa0f2579ba | |||
| bdd77881a6 | |||
| e77be200c8 | |||
| c82447ba69 | |||
| 1e237fb5bc | |||
| 74a7f387c5 | |||
| 614abeb196 | |||
| 33668e5a64 | |||
| b909409c46 | |||
| f0baef0c22 | |||
| 9f6ffd9a07 | |||
| a850e5dd28 | |||
| 7d295e6883 | |||
| 9017313bf7 | |||
| 63096f22e4 | |||
| 6232c3584f | |||
| 5c8d6b9262 | |||
| 93cd8d38ff | |||
| 0850614264 | |||
| 772202dc6e | |||
| 6d7c536a7e | |||
| 4759ce99e7 | |||
| 3e08a647a5 | |||
| a76cf4e887 | |||
| 0e1cd727c4 | |||
| 86ccc3ef4f | |||
| e72923ec86 | |||
| e3794e1ba8 | |||
| ddc4c09323 | |||
| f9dd22909c | |||
| 51d8b0cc36 | |||
| c92d2c32e9 | |||
| c0ad39dbe9 | |||
| 359fee23b0 | |||
| 9330c68e5c | |||
| caff01fc4b | |||
| 25c7a0b304 | |||
| 67316ef0b1 | |||
| 67c75963fb | |||
| 4e855dc060 | |||
| 31b70e9849 | |||
| db83f0d06f | |||
| 91802b178b | |||
| cf4809be39 | |||
| 77ed26c527 | |||
| 5cedb52e4c | |||
| 303307092f | |||
| 7ec762cb3d | |||
| 90f7d3bfd6 | |||
| 6bd916f687 | |||
| 100b12a2fa | |||
| ade619232e | |||
| daec16f002 | |||
| e55f1283b8 | |||
| de17e5a004 | |||
| e97a6c47e9 | |||
| f84866c2d8 | |||
| c1ed0e37b0 | |||
| f611d2c080 |
25
.env
25
.env
|
|
@ -1,15 +1,28 @@
|
|||
DB_HOST=the-tip-top-db
|
||||
DB_HOST=51.75.24.29
|
||||
DB_PORT=5433
|
||||
DB_USER=postgres
|
||||
DB_PASS=postgres
|
||||
DB_NAME=thetiptop
|
||||
DB_NAME=thetiptop_dev
|
||||
PORT=4000
|
||||
JWT_SECRET=thetiptopsecret
|
||||
|
||||
GOOGLE_CLIENT_ID=546665126481-nmfcsbrjm6an7tbnv2jbspihgtu72ifn.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
FACEBOOK_APP_ID=ton_app_id_facebook
|
||||
FACEBOOK_APP_SECRET=ton_secret_facebook
|
||||
GOOGLE_CLIENT_ID=546665126481-itnlvt22hjn6t0bbgua0aj55h6dpplsk.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
FACEBOOK_APP_ID=836681122652445
|
||||
FACEBOOK_APP_SECRET=e6889f4339d140c218f1df177149893f
|
||||
|
||||
JWT_SECRET=thetiptopsecret
|
||||
SESSION_SECRET=thetiptopsessionsecret
|
||||
|
||||
# Frontend URL (pour les liens dans les emails)
|
||||
FRONTEND_URL=http://localhost:3000
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=thetiptopgr3@gmail.com
|
||||
SMTP_PASS=xydqvyrxcwwsiups
|
||||
EMAIL_FROM=thetiptopgr3@gmail.com
|
||||
|
||||
# reCAPTCHA v2 (obtenir les clés sur https://www.google.com/recaptcha/admin)
|
||||
RECAPTCHA_SECRET_KEY=6Le2Gx8sAAAAABh58WaZuajmkQsf4imY8ees_Zaq
|
||||
|
|
|
|||
32
.env.dev
Normal file
32
.env.dev
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Environnement DEV
|
||||
NODE_ENV=development
|
||||
DB_HOST=51.75.24.29
|
||||
DB_PORT=5433
|
||||
DB_USER=postgres
|
||||
DB_PASS=postgres
|
||||
DB_NAME=thetiptop_dev
|
||||
PORT=4000
|
||||
|
||||
# URLs Frontend/Backend
|
||||
FRONTEND_URL=https://dev.dsp5-archi-o24a-15m-g3.fr
|
||||
BACKEND_URL=https://api.dev.dsp5-archi-o24a-15m-g3.fr
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=thetiptopsecret_dev
|
||||
SESSION_SECRET=thetiptopsessionsecret_dev
|
||||
|
||||
# OAuth (à configurer avec les URLs de dev)
|
||||
GOOGLE_CLIENT_ID=546665126481-itnlvt22hjn6t0bbgua0aj55h6dpplsk.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
FACEBOOK_APP_ID=836681122652445
|
||||
FACEBOOK_APP_SECRET=e6889f4339d140c218f1df177149893f
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=thetiptopgr3@gmail.com
|
||||
SMTP_PASS=xydqvyrxcwwsiups
|
||||
EMAIL_FROM=thetiptopgr3@gmail.com
|
||||
|
||||
# reCAPTCHA v2
|
||||
RECAPTCHA_SECRET_KEY=6Le2Gx8sAAAAABh58WaZuajmkQsf4imY8ees_Zaq
|
||||
32
.env.preprod
Normal file
32
.env.preprod
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Environnement PREPROD
|
||||
NODE_ENV=preproduction
|
||||
DB_HOST=51.75.24.29
|
||||
DB_PORT=5434
|
||||
DB_USER=postgres
|
||||
DB_PASS=postgres
|
||||
DB_NAME=thetiptop_preprod
|
||||
PORT=4000
|
||||
|
||||
# URLs Frontend/Backend
|
||||
FRONTEND_URL=https://preprod.dsp5-archi-o24a-15m-g3.fr
|
||||
BACKEND_URL=https://api.preprod.dsp5-archi-o24a-15m-g3.fr
|
||||
|
||||
# JWT (Secrets générés automatiquement)
|
||||
JWT_SECRET=koN619HNfIgBKrCRl6o1feaVg30GPHQR+4DvbCoxmlA=
|
||||
SESSION_SECRET=DXgw+RHWLjW40G33fe5NqPnzwMkKf7bVxx3wZROkypM=
|
||||
|
||||
# OAuth
|
||||
GOOGLE_CLIENT_ID=546665126481-itnlvt22hjn6t0bbgua0aj55h6dpplsk.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
FACEBOOK_APP_ID=836681122652445
|
||||
FACEBOOK_APP_SECRET=e6889f4339d140c218f1df177149893f
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=thetiptopgr3@gmail.com
|
||||
SMTP_PASS=xydqvyrxcwwsiups
|
||||
EMAIL_FROM=thetiptopgr3@gmail.com
|
||||
|
||||
# reCAPTCHA v2
|
||||
RECAPTCHA_SECRET_KEY=6Le2Gx8sAAAAABh58WaZuajmkQsf4imY8ees_Zaq
|
||||
32
.env.production
Normal file
32
.env.production
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Environnement PRODUCTION
|
||||
NODE_ENV=production
|
||||
DB_HOST=51.75.24.29
|
||||
DB_PORT=5432
|
||||
DB_USER=postgres
|
||||
DB_PASS=postgres
|
||||
DB_NAME=thetiptop
|
||||
PORT=4000
|
||||
|
||||
# URLs Frontend/Backend
|
||||
FRONTEND_URL=https://dsp5-archi-o24a-15m-g3.fr
|
||||
BACKEND_URL=https://api.dsp5-archi-o24a-15m-g3.fr
|
||||
|
||||
# JWT (Secrets générés automatiquement)
|
||||
JWT_SECRET=v0vIxcahaOdJMzqoKjRpCGkRFyVTVDJyk7s2F1LIXu0=
|
||||
SESSION_SECRET=BzTTnx+GxsBpdVMcBKH5Vo6gH7h+/7tlZ67nppNduoY=
|
||||
|
||||
# OAuth
|
||||
GOOGLE_CLIENT_ID=546665126481-itnlvt22hjn6t0bbgua0aj55h6dpplsk.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
FACEBOOK_APP_ID=836681122652445
|
||||
FACEBOOK_APP_SECRET=e6889f4339d140c218f1df177149893f
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=thetiptopgr3@gmail.com
|
||||
SMTP_PASS=xydqvyrxcwwsiups
|
||||
EMAIL_FROM=thetiptopgr3@gmail.com
|
||||
|
||||
# reCAPTCHA v2
|
||||
RECAPTCHA_SECRET_KEY=6Le2Gx8sAAAAABh58WaZuajmkQsf4imY8ees_Zaq
|
||||
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env.prod
|
||||
.env.dev
|
||||
.env.preprod
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Test
|
||||
*.test.js.snap
|
||||
64
.sonarignore
Normal file
64
.sonarignore
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
**/node_modules/**
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Database
|
||||
database/
|
||||
*.sql
|
||||
|
||||
# Scripts
|
||||
scripts/
|
||||
*.sh
|
||||
*.bat
|
||||
*.ps1
|
||||
|
||||
# Configuration files
|
||||
*.config.js
|
||||
jest.config.js
|
||||
eslint.config.js
|
||||
|
||||
# Environment files
|
||||
.env*
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
backend.log
|
||||
|
||||
# Test files
|
||||
test-*.js
|
||||
*.test.js
|
||||
*.spec.js
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
|
||||
# CI/CD
|
||||
Jenkinsfile
|
||||
.github/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
postman-collection.json
|
||||
|
||||
# Public files
|
||||
public/
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.bak
|
||||
*.backup
|
||||
1100
API-DOCUMENTATION.md
Normal file
1100
API-DOCUMENTATION.md
Normal file
File diff suppressed because it is too large
Load Diff
197
DEPLOYMENT-WORKFLOW.md
Normal file
197
DEPLOYMENT-WORKFLOW.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# 🚀 Workflow de Déploiement - Thé Tip Top
|
||||
|
||||
## 📊 Vue d'ensemble des environnements
|
||||
|
||||
| Environnement | Branche | Base de données | Port DB | URLs |
|
||||
|---------------|---------|-----------------|---------|------|
|
||||
| **DEV** | `dev` | `thetiptop_dev` | 5433 | https://dev.dsp5-archi-o24a-15m-g3.fr<br>https://api.dev.dsp5-archi-o24a-15m-g3.fr |
|
||||
| **PREPROD** | `preprod` | `thetiptop_preprod` | 5434 | https://preprod.dsp5-archi-o24a-15m-g3.fr<br>https://api.preprod.dsp5-archi-o24a-15m-g3.fr |
|
||||
| **PROD** | `main` | `thetiptop` | 5432 | https://dsp5-archi-o24a-15m-g3.fr<br>https://api.dsp5-archi-o24a-15m-g3.fr |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Workflow Git
|
||||
|
||||
```
|
||||
dev → preprod → main
|
||||
↓ ↓ ↓
|
||||
DEV PREPROD PROD
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Procédure de déploiement
|
||||
|
||||
### 1️⃣ **Développement sur DEV**
|
||||
|
||||
```bash
|
||||
# Vous êtes sur la branche dev
|
||||
git checkout dev
|
||||
|
||||
# Faire vos modifications...
|
||||
|
||||
# Commit et push vers dev
|
||||
git add .
|
||||
git commit -m "feat: description de la fonctionnalité"
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
**Déploiement DEV :**
|
||||
```bash
|
||||
# Sur le serveur DEV
|
||||
cd /path/to/backend
|
||||
git pull origin dev
|
||||
cp .env.dev .env
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ **Déploiement vers PREPROD**
|
||||
|
||||
Après avoir testé sur DEV et validé les fonctionnalités :
|
||||
|
||||
```bash
|
||||
# Merger dev vers preprod
|
||||
git checkout preprod
|
||||
git pull origin preprod
|
||||
git merge dev
|
||||
git push origin preprod
|
||||
```
|
||||
|
||||
**Déploiement PREPROD :**
|
||||
```bash
|
||||
# Sur le serveur PREPROD
|
||||
cd /path/to/backend
|
||||
git pull origin preprod
|
||||
cp .env.preprod .env
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**⚠️ Tests à effectuer en PREPROD :**
|
||||
- [ ] Toutes les fonctionnalités principales
|
||||
- [ ] Authentification (Google, Facebook)
|
||||
- [ ] Jeu et attribution des lots
|
||||
- [ ] Interface admin
|
||||
- [ ] Tirage au sort
|
||||
- [ ] Performance et sécurité
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ **Déploiement en PRODUCTION**
|
||||
|
||||
Après validation complète en PREPROD :
|
||||
|
||||
```bash
|
||||
# Merger preprod vers main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git merge preprod
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Déploiement PRODUCTION :**
|
||||
```bash
|
||||
# Sur le serveur PRODUCTION
|
||||
cd /path/to/backend
|
||||
git pull origin main
|
||||
cp .env.production .env
|
||||
|
||||
# ⚠️ IMPORTANT : Vérifier les secrets en production
|
||||
nano .env # Vérifier JWT_SECRET, SESSION_SECRET, etc.
|
||||
|
||||
npm install
|
||||
npm run build # Si nécessaire
|
||||
npm start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Gestion des bases de données
|
||||
|
||||
### Migration de données DEV → PREPROD
|
||||
|
||||
```bash
|
||||
# Dump de la base DEV
|
||||
pg_dump -h 51.75.24.29 -p 5433 -U postgres -d thetiptop_dev > dev_dump.sql
|
||||
|
||||
# Restaurer en PREPROD
|
||||
psql -h 51.75.24.29 -p 5434 -U postgres -d thetiptop_preprod < dev_dump.sql
|
||||
```
|
||||
|
||||
### Migration de données PREPROD → PROD
|
||||
|
||||
```bash
|
||||
# Dump de la base PREPROD
|
||||
pg_dump -h 51.75.24.29 -p 5434 -U postgres -d thetiptop_preprod > preprod_dump.sql
|
||||
|
||||
# ⚠️ BACKUP PROD avant restauration !
|
||||
pg_dump -h 51.75.24.29 -p 5432 -U postgres -d thetiptop > prod_backup_$(date +%Y%m%d).sql
|
||||
|
||||
# Restaurer en PROD
|
||||
psql -h 51.75.24.29 -p 5432 -U postgres -d thetiptop < preprod_dump.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Configuration des secrets
|
||||
|
||||
### Secrets à changer ABSOLUMENT en PRODUCTION :
|
||||
|
||||
- `JWT_SECRET` - Générer avec : `openssl rand -base64 32`
|
||||
- `SESSION_SECRET` - Générer avec : `openssl rand -base64 32`
|
||||
- `NEXTAUTH_SECRET` - Générer avec : `openssl rand -base64 32`
|
||||
|
||||
### OAuth en production :
|
||||
|
||||
1. **Google OAuth** : Créer un nouveau projet sur Google Cloud Console avec les URLs de prod
|
||||
2. **Facebook OAuth** : Créer une nouvelle app Facebook avec les URLs de prod
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist avant déploiement PRODUCTION
|
||||
|
||||
- [ ] Tous les tests passent en PREPROD
|
||||
- [ ] Les secrets ont été changés (JWT, SESSION)
|
||||
- [ ] OAuth configuré avec les URLs de production
|
||||
- [ ] Backup de la base de données PROD effectué
|
||||
- [ ] Variables d'environnement vérifiées
|
||||
- [ ] CORS configuré avec les domaines de production
|
||||
- [ ] Logs configurés pour la production
|
||||
- [ ] Plan de rollback préparé
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Rollback en cas de problème
|
||||
|
||||
### Si problème en PRODUCTION :
|
||||
|
||||
```bash
|
||||
# 1. Revenir à la version précédente
|
||||
git checkout main
|
||||
git reset --hard <commit-précédent>
|
||||
git push origin main --force
|
||||
|
||||
# 2. Restaurer le backup de la base de données
|
||||
psql -h 51.75.24.29 -p 5432 -U postgres -d thetiptop < prod_backup_YYYYMMDD.sql
|
||||
|
||||
# 3. Redémarrer l'application
|
||||
npm start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
En cas de problème, vérifier :
|
||||
1. Les logs du serveur
|
||||
2. Les logs de la base de données
|
||||
3. Les variables d'environnement
|
||||
4. La configuration CORS
|
||||
5. Les certificats SSL
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour** : $(date +%Y-%m-%d)
|
||||
|
|
@ -1,11 +1,16 @@
|
|||
# Utilise une image Node légère
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copie les fichiers
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY . .
|
||||
|
||||
# Définit la variable d’environnement
|
||||
ENV NODE_ENV=production
|
||||
EXPOSE 4000
|
||||
|
||||
CMD ["npm", "start"]
|
||||
|
|
|
|||
95
JENKINS_AUTO_TRIGGER_SETUP.md
Normal file
95
JENKINS_AUTO_TRIGGER_SETUP.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Configuration du Declenchement Automatique Jenkins
|
||||
|
||||
## Probleme Actuel
|
||||
Le pipeline Jenkins ne se lance pas automatiquement apres un push Git.
|
||||
|
||||
## Solutions Possibles
|
||||
|
||||
### Solution 1: Verifier le Plugin Generic Webhook Trigger
|
||||
|
||||
1. Aller dans Jenkins > Manage Jenkins > Manage Plugins
|
||||
2. Chercher "Generic Webhook Trigger Plugin"
|
||||
3. Si pas installe, l'installer et redemarrer Jenkins
|
||||
|
||||
### Solution 2: Configuration du Webhook dans Gitea
|
||||
|
||||
#### Etape 1: Creer le webhook dans Gitea
|
||||
1. Aller sur https://gitea.wk-archi-o24a-15m-g3.fr/wkadmin/the-tip-top-backend
|
||||
2. Settings > Webhooks > Add Webhook > Gitea
|
||||
3. Configurer:
|
||||
- **Target URL**: `http://jenkins-url/generic-webhook-trigger/invoke?token=the-tip-top-backend-token`
|
||||
- **HTTP Method**: POST
|
||||
- **POST Content Type**: application/json
|
||||
- **Secret**: (laisser vide)
|
||||
- **Trigger On**: Push events
|
||||
- **Branch filter**: `dev` (ou `*` pour toutes les branches)
|
||||
- **Active**: Coche
|
||||
|
||||
4. Cliquer sur "Add Webhook"
|
||||
|
||||
#### Etape 2: Tester le webhook
|
||||
1. Faire un commit et push
|
||||
2. Dans Gitea, aller dans Settings > Webhooks
|
||||
3. Cliquer sur le webhook cree
|
||||
4. Scroller vers le bas pour voir "Recent Deliveries"
|
||||
5. Verifier que la reponse est 200 OK
|
||||
|
||||
### Solution 3: Verifier la Configuration SCM dans Jenkins
|
||||
|
||||
1. Aller dans le job Jenkins: `the-tip-top-backend`
|
||||
2. Configure > Build Triggers
|
||||
3. Verifier que "Poll SCM" est coche avec: `* * * * *`
|
||||
4. Verifier que "Generic Webhook Trigger" est configure
|
||||
|
||||
### Solution 4: Forcer un Scan Manuel (Temporaire)
|
||||
|
||||
En attendant que le webhook fonctionne:
|
||||
1. Aller dans le job Jenkins
|
||||
2. Cliquer sur "Build with Parameters"
|
||||
3. Choisir ENV = dev
|
||||
4. Cliquer sur "Build"
|
||||
|
||||
### Solution 5: Utiliser un Simple Webhook au lieu de Generic Webhook
|
||||
|
||||
Si le plugin Generic Webhook Trigger pose probleme, modifier le Jenkinsfile:
|
||||
|
||||
```groovy
|
||||
triggers {
|
||||
// Polling SCM toutes les minutes
|
||||
pollSCM('* * * * *')
|
||||
|
||||
// OU utiliser le trigger Gitea (plus simple)
|
||||
// Pas besoin de plugin supplementaire
|
||||
}
|
||||
|
||||
properties([
|
||||
pipelineTriggers([
|
||||
[$class: 'GitHubPushTrigger']
|
||||
])
|
||||
])
|
||||
```
|
||||
|
||||
Puis dans Gitea webhook, utiliser:
|
||||
- **Target URL**: `http://jenkins-url/git/notifyCommit?url=https://gitea.wk-archi-o24a-15m-g3.fr/wkadmin/the-tip-top-backend.git`
|
||||
|
||||
## Verification
|
||||
|
||||
Pour verifier que le trigger fonctionne:
|
||||
|
||||
1. Faire un petit changement (ex: ajouter un commentaire dans README.md)
|
||||
2. Commit et push
|
||||
3. Attendre 1 minute maximum
|
||||
4. Verifier dans Jenkins si un nouveau build demarre
|
||||
|
||||
## Logs de Debug
|
||||
|
||||
Si le probleme persiste, verifier les logs Jenkins:
|
||||
1. Jenkins > Manage Jenkins > System Log
|
||||
2. Chercher les erreurs liees a "SCM" ou "webhook"
|
||||
|
||||
## Contact
|
||||
|
||||
Si aucune solution ne fonctionne:
|
||||
- Verifier que Jenkins a acces au repo Git (credentials)
|
||||
- Verifier les firewalls entre Gitea et Jenkins
|
||||
- Verifier que l'URL du webhook est accessible depuis Gitea
|
||||
169
JENKINS_WEBHOOK_SETUP.md
Normal file
169
JENKINS_WEBHOOK_SETUP.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Configuration du Webhook Jenkins pour Auto-déploiement (Backend)
|
||||
|
||||
## 📋 Vue d'ensemble
|
||||
|
||||
Le Jenkinsfile a été configuré avec deux méthodes de déclenchement automatique :
|
||||
|
||||
1. **Polling SCM** (Simple mais moins efficace)
|
||||
2. **Webhook Gitea** (Recommandé - plus rapide et efficace)
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Option 1 : Polling SCM (Déjà actif)
|
||||
|
||||
Le pipeline vérifie automatiquement les changements **toutes les minutes**.
|
||||
|
||||
**Avantages :**
|
||||
- ✅ Aucune configuration supplémentaire nécessaire
|
||||
- ✅ Fonctionne immédiatement
|
||||
|
||||
**Inconvénients :**
|
||||
- ❌ Délai de détection (jusqu'à 1 minute)
|
||||
- ❌ Charge sur le serveur Git
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Option 2 : Webhook Gitea (Recommandé)
|
||||
|
||||
### Configuration dans Gitea
|
||||
|
||||
1. **Accéder aux paramètres du dépôt :**
|
||||
- Aller sur : `https://gitea.wk-archi-o24a-15m-g3.fr/wkadmin/the-tip-top-backend`
|
||||
- Cliquer sur **Paramètres** (Settings) → **Webhooks**
|
||||
|
||||
2. **Créer un nouveau webhook :**
|
||||
- Cliquer sur **Ajouter un Webhook** → **Gitea**
|
||||
- Remplir les champs suivants :
|
||||
|
||||
```
|
||||
URL cible : http://<JENKINS_URL>/generic-webhook-trigger/invoke?token=the-tip-top-backend-token
|
||||
|
||||
Méthode HTTP : POST
|
||||
|
||||
Type de contenu : application/json
|
||||
|
||||
Secret : (laisser vide ou utiliser un secret personnalisé)
|
||||
|
||||
Événements déclencheurs :
|
||||
☑ Push
|
||||
☐ Create
|
||||
☐ Delete
|
||||
☐ Fork
|
||||
☐ Issues
|
||||
☐ Pull Request
|
||||
☐ Release
|
||||
|
||||
Actif : ☑ Oui
|
||||
```
|
||||
|
||||
3. **Remplacer `<JENKINS_URL>` par :**
|
||||
- L'URL de votre serveur Jenkins (ex: `jenkins.wk-archi-o24a-15m-g3.fr`)
|
||||
|
||||
4. **Tester le webhook :**
|
||||
- Après création, cliquer sur le webhook
|
||||
- Cliquer sur **Tester la livraison**
|
||||
- Vérifier que le statut est **200 OK**
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Sécurité
|
||||
|
||||
Le token `the-tip-top-backend-token` dans le Jenkinsfile protège le webhook contre les déclenchements non autorisés.
|
||||
|
||||
Pour changer le token :
|
||||
1. Modifier la ligne dans `Jenkinsfile` :
|
||||
```groovy
|
||||
token: 'votre-nouveau-token-secret'
|
||||
```
|
||||
2. Mettre à jour l'URL du webhook dans Gitea
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Tester le déclenchement automatique
|
||||
|
||||
1. Faire un commit et push :
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "test: trigger auto deployment"
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
2. Vérifier dans Jenkins :
|
||||
- Le build devrait démarrer automatiquement
|
||||
- Consulter les logs pour voir le message : `Triggered by Gitea push`
|
||||
|
||||
---
|
||||
|
||||
## 📊 Branches et Environnements
|
||||
|
||||
Le pipeline détecte automatiquement l'environnement selon la branche :
|
||||
|
||||
| Branche | Environnement | Tag Docker |
|
||||
|-----------|---------------|---------------|
|
||||
| `dev` | dev | dev-latest |
|
||||
| `preprod` | preprod | preprod-latest|
|
||||
| `main` | prod | prod-latest |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Plugin Jenkins requis
|
||||
|
||||
Pour utiliser le webhook avec `GenericTrigger`, assurez-vous que le plugin suivant est installé :
|
||||
|
||||
- **Generic Webhook Trigger Plugin**
|
||||
|
||||
Installation :
|
||||
1. Jenkins → Manage Jenkins → Manage Plugins
|
||||
2. Onglet "Available" → Rechercher "Generic Webhook Trigger"
|
||||
3. Installer et redémarrer Jenkins
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
- Le polling SCM (`* * * * *`) restera actif comme solution de secours
|
||||
- Vous pouvez le désactiver une fois le webhook configuré en commentant la ligne `pollSCM`
|
||||
- Les deux méthodes peuvent coexister sans problème
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Étapes de déploiement automatique
|
||||
|
||||
Lorsqu'un push est détecté, le pipeline exécute automatiquement :
|
||||
|
||||
1. **Init** - Détection de l'environnement (dev/preprod/prod)
|
||||
2. **Checkout** - Récupération du code
|
||||
3. **Tests & Qualité** - Linting et tests unitaires
|
||||
4. **Build Docker** - Construction de l'image Docker
|
||||
5. **Push Registry** - Envoi vers le registre Docker
|
||||
6. **Backup** - Sauvegarde de l'environnement actuel
|
||||
7. **Deploy** - Déploiement de la nouvelle version
|
||||
|
||||
---
|
||||
|
||||
## ❓ Troubleshooting
|
||||
|
||||
### Le webhook ne se déclenche pas
|
||||
|
||||
1. Vérifier les logs du webhook dans Gitea (Paramètres → Webhooks → Livraisons)
|
||||
2. Vérifier que Jenkins est accessible depuis le serveur Gitea
|
||||
3. Vérifier que le token correspond entre Jenkinsfile et l'URL du webhook
|
||||
4. Vérifier les logs Jenkins pour les erreurs
|
||||
|
||||
### Le build démarre mais échoue
|
||||
|
||||
1. Vérifier les credentials Docker registry
|
||||
2. Vérifier les permissions d'accès au répertoire de déploiement
|
||||
3. Vérifier la connexion à la base de données
|
||||
4. Consulter les logs détaillés du build dans Jenkins
|
||||
|
||||
### La base de données n'est pas accessible
|
||||
|
||||
Si vous voyez l'erreur `connect ETIMEDOUT 51.75.24.29:5433` :
|
||||
1. Vérifier que PostgreSQL est démarré sur le serveur
|
||||
2. Vérifier les règles de firewall (port 5433)
|
||||
3. Vérifier les credentials dans le fichier `.env`
|
||||
|
||||
---
|
||||
|
||||
Généré avec [Claude Code](https://claude.com/claude-code)
|
||||
296
Jenkinsfile
vendored
296
Jenkinsfile
vendored
|
|
@ -1,25 +1,75 @@
|
|||
/**
|
||||
* ============================================================================
|
||||
* PIPELINE CI/CD - THÉ TIP TOP BACKEND
|
||||
* ============================================================================
|
||||
*
|
||||
* Ce pipeline automatise le processus de déploiement continu du backend.
|
||||
*
|
||||
* ÉTAPES DU PIPELINE :
|
||||
* 1. Init - Détection de l'environnement (dev/preprod/prod)
|
||||
* 2. Checkout - Récupération du code source depuis Git
|
||||
* 3. Install - Installation des dépendances Node.js
|
||||
* 4. Lint & Tests - ESLint + Jest avec couverture de code
|
||||
* 5. SonarQube - Analyse statique avec rapport de couverture
|
||||
* 6. Build - Construction de l'image Docker
|
||||
* 7. Push - Envoi de l'image vers le registre Docker privé
|
||||
* 8. Backup - Sauvegarde de la base de données (prod uniquement)
|
||||
* 9. Deploy - Déploiement sur le serveur cible
|
||||
*
|
||||
* ENVIRONNEMENTS :
|
||||
* - dev : Développement (branche dev)
|
||||
* - preprod : Pré-production (branche preprod)
|
||||
* - prod : Production (branche main)
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
// =========================================================================
|
||||
// DÉCLENCHEUR : Vérifie les changements Git chaque minute
|
||||
// =========================================================================
|
||||
triggers {
|
||||
pollSCM('* * * * *')
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// PARAMÈTRES : Permet de choisir manuellement l'environnement
|
||||
// =========================================================================
|
||||
parameters {
|
||||
choice(
|
||||
name: 'ENV',
|
||||
choices: ['dev', 'preprod', 'prod'],
|
||||
description: 'Choisir l’environnement de déploiement'
|
||||
description: 'Choisir l environnement de deploiement'
|
||||
)
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// VARIABLES D'ENVIRONNEMENT GLOBALES
|
||||
// =========================================================================
|
||||
environment {
|
||||
REGISTRY_URL = "registry.wk-archi-o24a-15m-g3.fr"
|
||||
IMAGE_NAME = "the-tip-top-backend"
|
||||
NPM_CACHE = "/var/jenkins_home/npm-cache"
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
stage('Init') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 1 : INITIALISATION
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Détecter automatiquement l'environnement selon la branche Git
|
||||
// - Branche 'dev' → Environnement dev
|
||||
// - Branche 'preprod' → Environnement preprod
|
||||
// - Branche 'main' → Environnement prod
|
||||
// =====================================================================
|
||||
stage('🧭 Init - Détection environnement') {
|
||||
steps {
|
||||
script {
|
||||
def currentBranch = sh(script: "git rev-parse --abbrev-ref HEAD", returnStdout: true).trim()
|
||||
// Utiliser BRANCH_NAME (Multibranch Pipeline) ou GIT_BRANCH (fallback)
|
||||
// git rev-parse --abbrev-ref HEAD retourne "HEAD" en detached HEAD mode
|
||||
def currentBranch = env.BRANCH_NAME ?: env.GIT_BRANCH?.replaceAll('origin/', '') ?: sh(script: "git rev-parse --abbrev-ref HEAD", returnStdout: true).trim()
|
||||
echo "🧭 Branche détectée : ${currentBranch}"
|
||||
|
||||
if (["dev", "preprod", "main"].contains(currentBranch)) {
|
||||
|
|
@ -32,53 +82,143 @@ pipeline {
|
|||
env.DEPLOY_PATH = "/srv/devops/the-tip-top/${env.ENV}"
|
||||
|
||||
echo """
|
||||
🌍 Environnement = ${env.ENV}
|
||||
🏷️ Tag Docker = ${env.TAG}
|
||||
📂 Chemin de déploiement = ${env.DEPLOY_PATH}
|
||||
╔══════════════════════════════════════════╗
|
||||
║ CONFIGURATION PIPELINE ║
|
||||
╠══════════════════════════════════════════╣
|
||||
║ 🌍 Environnement : ${env.ENV.padRight(18)} ║
|
||||
║ 🏷️ Tag Docker : ${env.TAG.padRight(18)} ║
|
||||
║ 📂 Chemin déploie. : ${env.DEPLOY_PATH.take(18).padRight(18)} ║
|
||||
╚══════════════════════════════════════════╝
|
||||
"""
|
||||
|
||||
sh "ls -l ${env.DEPLOY_PATH} || echo '⚠️ Dossier non accessible depuis Jenkins'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Checkout') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 2 : CHECKOUT
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Récupérer le code source depuis le dépôt Gitea
|
||||
// Action : Clone ou pull du code selon l'état du workspace
|
||||
// =====================================================================
|
||||
stage('📦 Checkout - Récupération code source') {
|
||||
steps {
|
||||
echo "📦 Récupération du code source..."
|
||||
echo "📦 Récupération du code source depuis Gitea..."
|
||||
checkout scm
|
||||
echo "✅ Code source récupéré avec succès"
|
||||
}
|
||||
}
|
||||
|
||||
stage('Tests & Qualité') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 3 : INSTALLATION DES DÉPENDANCES
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Installer les packages Node.js nécessaires
|
||||
// Container : node:18-alpine (léger et rapide)
|
||||
// Commande : npm ci (installation propre depuis package-lock.json)
|
||||
// Cache : Utilise un cache NPM partagé pour accélérer les builds
|
||||
// =====================================================================
|
||||
stage('📦 Install - Dépendances Node.js') {
|
||||
agent {
|
||||
docker {
|
||||
image 'node:18-alpine'
|
||||
args "-u root -v ${NPM_CACHE}:/root/.npm"
|
||||
}
|
||||
}
|
||||
steps {
|
||||
echo "📦 Installation des dépendances Node.js..."
|
||||
sh 'npm ci --prefer-offline'
|
||||
stash includes: 'node_modules/**', name: 'node_modules'
|
||||
echo "✅ Dépendances installées avec succès"
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ÉTAPE 4 : SONARQUBE - Qualité de code
|
||||
// ---------------------------------------------------------------------
|
||||
// Analyse statique du code pour détecter :
|
||||
// - Bugs potentiels
|
||||
// - Vulnérabilités de sécurité
|
||||
// - Code smells (mauvaises pratiques)
|
||||
// - Duplications de code
|
||||
// =====================================================================
|
||||
stage('📊 SonarQube Analysis') {
|
||||
agent {
|
||||
docker {
|
||||
image 'sonarsource/sonar-scanner-cli:latest'
|
||||
args '-u root'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
echo "🧪 Lancement des tests et analyse..."
|
||||
sh '''
|
||||
npm ci
|
||||
npm run lint || echo "⚠️ Erreurs de lint détectées"
|
||||
npm test || echo "⚠️ Tests échoués — ignorés pour le déploiement"
|
||||
'''
|
||||
echo "📊 Analyse SonarQube - Qualité de code..."
|
||||
withSonarQubeEnv('SonarQube') {
|
||||
sh """
|
||||
sonar-scanner
|
||||
"""
|
||||
}
|
||||
echo "✅ Analyse SonarQube terminée"
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Docker image') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 5 : LINT & TESTS
|
||||
// ---------------------------------------------------------------------
|
||||
// ESLint : Vérifie le style et les erreurs de code
|
||||
// Jest : Exécute les tests unitaires et d'intégration
|
||||
// Couverture : Génère un rapport de couverture (lcov)
|
||||
// =====================================================================
|
||||
stage('🧪 Lint & Tests') {
|
||||
agent {
|
||||
docker {
|
||||
image 'node:18-alpine'
|
||||
args "-u root -v ${NPM_CACHE}:/root/.npm"
|
||||
}
|
||||
}
|
||||
steps {
|
||||
echo "🐳 Construction de l’image Docker backend..."
|
||||
unstash 'node_modules'
|
||||
echo "🧪 Lancement ESLint et Jest..."
|
||||
script {
|
||||
def lintStatus = sh(script: 'npm run lint', returnStatus: true)
|
||||
def testStatus = sh(script: 'npm test -- --coverage', returnStatus: true)
|
||||
|
||||
if (lintStatus != 0) {
|
||||
error "❌ ESLint a échoué - Corrigez les erreurs de style"
|
||||
}
|
||||
if (testStatus != 0) {
|
||||
error "❌ Les tests ont échoué - Vérifiez les tests unitaires"
|
||||
}
|
||||
echo "✅ Lint et tests passés avec succès"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ÉTAPE 6 : BUILD IMAGE DOCKER
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Construire l'image Docker du backend
|
||||
// Tags créés :
|
||||
// - {env}-latest (ex: dev-latest, prod-latest)
|
||||
// - latest
|
||||
// =====================================================================
|
||||
stage('🐳 Build - Image Docker') {
|
||||
steps {
|
||||
echo "🐳 Construction de l'image Docker backend..."
|
||||
sh """
|
||||
docker build -t ${REGISTRY_URL}/${IMAGE_NAME}:${TAG} .
|
||||
docker tag ${REGISTRY_URL}/${IMAGE_NAME}:${TAG} ${REGISTRY_URL}/${IMAGE_NAME}:latest
|
||||
"""
|
||||
echo "✅ Image Docker construite : ${REGISTRY_URL}/${IMAGE_NAME}:${TAG}"
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push to Registry') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 7 : PUSH VERS LE REGISTRE
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Envoyer l'image Docker vers le registre privé
|
||||
// Registre : registry.wk-archi-o24a-15m-g3.fr
|
||||
// Authentification : Credentials Jenkins sécurisés
|
||||
// =====================================================================
|
||||
stage('📤 Push - Registre Docker') {
|
||||
steps {
|
||||
echo "📤 Envoi de l’image vers le registre..."
|
||||
echo "📤 Envoi de l'image vers le registre Docker privé..."
|
||||
withCredentials([usernamePassword(credentialsId: 'registry-credentials', usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) {
|
||||
sh """
|
||||
echo "$REG_PASS" | docker login ${REGISTRY_URL} -u "$REG_USER" --password-stdin
|
||||
|
|
@ -86,51 +226,109 @@ pipeline {
|
|||
docker push ${REGISTRY_URL}/${IMAGE_NAME}:latest
|
||||
"""
|
||||
}
|
||||
echo "✅ Image envoyée avec succès vers ${REGISTRY_URL}"
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy') {
|
||||
// =====================================================================
|
||||
// ÉTAPE 8 : BACKUP BASE DE DONNÉES (Production uniquement)
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Sauvegarder la base de données avant le déploiement
|
||||
// Condition : Seulement en environnement de production
|
||||
// Script : /srv/devops/the-tip-top/backup.sh
|
||||
// Sécurité : Continue même si le backup échoue (warning)
|
||||
// =====================================================================
|
||||
stage('💾 Backup - Base de données') {
|
||||
when {
|
||||
expression { env.ENV == 'prod' }
|
||||
}
|
||||
steps {
|
||||
echo "🚀 Déploiement sur ${env.ENV}..."
|
||||
echo "💾 Sauvegarde de la base de données PostgreSQL..."
|
||||
sh """
|
||||
/srv/devops/the-tip-top/backup.sh || echo "⚠️ Backup échoué mais on continue"
|
||||
"""
|
||||
echo "✅ Backup de la base de données terminé"
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ÉTAPE 9 : DÉPLOIEMENT
|
||||
// ---------------------------------------------------------------------
|
||||
// But : Déployer le backend sur le serveur cible
|
||||
// Actions :
|
||||
// 1. Pull de la nouvelle image depuis le registre
|
||||
// 2. Recréation du container avec la nouvelle image
|
||||
// Chemins : /srv/devops/the-tip-top/{dev|preprod|prod}
|
||||
// =====================================================================
|
||||
stage('🚀 Deploy - Mise en production') {
|
||||
steps {
|
||||
echo "🚀 Déploiement du backend sur l'environnement ${env.ENV}..."
|
||||
sh """
|
||||
cd "${DEPLOY_PATH}"
|
||||
docker compose pull backend
|
||||
docker compose up -d --force-recreate backend
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Health Check') {
|
||||
steps {
|
||||
echo "🩺 Vérification de l’état du backend..."
|
||||
script {
|
||||
def domain = (env.ENV == 'dev') ? "api.dev.dsp5-archi-o24a-15m-g3.fr" :
|
||||
(env.ENV == 'preprod') ? "api.preprod.dsp5-archi-o24a-15m-g3.fr" :
|
||||
"api.dsp5-archi-o24a-15m-g3.fr"
|
||||
|
||||
def statusCode = "000"
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
statusCode = sh(script: "curl -k -s -o /dev/null -w '%{http_code}' https://${domain}/health || echo 000", returnStdout: true).trim()
|
||||
if (statusCode in ['200', '301', '302']) {
|
||||
echo "✅ Backend ${env.ENV} opérationnel (HTTP ${statusCode})"
|
||||
return
|
||||
}
|
||||
|
||||
echo "⏳ Tentative ${i}/10 → HTTP ${statusCode}"
|
||||
sleep 5
|
||||
}
|
||||
error("❌ Health check échoué - code HTTP ${statusCode}")
|
||||
}
|
||||
echo "✅ Backend déployé avec succès sur ${env.ENV}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// ACTIONS POST-PIPELINE
|
||||
// =========================================================================
|
||||
post {
|
||||
success {
|
||||
echo "✅ Pipeline ${env.ENV} terminé avec succès !"
|
||||
echo """
|
||||
╔══════════════════════════════════════════╗
|
||||
║ ✅ PIPELINE TERMINÉ AVEC SUCCÈS ║
|
||||
╠══════════════════════════════════════════╣
|
||||
║ Environnement : ${env.ENV.padRight(23)} ║
|
||||
║ Image : ${IMAGE_NAME.padRight(23)} ║
|
||||
║ Tag : ${env.TAG.padRight(23)} ║
|
||||
╚══════════════════════════════════════════╝
|
||||
"""
|
||||
emailext(
|
||||
to: 'soufiane.baali99@gmail.com',
|
||||
subject: "✅ Pipeline Backend SUCCÈS - ${env.ENV}",
|
||||
body: """
|
||||
<h2 style="color: green;">✅ Pipeline Backend terminé avec succès</h2>
|
||||
<table border="1" cellpadding="10" style="border-collapse: collapse;">
|
||||
<tr><td><strong>Projet</strong></td><td>${env.JOB_NAME}</td></tr>
|
||||
<tr><td><strong>Build</strong></td><td>#${env.BUILD_NUMBER}</td></tr>
|
||||
<tr><td><strong>Environnement</strong></td><td>${env.ENV}</td></tr>
|
||||
<tr><td><strong>Image</strong></td><td>${IMAGE_NAME}:${env.TAG}</td></tr>
|
||||
<tr><td><strong>Durée</strong></td><td>${currentBuild.durationString}</td></tr>
|
||||
</table>
|
||||
<p>🔗 <a href="${env.BUILD_URL}">Voir les détails du build</a></p>
|
||||
""",
|
||||
mimeType: 'text/html'
|
||||
)
|
||||
}
|
||||
failure {
|
||||
echo "❌ Échec du pipeline ${env.ENV}."
|
||||
echo """
|
||||
╔══════════════════════════════════════════╗
|
||||
║ ❌ ÉCHEC DU PIPELINE ║
|
||||
╠══════════════════════════════════════════╣
|
||||
║ Environnement : ${env.ENV.padRight(23)} ║
|
||||
║ Vérifiez les logs pour plus de détails ║
|
||||
╚══════════════════════════════════════════╝
|
||||
"""
|
||||
emailext(
|
||||
to: 'soufiane.baali99@gmail.com',
|
||||
subject: "❌ Pipeline Backend ÉCHEC - ${env.ENV}",
|
||||
body: """
|
||||
<h2 style="color: red;">❌ Pipeline Backend a échoué</h2>
|
||||
<table border="1" cellpadding="10" style="border-collapse: collapse;">
|
||||
<tr><td><strong>Projet</strong></td><td>${env.JOB_NAME}</td></tr>
|
||||
<tr><td><strong>Build</strong></td><td>#${env.BUILD_NUMBER}</td></tr>
|
||||
<tr><td><strong>Environnement</strong></td><td>${env.ENV}</td></tr>
|
||||
<tr><td><strong>Durée</strong></td><td>${currentBuild.durationString}</td></tr>
|
||||
</table>
|
||||
<p>🔗 <a href="${env.BUILD_URL}">Voir les logs du build</a></p>
|
||||
<p>🔗 <a href="${env.BUILD_URL}console">Console Output</a></p>
|
||||
""",
|
||||
mimeType: 'text/html'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
126
NEWSLETTER_SETUP.md
Normal file
126
NEWSLETTER_SETUP.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Newsletter Feature - Installation Guide
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
La fonctionnalité newsletter a été ajoutée au backend et frontend. Elle permet aux utilisateurs de s'abonner à la newsletter depuis le footer du site.
|
||||
|
||||
## Backend
|
||||
|
||||
### Fichiers créés :
|
||||
|
||||
1. **Migration de base de données** : `database/migrations/add-newsletter-table.sql`
|
||||
2. **Controller** : `src/controllers/newsletter.controller.js`
|
||||
3. **Service** : `src/services/newsletter.service.js`
|
||||
4. **Routes** : `src/routes/newsletter.routes.js`
|
||||
5. **Validation** : `src/validations/newsletter.validation.js`
|
||||
|
||||
### Endpoints API :
|
||||
|
||||
- `POST /api/newsletter/subscribe` - S'abonner (public)
|
||||
- `POST /api/newsletter/unsubscribe` - Se désabonner (public)
|
||||
- `GET /api/newsletter/subscribers` - Liste des abonnés (Admin seulement)
|
||||
- `GET /api/newsletter/count` - Nombre d'abonnés actifs (Admin seulement)
|
||||
|
||||
## Frontend
|
||||
|
||||
### Fichiers modifiés/créés :
|
||||
|
||||
1. **Service** : `services/newsletter.service.ts`
|
||||
2. **Constants** : `utils/constants.ts` (ajout des endpoints newsletter)
|
||||
3. **Footer** : `components/Footer.tsx` (ajout du formulaire d'inscription)
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Appliquer la migration de base de données
|
||||
|
||||
Exécutez le script SQL pour créer la table `newsletters` :
|
||||
|
||||
```bash
|
||||
# Depuis le répertoire backend
|
||||
psql -h 51.75.24.29 -U postgres -d thetiptop_dev -p 5433 -f database/migrations/add-newsletter-table.sql
|
||||
```
|
||||
|
||||
Ou connectez-vous à votre base de données et exécutez manuellement le contenu du fichier `database/migrations/add-newsletter-table.sql`.
|
||||
|
||||
### 2. Redémarrer le backend
|
||||
|
||||
```bash
|
||||
# Dans le répertoire backend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 3. Redémarrer le frontend
|
||||
|
||||
```bash
|
||||
# Dans le répertoire frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
### Pour les utilisateurs :
|
||||
|
||||
- Formulaire d'inscription à la newsletter dans le footer
|
||||
- Validation d'email en temps réel
|
||||
- Messages de confirmation/erreur
|
||||
- Email de confirmation d'abonnement automatique
|
||||
- Possibilité de se désabonner
|
||||
|
||||
### Pour les administrateurs :
|
||||
|
||||
- Voir la liste complète des abonnés
|
||||
- Voir le nombre d'abonnés actifs
|
||||
- API endpoints protégés par authentification et rôle ADMIN
|
||||
|
||||
## Emails
|
||||
|
||||
Les emails de confirmation sont envoyés automatiquement lors de l'inscription. Configuration SMTP requise dans le fichier `.env` :
|
||||
|
||||
```env
|
||||
SMTP_HOST=your-smtp-host
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your-smtp-user
|
||||
SMTP_PASS=your-smtp-password
|
||||
EMAIL_FROM=noreply@thetiptop.fr
|
||||
```
|
||||
|
||||
En mode développement sans configuration SMTP, les emails sont affichés dans la console.
|
||||
|
||||
## Test
|
||||
|
||||
### Test manuel :
|
||||
|
||||
1. Ouvrez le frontend (http://localhost:3000)
|
||||
2. Scrollez jusqu'au footer
|
||||
3. Entrez votre email dans le champ "Newsletter"
|
||||
4. Cliquez sur "S'inscrire"
|
||||
5. Vérifiez le message de confirmation
|
||||
|
||||
### Test API avec curl :
|
||||
|
||||
```bash
|
||||
# S'abonner
|
||||
curl -X POST http://localhost:4000/api/newsletter/subscribe \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email": "test@example.com"}'
|
||||
|
||||
# Nombre d'abonnés (nécessite un token admin)
|
||||
curl -X GET http://localhost:4000/api/newsletter/count \
|
||||
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
|
||||
```
|
||||
|
||||
## Sécurité
|
||||
|
||||
- Les endpoints publics (subscribe/unsubscribe) ne nécessitent pas d'authentification
|
||||
- Les endpoints admin (subscribers/count) nécessitent un token JWT et le rôle ADMIN
|
||||
- Validation Zod sur les entrées
|
||||
- Protection contre les doublons d'email
|
||||
- Sanitisation des données
|
||||
|
||||
## Prochaines étapes possibles
|
||||
|
||||
- Ajout d'une page de gestion de newsletter pour les admins
|
||||
- Export de la liste des abonnés en CSV
|
||||
- Système de campagnes email
|
||||
- Segmentation des abonnés
|
||||
- Statistiques d'engagement
|
||||
159
README.md
Normal file
159
README.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# The Tip Top Backend API
|
||||
|
||||
API backend pour le jeu-concours The Tip Top.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
Le fichier `.env` contient la configuration de la base de données et de l'application :
|
||||
|
||||
```env
|
||||
DB_HOST=51.75.24.29
|
||||
DB_PORT=5433
|
||||
DB_USER=postgres
|
||||
DB_PASS=postgres
|
||||
DB_NAME=thetiptop_dev
|
||||
PORT=4000
|
||||
JWT_SECRET=thetiptopsecret
|
||||
SESSION_SECRET=thetiptopsessionsecret
|
||||
```
|
||||
|
||||
**Note** : La base de données PostgreSQL est hébergée sur le serveur distant `51.75.24.29:5433`.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Installer les dépendances
|
||||
npm install
|
||||
|
||||
# Créer le schéma de la base de données
|
||||
psql -U postgres -d thetiptop_dev -p 5433 -f database/schema.sql
|
||||
|
||||
# Insérer les données de test
|
||||
npm run db:seed
|
||||
```
|
||||
|
||||
## Démarrage
|
||||
|
||||
### Démarrage normal
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
### Démarrage en mode développement (avec nodemon)
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Arrêter le serveur
|
||||
|
||||
**Sur Windows :**
|
||||
1. Ouvrez le Gestionnaire des tâches (Ctrl + Shift + Esc)
|
||||
2. Cherchez les processus "Node.js"
|
||||
3. Terminez-les tous
|
||||
|
||||
**Ou via PowerShell :**
|
||||
```powershell
|
||||
Get-Process node | Stop-Process -Force
|
||||
```
|
||||
|
||||
## Structure du projet
|
||||
|
||||
```
|
||||
the-tip-top-backend/
|
||||
├── database/
|
||||
│ ├── schema.sql # Schéma de la base de données
|
||||
│ ├── seed.js # Script de seed
|
||||
│ └── README.md # Documentation DB
|
||||
├── src/
|
||||
│ ├── config/
|
||||
│ │ └── env.js # Configuration environnement
|
||||
│ ├── controllers/ # Contrôleurs (logique métier)
|
||||
│ ├── middleware/ # Middlewares (auth, validation, erreurs)
|
||||
│ ├── routes/ # Définition des routes
|
||||
│ ├── services/ # Services (email, etc.)
|
||||
│ ├── utils/ # Utilitaires
|
||||
│ └── validations/ # Schémas de validation Zod
|
||||
├── db.js # Configuration PostgreSQL
|
||||
├── index.js # Point d'entrée de l'application
|
||||
└── .env # Variables d'environnement
|
||||
```
|
||||
|
||||
## Routes API
|
||||
|
||||
### Authentification (`/api/auth`)
|
||||
- `POST /register` - Inscription
|
||||
- `POST /login` - Connexion
|
||||
- `GET /verify-email/:token` - Vérification email
|
||||
- `POST /forgot-password` - Demande de réinitialisation
|
||||
- `POST /reset-password` - Réinitialisation du mot de passe
|
||||
|
||||
### Utilisateur (`/api/users`)
|
||||
- `GET /profile` - Récupérer le profil
|
||||
- `PUT /profile` - Mettre à jour le profil
|
||||
- `POST /change-password` - Changer le mot de passe
|
||||
|
||||
### Jeu (`/api/game`)
|
||||
- `POST /play` - Jouer et obtenir un ticket
|
||||
- `GET /my-tickets` - Mes tickets
|
||||
- `GET /ticket/:code` - Détails d'un ticket
|
||||
|
||||
### Employé (`/api/employee`)
|
||||
- `POST /validate-ticket` - Valider un ticket
|
||||
- `GET /pending-tickets` - Tickets en attente
|
||||
|
||||
### Admin (`/api/admin`)
|
||||
- `GET /statistics` - Statistiques globales
|
||||
- `GET /prizes` - Liste des prix
|
||||
- `POST /prizes` - Créer un prix
|
||||
- `PUT /prizes/:id` - Modifier un prix
|
||||
- `DELETE /prizes/:id` - Supprimer un prix
|
||||
- `GET /users` - Liste des utilisateurs
|
||||
- `POST /employees` - Créer un employé
|
||||
- `PUT /users/:id` - Modifier un utilisateur
|
||||
- `DELETE /users/:id` - Supprimer un utilisateur
|
||||
- `GET /tickets` - Liste des tickets
|
||||
|
||||
## Comptes de test
|
||||
|
||||
Après avoir exécuté le seed :
|
||||
|
||||
| Email | Mot de passe | Rôle |
|
||||
|-------|-------------|------|
|
||||
| admin@thetiptop.com | Admin123! | ADMIN |
|
||||
| employee1@thetiptop.com | Employee123! | EMPLOYEE |
|
||||
| employee2@thetiptop.com | Employee123! | EMPLOYEE |
|
||||
| client1@example.com | Client123! | CLIENT |
|
||||
| client2-5@example.com | Client123! | CLIENT |
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
# Tests unitaires
|
||||
npm test
|
||||
|
||||
# Tests d'intégration
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
## Endpoints de monitoring
|
||||
|
||||
- `GET /` - Health check
|
||||
- `GET /db-check` - Vérification connexion DB
|
||||
- `GET /metrics` - Métriques Prometheus
|
||||
|
||||
## Technologies
|
||||
|
||||
- **Express** - Framework web
|
||||
- **PostgreSQL** - Base de données
|
||||
- **JWT** - Authentification
|
||||
- **Bcrypt** - Hashage de mots de passe
|
||||
- **Zod** - Validation des données
|
||||
- **Nodemailer** - Envoi d'emails
|
||||
- **Helmet** - Sécurité HTTP
|
||||
- **CORS** - Gestion des origines
|
||||
- **Morgan** - Logs HTTP
|
||||
- **Prometheus** - Métriques
|
||||
44
RESTART-ALL.ps1
Normal file
44
RESTART-ALL.ps1
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " RESTART BACKEND AND FRONTEND" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Step 1: Stopping all Node.js processes..." -ForegroundColor Yellow
|
||||
$nodeProcesses = Get-Process node -ErrorAction SilentlyContinue
|
||||
if ($nodeProcesses) {
|
||||
$nodeProcesses | Stop-Process -Force
|
||||
Write-Host "[OK] Stopped $($nodeProcesses.Count) Node.js process(es)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] No Node.js processes running" -ForegroundColor Gray
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Waiting 3 seconds for ports to be released..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 3
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Step 2: Starting Backend..." -ForegroundColor Yellow
|
||||
$backendPath = "C:\Users\LENOVO\Back\the-tip-top-backend"
|
||||
Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd '$backendPath'; npm start" -WindowStyle Normal
|
||||
Write-Host "[OK] Backend starting..." -ForegroundColor Green
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Waiting 5 seconds for backend to initialize..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Step 3: Starting Frontend..." -ForegroundColor Yellow
|
||||
$frontendPath = "C:\Users\LENOVO\front\the-tip-top-frontend"
|
||||
Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd '$frontendPath'; npm run dev" -WindowStyle Normal
|
||||
Write-Host "[OK] Frontend starting..." -ForegroundColor Green
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " RESTART COMPLETE" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Backend: http://localhost:4000" -ForegroundColor White
|
||||
Write-Host "Frontend: http://localhost:3004" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "Press any key to close this window..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
39
RESTART-FORCE.bat
Normal file
39
RESTART-FORCE.bat
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
@echo off
|
||||
cls
|
||||
echo.
|
||||
echo ========================================
|
||||
echo ARRET FORCE DE TOUS LES NODE.JS
|
||||
echo ========================================
|
||||
echo.
|
||||
echo Etape 1: Arret de TOUS les processus Node...
|
||||
echo.
|
||||
|
||||
REM Tuer tous les processus Node
|
||||
taskkill /F /IM node.exe /T 2>nul
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo [OK] Processus Node arretes
|
||||
) else (
|
||||
echo [INFO] Aucun processus Node trouve
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Attente de 5 secondes...
|
||||
timeout /t 5 /nobreak >nul
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo DEMARRAGE DU BACKEND
|
||||
echo ========================================
|
||||
echo.
|
||||
echo Dossier: C:\Users\LENOVO\Back\the-tip-top-backend
|
||||
echo Commande: npm start
|
||||
echo.
|
||||
echo Le backend va demarrer dans 3 secondes...
|
||||
timeout /t 3 /nobreak >nul
|
||||
|
||||
cd /d "C:\Users\LENOVO\Back\the-tip-top-backend"
|
||||
echo.
|
||||
echo *** BACKEND EN COURS DE DEMARRAGE ***
|
||||
echo.
|
||||
npm start
|
||||
14
backend.log
Normal file
14
backend.log
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
> backend@1.0.0 start
|
||||
> node index.js
|
||||
|
||||
[dotenv@17.2.3] injecting env (12) from .env -- tip: ⚙️ specify custom .env file path with { path: '/custom/path/.env' }
|
||||
🧩 DB Config → {
|
||||
host: '51.75.24.29',
|
||||
port: '5433',
|
||||
user: 'postgres',
|
||||
pass: '***',
|
||||
database: 'thetiptop_dev'
|
||||
}
|
||||
[dotenv@17.2.3] injecting env (0) from .env -- tip: ⚙️ suppress all logs with { quiet: true }
|
||||
🚀 Backend lancé sur 0.0.0.0:4000 ✅
|
||||
10
create-test-users.sql
Normal file
10
create-test-users.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
-- Script pour créer des utilisateurs de test avec différents rôles
|
||||
-- À exécuter dans PostgreSQL
|
||||
|
||||
-- Promouvoir les utilisateurs existants
|
||||
UPDATE users SET role = 'ADMIN' WHERE email = 'admin@test.com';
|
||||
UPDATE users SET role = 'EMPLOYEE' WHERE email = 'employee@test.com';
|
||||
|
||||
-- Vérifier les utilisateurs
|
||||
SELECT email, role, is_verified FROM users
|
||||
WHERE email IN ('test-client@test.com', 'employee@test.com', 'admin@test.com');
|
||||
113
database/README.md
Normal file
113
database/README.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Database Setup
|
||||
|
||||
Ce dossier contient le schéma SQL et le script de seed pour la base de données PostgreSQL.
|
||||
|
||||
## Structure
|
||||
|
||||
- `schema.sql` - Schéma complet de la base de données (tables, index, triggers, vues)
|
||||
- `seed.js` - Script pour insérer des données de test
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Créer le schéma
|
||||
|
||||
Exécutez le fichier SQL pour créer toutes les tables et insérer les données initiales (prix, configuration du jeu) :
|
||||
|
||||
```bash
|
||||
psql -U postgres -d thetiptop -f database/schema.sql
|
||||
```
|
||||
|
||||
Ou utilisez le script npm :
|
||||
|
||||
```bash
|
||||
npm run db:schema
|
||||
```
|
||||
|
||||
### 2. Insérer les données de test
|
||||
|
||||
Exécutez le script de seed pour créer des utilisateurs et tickets de test :
|
||||
|
||||
```bash
|
||||
npm run db:seed
|
||||
```
|
||||
|
||||
## Données de test créées
|
||||
|
||||
Le script de seed crée :
|
||||
|
||||
### Utilisateurs
|
||||
|
||||
| Email | Mot de passe | Rôle |
|
||||
|-------|-------------|------|
|
||||
| admin@thetiptop.com | Admin123! | ADMIN |
|
||||
| employee1@thetiptop.com | Employee123! | EMPLOYEE |
|
||||
| employee2@thetiptop.com | Employee123! | EMPLOYEE |
|
||||
| client1@example.com | Client123! | CLIENT |
|
||||
| client2@example.com | Client123! | CLIENT |
|
||||
| client3@example.com | Client123! | CLIENT |
|
||||
| client4@example.com | Client123! | CLIENT |
|
||||
| client5@example.com | Client123! | CLIENT |
|
||||
|
||||
### Tickets
|
||||
|
||||
- Environ 15-25 tickets répartis entre les clients
|
||||
- 80% en statut PENDING
|
||||
- 10% en statut CLAIMED
|
||||
- 10% en statut REJECTED
|
||||
- Prix distribués aléatoirement
|
||||
|
||||
## Schéma de la base de données
|
||||
|
||||
### Tables principales
|
||||
|
||||
#### users
|
||||
Stocke tous les utilisateurs (clients, employés, admins)
|
||||
- Authentification par email/password
|
||||
- Vérification par email
|
||||
- Récupération de mot de passe
|
||||
|
||||
#### prizes
|
||||
Les différents prix disponibles dans le jeu-concours
|
||||
- 5 types de prix (INFUSEUR, THE_SIGNATURE, COFFRET_DECOUVERTE, COFFRET_PRESTIGE, THE_GRATUIT)
|
||||
- Stock et probabilités configurables
|
||||
- Gestion du statut actif/inactif
|
||||
|
||||
#### tickets
|
||||
Les tickets de jeu générés pour chaque participation
|
||||
- Code unique
|
||||
- Statuts: PENDING, CLAIMED, REJECTED
|
||||
- Lié à un utilisateur et un prix
|
||||
- Traçabilité de la validation
|
||||
|
||||
#### game_settings
|
||||
Configuration générale du jeu-concours
|
||||
- Dates de début et fin
|
||||
- Nombre total de tickets
|
||||
- Statut actif/inactif
|
||||
|
||||
### Vues utiles
|
||||
|
||||
#### prize_statistics
|
||||
Statistiques agrégées par prix (stock initial, utilisé, restant, etc.)
|
||||
|
||||
#### pending_tickets
|
||||
Liste des tickets en attente de validation avec toutes les informations nécessaires
|
||||
|
||||
## Réinitialisation
|
||||
|
||||
Pour réinitialiser complètement la base de données :
|
||||
|
||||
```bash
|
||||
# 1. Supprimer et recréer le schéma
|
||||
npm run db:schema
|
||||
|
||||
# 2. Insérer les données de test
|
||||
npm run db:seed
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Le schéma utilise des UUID pour tous les IDs
|
||||
- Les triggers mettent automatiquement à jour les champs `updated_at`
|
||||
- Les index sont créés sur les colonnes fréquemment utilisées pour optimiser les performances
|
||||
- Les contraintes CASCADE assurent l'intégrité référentielle
|
||||
49
database/migrations/README.md
Normal file
49
database/migrations/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Migrations de Base de Données
|
||||
|
||||
Ce dossier contient les migrations pour la base de données The Tip Top.
|
||||
|
||||
## Migration: Ajout des champs démographiques
|
||||
|
||||
### Fichier: `add-demographics-fields.sql`
|
||||
|
||||
Cette migration ajoute les champs nécessaires pour les statistiques démographiques:
|
||||
- `gender` (ENUM: MALE, FEMALE, OTHER, NOT_SPECIFIED)
|
||||
- `date_of_birth` (DATE)
|
||||
|
||||
### Comment exécuter la migration
|
||||
|
||||
#### Option 1: Via psql
|
||||
|
||||
```bash
|
||||
psql -h 51.75.24.29 -U postgres -d thetiptop_dev -p 5433 -f database/migrations/add-demographics-fields.sql
|
||||
```
|
||||
|
||||
#### Option 2: Via un client SQL (DBeaver, pgAdmin, etc.)
|
||||
|
||||
1. Connectez-vous à votre base de données
|
||||
2. Ouvrez le fichier `add-demographics-fields.sql`
|
||||
3. Exécutez le script
|
||||
|
||||
### Vérification
|
||||
|
||||
Pour vérifier que la migration a été appliquée correctement:
|
||||
|
||||
```sql
|
||||
-- Vérifier les nouvelles colonnes
|
||||
SELECT column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'users'
|
||||
AND column_name IN ('gender', 'date_of_birth');
|
||||
|
||||
-- Vérifier le type ENUM gender_type
|
||||
SELECT typname, enumlabel
|
||||
FROM pg_type
|
||||
JOIN pg_enum ON pg_type.oid = pg_enum.enumtypid
|
||||
WHERE typname = 'gender_type';
|
||||
```
|
||||
|
||||
## Notes importantes
|
||||
|
||||
- Cette migration est idempotente (peut être exécutée plusieurs fois sans erreur)
|
||||
- Les valeurs par défaut sont définies pour ne pas affecter les données existantes
|
||||
- Les index sont créés automatiquement pour améliorer les performances des requêtes de statistiques
|
||||
38
database/migrations/add-demographics-fields.sql
Normal file
38
database/migrations/add-demographics-fields.sql
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-- ============================================
|
||||
-- MIGRATION: Ajout des champs démographiques
|
||||
-- ============================================
|
||||
|
||||
-- Créer le type ENUM pour le genre
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE gender_type AS ENUM ('MALE', 'FEMALE', 'OTHER', 'NOT_SPECIFIED');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
-- Ajouter les colonnes gender et date_of_birth à la table users si elles n'existent pas
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Ajouter la colonne gender si elle n'existe pas
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'users' AND column_name = 'gender'
|
||||
) THEN
|
||||
ALTER TABLE users ADD COLUMN gender gender_type DEFAULT 'NOT_SPECIFIED';
|
||||
END IF;
|
||||
|
||||
-- Ajouter la colonne date_of_birth si elle n'existe pas
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'users' AND column_name = 'date_of_birth'
|
||||
) THEN
|
||||
ALTER TABLE users ADD COLUMN date_of_birth DATE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Créer un index sur gender pour améliorer les performances des requêtes de statistiques
|
||||
CREATE INDEX IF NOT EXISTS idx_users_gender ON users(gender);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_date_of_birth ON users(date_of_birth);
|
||||
|
||||
-- Commentaires
|
||||
COMMENT ON COLUMN users.gender IS 'Genre de l''utilisateur: MALE, FEMALE, OTHER, NOT_SPECIFIED';
|
||||
COMMENT ON COLUMN users.date_of_birth IS 'Date de naissance de l''utilisateur pour les statistiques démographiques';
|
||||
73
database/migrations/add-email-campaigns.sql
Normal file
73
database/migrations/add-email-campaigns.sql
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
-- Migration: Système de campagnes email
|
||||
-- Date: 2025-11-14
|
||||
-- Description: Tables pour gérer les campagnes d'emailing
|
||||
|
||||
-- Table des campagnes email
|
||||
CREATE TABLE IF NOT EXISTS email_campaigns (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
subject VARCHAR(500) NOT NULL,
|
||||
template_html TEXT NOT NULL,
|
||||
template_text TEXT,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
status VARCHAR(50) DEFAULT 'DRAFT', -- DRAFT, SCHEDULED, SENT, CANCELLED
|
||||
scheduled_at TIMESTAMP,
|
||||
sent_at TIMESTAMP,
|
||||
recipient_count INTEGER DEFAULT 0,
|
||||
opened_count INTEGER DEFAULT 0,
|
||||
clicked_count INTEGER DEFAULT 0,
|
||||
criteria JSONB, -- Critères de ciblage
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Table des destinataires (pour tracking individuel)
|
||||
CREATE TABLE IF NOT EXISTS email_campaign_recipients (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
campaign_id UUID NOT NULL REFERENCES email_campaigns(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
email VARCHAR(255) NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'PENDING', -- PENDING, SENT, FAILED, BOUNCED
|
||||
sent_at TIMESTAMP,
|
||||
opened_at TIMESTAMP,
|
||||
clicked_at TIMESTAMP,
|
||||
unsubscribed_at TIMESTAMP,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Table des templates prédéfinis
|
||||
CREATE TABLE IF NOT EXISTS email_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
subject VARCHAR(500),
|
||||
html_content TEXT NOT NULL,
|
||||
text_content TEXT,
|
||||
category VARCHAR(100), -- welcome, promotion, notification, etc.
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Index pour performance
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaigns_status ON email_campaigns(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaigns_created_by ON email_campaigns(created_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaigns_scheduled_at ON email_campaigns(scheduled_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaign_recipients_campaign ON email_campaign_recipients(campaign_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaign_recipients_user ON email_campaign_recipients(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_email_campaign_recipients_status ON email_campaign_recipients(status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_email_templates_category ON email_templates(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_email_templates_is_active ON email_templates(is_active);
|
||||
|
||||
-- Commentaires
|
||||
COMMENT ON TABLE email_campaigns IS 'Campagnes email marketing';
|
||||
COMMENT ON TABLE email_campaign_recipients IS 'Destinataires individuels avec tracking';
|
||||
COMMENT ON TABLE email_templates IS 'Templates email reutilisables';
|
||||
|
||||
COMMENT ON COLUMN email_campaigns.criteria IS 'Criteres JSON de ciblage des destinataires';
|
||||
COMMENT ON COLUMN email_campaigns.status IS 'DRAFT=Brouillon, SCHEDULED=Planifie, SENT=Envoye, CANCELLED=Annule';
|
||||
49
database/migrations/add-grand-prize-draw.sql
Normal file
49
database/migrations/add-grand-prize-draw.sql
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
-- Migration: Ajout de la table pour les tirages au sort du gros lot
|
||||
-- Date: 2025-11-13
|
||||
|
||||
-- Table pour stocker les tirages au sort
|
||||
CREATE TABLE IF NOT EXISTS grand_prize_draws (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Informations du tirage
|
||||
draw_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
conducted_by UUID NOT NULL REFERENCES users(id), -- Admin qui a lancé le tirage
|
||||
|
||||
-- Gagnant
|
||||
winner_id UUID NOT NULL REFERENCES users(id),
|
||||
winner_email VARCHAR(255) NOT NULL,
|
||||
winner_name VARCHAR(255) NOT NULL,
|
||||
|
||||
-- Détails du lot
|
||||
prize_name VARCHAR(255) NOT NULL,
|
||||
prize_value VARCHAR(100),
|
||||
|
||||
-- Participants
|
||||
total_participants INTEGER NOT NULL,
|
||||
eligible_participants INTEGER NOT NULL,
|
||||
|
||||
-- Critères d'éligibilité utilisés
|
||||
criteria JSONB, -- Stocke les critères du tirage
|
||||
|
||||
-- Statut
|
||||
status VARCHAR(50) DEFAULT 'COMPLETED', -- COMPLETED, NOTIFIED, CLAIMED
|
||||
notified_at TIMESTAMP,
|
||||
claimed_at TIMESTAMP,
|
||||
|
||||
-- Metadata
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT unique_grand_prize_draw UNIQUE (draw_date)
|
||||
);
|
||||
|
||||
-- Index pour les recherches
|
||||
CREATE INDEX IF NOT EXISTS idx_grand_prize_draws_winner ON grand_prize_draws(winner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_grand_prize_draws_date ON grand_prize_draws(draw_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_grand_prize_draws_status ON grand_prize_draws(status);
|
||||
|
||||
-- Commentaires
|
||||
COMMENT ON TABLE grand_prize_draws IS 'Stocke les tirages au sort du gros lot final';
|
||||
COMMENT ON COLUMN grand_prize_draws.conducted_by IS 'Admin qui a effectué le tirage';
|
||||
COMMENT ON COLUMN grand_prize_draws.criteria IS 'Critères d''éligibilité en JSON (ex: {minTickets: 1, verified: true})';
|
||||
COMMENT ON COLUMN grand_prize_draws.status IS 'COMPLETED: tirage effectué, NOTIFIED: gagnant notifié, CLAIMED: lot récupéré';
|
||||
31
database/migrations/add-grand-prize-type.sql
Normal file
31
database/migrations/add-grand-prize-type.sql
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
-- Migration: Ajouter le type GRAND_PRIZE pour le gros lot du tirage final
|
||||
-- Date: 2025-11-14
|
||||
|
||||
-- Ajouter le type GRAND_PRIZE à l'enum prize_type
|
||||
ALTER TYPE prize_type ADD VALUE IF NOT EXISTS 'GRAND_PRIZE';
|
||||
|
||||
-- Créer le gros lot "An de thé" (360€)
|
||||
INSERT INTO prizes (
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
value,
|
||||
stock,
|
||||
probability,
|
||||
is_active
|
||||
) VALUES (
|
||||
'GRAND_PRIZE',
|
||||
'An de thé',
|
||||
'Gros lot du tirage final - Un an de thé d''exception',
|
||||
'360.00',
|
||||
1,
|
||||
0,
|
||||
TRUE
|
||||
)
|
||||
ON CONFLICT (name) DO UPDATE SET
|
||||
type = 'GRAND_PRIZE',
|
||||
description = 'Gros lot du tirage final - Un an de thé d''exception',
|
||||
value = '360.00',
|
||||
stock = 1,
|
||||
probability = 0,
|
||||
is_active = TRUE;
|
||||
14
database/migrations/add-is-active-to-users.sql
Normal file
14
database/migrations/add-is-active-to-users.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- ============================================
|
||||
-- MIGRATION: Ajouter is_active à la table users
|
||||
-- ============================================
|
||||
-- Cette migration ajoute le champ is_active pour permettre
|
||||
-- l'archivage des comptes utilisateurs (soft delete)
|
||||
|
||||
-- Ajouter la colonne is_active avec valeur par défaut TRUE
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT TRUE;
|
||||
|
||||
-- Créer un index pour améliorer les performances des filtres
|
||||
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
|
||||
|
||||
-- Mettre à jour tous les utilisateurs existants comme actifs
|
||||
UPDATE users SET is_active = TRUE WHERE is_active IS NULL;
|
||||
26
database/migrations/add-newsletter-table.sql
Normal file
26
database/migrations/add-newsletter-table.sql
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
-- ============================================
|
||||
-- MIGRATION: Add Newsletter Table
|
||||
-- ============================================
|
||||
|
||||
-- Create newsletters table
|
||||
CREATE TABLE IF NOT EXISTS newsletters (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
subscribed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
unsubscribed_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX idx_newsletters_email ON newsletters(email);
|
||||
CREATE INDEX idx_newsletters_is_active ON newsletters(is_active);
|
||||
|
||||
-- Add trigger for updated_at
|
||||
CREATE TRIGGER update_newsletters_updated_at BEFORE UPDATE ON newsletters
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Add comment
|
||||
COMMENT ON TABLE newsletters IS 'Table des abonnements à la newsletter';
|
||||
COMMENT ON COLUMN newsletters.is_active IS 'TRUE si abonné, FALSE si désabonné';
|
||||
26
database/migrations/add-prize-delivery-tracking.sql
Normal file
26
database/migrations/add-prize-delivery-tracking.sql
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
-- Migration: Ajout du tracking de remise physique des lots
|
||||
-- Date: 2025-11-14
|
||||
-- Description: Ajoute les colonnes pour suivre la remise physique des lots
|
||||
|
||||
-- Ajouter la colonne delivered_at pour marquer la date de remise physique
|
||||
ALTER TABLE tickets
|
||||
ADD COLUMN IF NOT EXISTS delivered_at TIMESTAMP DEFAULT NULL;
|
||||
|
||||
-- Ajouter la colonne delivered_by pour tracer qui a remis le lot
|
||||
ALTER TABLE tickets
|
||||
ADD COLUMN IF NOT EXISTS delivered_by UUID DEFAULT NULL REFERENCES users(id);
|
||||
|
||||
-- Ajouter la colonne delivery_notes pour des notes sur la remise
|
||||
ALTER TABLE tickets
|
||||
ADD COLUMN IF NOT EXISTS delivery_notes TEXT DEFAULT NULL;
|
||||
|
||||
-- Créer un index sur delivered_at pour les requêtes de suivi
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_delivered_at ON tickets(delivered_at);
|
||||
|
||||
-- Créer un index sur delivered_by
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_delivered_by ON tickets(delivered_by);
|
||||
|
||||
-- Commentaires
|
||||
COMMENT ON COLUMN tickets.delivered_at IS 'Date de remise physique du lot au gagnant';
|
||||
COMMENT ON COLUMN tickets.delivered_by IS 'Employé/Admin qui a remis le lot';
|
||||
COMMENT ON COLUMN tickets.delivery_notes IS 'Notes sur la remise (lieu, conditions, etc.)';
|
||||
27
database/migrations/fix-tickets-schema.sql
Normal file
27
database/migrations/fix-tickets-schema.sql
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
-- Migration pour corriger le schéma de la table tickets
|
||||
-- Permet de créer des tickets non assignés (status=NULL, user_id=NULL)
|
||||
|
||||
-- 1. Modifier la colonne user_id pour accepter NULL
|
||||
ALTER TABLE tickets
|
||||
ALTER COLUMN user_id DROP NOT NULL;
|
||||
|
||||
-- 2. Modifier la colonne status pour ne pas avoir de valeur par défaut
|
||||
ALTER TABLE tickets
|
||||
ALTER COLUMN status DROP DEFAULT;
|
||||
|
||||
-- 3. Modifier la colonne played_at pour ne pas avoir de valeur par défaut
|
||||
ALTER TABLE tickets
|
||||
ALTER COLUMN played_at DROP DEFAULT;
|
||||
|
||||
-- 4. Mettre à jour les tickets existants qui ont été mal créés
|
||||
-- (tickets avec PENDING mais sans user_id ou avec played_at par défaut)
|
||||
UPDATE tickets
|
||||
SET
|
||||
status = NULL,
|
||||
user_id = NULL,
|
||||
played_at = NULL
|
||||
WHERE user_id IS NULL OR played_at = created_at;
|
||||
|
||||
COMMENT ON COLUMN tickets.user_id IS 'NULL = ticket non joué, UUID = ticket joué par cet utilisateur';
|
||||
COMMENT ON COLUMN tickets.status IS 'NULL = ticket non joué, PENDING/CLAIMED/REJECTED = statut après jeu';
|
||||
COMMENT ON COLUMN tickets.played_at IS 'NULL = ticket non joué, TIMESTAMP = date de jeu';
|
||||
10
database/migrations/make-user-id-nullable.sql
Normal file
10
database/migrations/make-user-id-nullable.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
-- Rendre le champ user_id nullable pour permettre les tickets non utilisés
|
||||
-- Les 500,000 tickets du jeu-concours existent avant d'être attribués à un utilisateur
|
||||
|
||||
ALTER TABLE tickets
|
||||
ALTER COLUMN user_id DROP NOT NULL;
|
||||
|
||||
-- Créer un index pour les tickets non utilisés pour optimiser les requêtes
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_unused ON tickets(user_id) WHERE user_id IS NULL;
|
||||
|
||||
COMMENT ON COLUMN tickets.user_id IS 'ID de l''utilisateur - NULL pour les tickets non encore utilisés';
|
||||
271
database/schema.sql
Normal file
271
database/schema.sql
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
-- ============================================
|
||||
-- SCHEMA SQL - THE TIP TOP DATABASE
|
||||
-- ============================================
|
||||
|
||||
-- Drop tables if they exist
|
||||
DROP TABLE IF EXISTS tickets CASCADE;
|
||||
DROP TABLE IF EXISTS prizes CASCADE;
|
||||
DROP TABLE IF EXISTS users CASCADE;
|
||||
DROP TABLE IF EXISTS game_settings CASCADE;
|
||||
|
||||
-- Drop types if they exist
|
||||
DROP TYPE IF EXISTS user_role CASCADE;
|
||||
DROP TYPE IF EXISTS ticket_status CASCADE;
|
||||
DROP TYPE IF EXISTS prize_type CASCADE;
|
||||
|
||||
-- ============================================
|
||||
-- ENUMS
|
||||
-- ============================================
|
||||
|
||||
CREATE TYPE user_role AS ENUM ('CLIENT', 'EMPLOYEE', 'ADMIN');
|
||||
CREATE TYPE ticket_status AS ENUM ('PENDING', 'CLAIMED', 'REJECTED');
|
||||
CREATE TYPE prize_type AS ENUM (
|
||||
'INFUSEUR',
|
||||
'THE_SIGNATURE',
|
||||
'COFFRET_DECOUVERTE',
|
||||
'COFFRET_PRESTIGE',
|
||||
'THE_GRATUIT'
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- TABLE: users
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
address TEXT,
|
||||
city VARCHAR(100),
|
||||
postal_code VARCHAR(10),
|
||||
role user_role DEFAULT 'CLIENT',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
is_verified BOOLEAN DEFAULT FALSE,
|
||||
verification_token VARCHAR(255),
|
||||
reset_token VARCHAR(255),
|
||||
reset_token_expiry TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Index pour améliorer les performances
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_role ON users(role);
|
||||
CREATE INDEX idx_users_verification_token ON users(verification_token);
|
||||
CREATE INDEX idx_users_reset_token ON users(reset_token);
|
||||
|
||||
-- ============================================
|
||||
-- TABLE: prizes
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE prizes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
type prize_type NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
value DECIMAL(10, 2) NOT NULL,
|
||||
stock INTEGER DEFAULT 0,
|
||||
probability DECIMAL(5, 4) NOT NULL CHECK (probability >= 0 AND probability <= 1),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Index pour améliorer les performances
|
||||
CREATE INDEX idx_prizes_type ON prizes(type);
|
||||
CREATE INDEX idx_prizes_is_active ON prizes(is_active);
|
||||
|
||||
-- ============================================
|
||||
-- TABLE: tickets
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE tickets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code VARCHAR(50) UNIQUE NOT NULL,
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE, -- NULL = ticket non joué
|
||||
prize_id UUID NOT NULL REFERENCES prizes(id) ON DELETE RESTRICT,
|
||||
status ticket_status, -- NULL = ticket non joué, PENDING/CLAIMED/REJECTED après jeu
|
||||
played_at TIMESTAMP, -- NULL = ticket non joué, TIMESTAMP = date de jeu
|
||||
claimed_at TIMESTAMP,
|
||||
validated_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
validated_at TIMESTAMP,
|
||||
rejection_reason TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Index pour améliorer les performances
|
||||
CREATE INDEX idx_tickets_code ON tickets(code);
|
||||
CREATE INDEX idx_tickets_user_id ON tickets(user_id);
|
||||
CREATE INDEX idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX idx_tickets_prize_id ON tickets(prize_id);
|
||||
CREATE INDEX idx_tickets_validated_by ON tickets(validated_by);
|
||||
|
||||
-- ============================================
|
||||
-- TABLE: game_settings
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE game_settings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
start_date TIMESTAMP NOT NULL,
|
||||
end_date TIMESTAMP NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
total_tickets INTEGER NOT NULL,
|
||||
tickets_generated INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- TRIGGERS
|
||||
-- ============================================
|
||||
|
||||
-- Trigger pour mettre à jour updated_at automatiquement
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_prizes_updated_at BEFORE UPDATE ON prizes
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_tickets_updated_at BEFORE UPDATE ON tickets
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_game_settings_updated_at BEFORE UPDATE ON game_settings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================
|
||||
-- INITIAL DATA
|
||||
-- ============================================
|
||||
|
||||
-- Configuration du jeu-concours Thé Tip Top
|
||||
-- Période de participation : 1 décembre 2025 au 31 décembre 2025 (30 jours)
|
||||
-- Période de récupération des lots : jusqu'au 31 janvier 2026 (60 jours)
|
||||
-- Tirage au sort : 1 février 2026
|
||||
INSERT INTO game_settings (start_date, end_date, is_active, total_tickets)
|
||||
VALUES ('2025-12-01 00:00:00', '2025-12-31 23:59:59', TRUE, 500000);
|
||||
|
||||
-- Prix avec probabilités respectant la distribution demandée
|
||||
-- Total: 500,000 tickets
|
||||
|
||||
-- 1. Infuseur à thé (60% = 300,000)
|
||||
INSERT INTO prizes (type, name, description, value, stock, probability, is_active)
|
||||
VALUES (
|
||||
'INFUSEUR',
|
||||
'Infuseur à thé',
|
||||
'Un infuseur à thé de qualité supérieure en inox',
|
||||
39.00,
|
||||
300000,
|
||||
0.60,
|
||||
TRUE
|
||||
);
|
||||
|
||||
-- 2. Boîte de 100g signature (20% = 100,000)
|
||||
INSERT INTO prizes (type, name, description, value, stock, probability, is_active)
|
||||
VALUES (
|
||||
'THE_SIGNATURE',
|
||||
'Thé Signature 100g',
|
||||
'Boîte de thé signature 100g - Mélange exclusif Thé Tip Top',
|
||||
49.00,
|
||||
100000,
|
||||
0.20,
|
||||
TRUE
|
||||
);
|
||||
|
||||
-- 3. Coffret découverte (10% = 50,000)
|
||||
INSERT INTO prizes (type, name, description, value, stock, probability, is_active)
|
||||
VALUES (
|
||||
'COFFRET_DECOUVERTE',
|
||||
'Coffret Découverte',
|
||||
'Coffret découverte avec assortiment de 5 thés (39€)',
|
||||
39.00,
|
||||
50000,
|
||||
0.10,
|
||||
TRUE
|
||||
);
|
||||
|
||||
-- 4. Coffret prestige (6% = 30,000)
|
||||
INSERT INTO prizes (type, name, description, value, stock, probability, is_active)
|
||||
VALUES (
|
||||
'COFFRET_PRESTIGE',
|
||||
'Coffret Prestige',
|
||||
'Coffret prestige avec sélection premium de thés rares (69€)',
|
||||
69.00,
|
||||
30000,
|
||||
0.06,
|
||||
TRUE
|
||||
);
|
||||
|
||||
-- 5. Thé gratuit (4% = 20,000)
|
||||
INSERT INTO prizes (type, name, description, value, stock, probability, is_active)
|
||||
VALUES (
|
||||
'THE_GRATUIT',
|
||||
'Thé Infusion Gratuit',
|
||||
'Un thé infusion de votre choix offert en magasin',
|
||||
0.00,
|
||||
20000,
|
||||
0.04,
|
||||
TRUE
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- VIEWS UTILES
|
||||
-- ============================================
|
||||
|
||||
-- Vue pour les statistiques des prix
|
||||
CREATE OR REPLACE VIEW prize_statistics AS
|
||||
SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.type,
|
||||
p.stock AS stock_initial,
|
||||
COUNT(t.id) AS tickets_used,
|
||||
(p.stock - COUNT(t.id)) AS stock_remaining,
|
||||
COUNT(CASE WHEN t.status = 'CLAIMED' THEN 1 END) AS prizes_claimed,
|
||||
COUNT(CASE WHEN t.status = 'PENDING' THEN 1 END) AS prizes_pending,
|
||||
COUNT(CASE WHEN t.status = 'REJECTED' THEN 1 END) AS prizes_rejected
|
||||
FROM prizes p
|
||||
LEFT JOIN tickets t ON p.id = t.prize_id
|
||||
GROUP BY p.id, p.name, p.type, p.stock;
|
||||
|
||||
-- Vue pour les tickets en attente de validation
|
||||
CREATE OR REPLACE VIEW pending_tickets AS
|
||||
SELECT
|
||||
t.id,
|
||||
t.code,
|
||||
t.status,
|
||||
t.played_at,
|
||||
u.email AS user_email,
|
||||
u.first_name || ' ' || u.last_name AS user_name,
|
||||
u.phone AS user_phone,
|
||||
p.name AS prize_name,
|
||||
p.type AS prize_type,
|
||||
p.value AS prize_value
|
||||
FROM tickets t
|
||||
JOIN users u ON t.user_id = u.id
|
||||
JOIN prizes p ON t.prize_id = p.id
|
||||
WHERE t.status = 'PENDING'
|
||||
ORDER BY t.played_at ASC;
|
||||
|
||||
-- ============================================
|
||||
-- COMMENTAIRES
|
||||
-- ============================================
|
||||
|
||||
COMMENT ON TABLE users IS 'Table des utilisateurs (clients, employés, admins)';
|
||||
COMMENT ON TABLE prizes IS 'Table des prix disponibles dans le jeu';
|
||||
COMMENT ON TABLE tickets IS 'Table des tickets de jeu joués par les utilisateurs';
|
||||
COMMENT ON TABLE game_settings IS 'Configuration générale du jeu-concours';
|
||||
|
||||
COMMENT ON COLUMN users.role IS 'Rôle: CLIENT, EMPLOYEE, ou ADMIN';
|
||||
COMMENT ON COLUMN prizes.probability IS 'Probabilité de gagner ce prix (0.0 à 1.0)';
|
||||
COMMENT ON COLUMN tickets.status IS 'Statut: PENDING (en attente), CLAIMED (réclamé), REJECTED (rejeté)';
|
||||
247
database/seed.js
Normal file
247
database/seed.js
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/**
|
||||
* Script de seed pour insérer des données de test
|
||||
* Usage: node database/seed.js
|
||||
*/
|
||||
import bcrypt from 'bcrypt';
|
||||
import { pool } from '../db.js';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
const SALT_ROUNDS = 10;
|
||||
|
||||
async function seedDatabase() {
|
||||
try {
|
||||
console.log('🌱 Démarrage du seed de la base de données...\n');
|
||||
|
||||
// ============================================
|
||||
// 1. SEED USERS
|
||||
// ============================================
|
||||
console.log('👥 Création des utilisateurs...');
|
||||
|
||||
// Hash des mots de passe
|
||||
const adminPassword = await bcrypt.hash('Admin123!', SALT_ROUNDS);
|
||||
const employeePassword = await bcrypt.hash('Employee123!', SALT_ROUNDS);
|
||||
const clientPassword = await bcrypt.hash('Client123!', SALT_ROUNDS);
|
||||
|
||||
// Admin
|
||||
const adminResult = await pool.query(
|
||||
`INSERT INTO users (email, password, first_name, last_name, phone, role, is_verified)
|
||||
VALUES ($1, $2, $3, $4, $5, 'ADMIN', TRUE)
|
||||
ON CONFLICT (email) DO UPDATE SET role = 'ADMIN'
|
||||
RETURNING id, email, role`,
|
||||
['admin@thetiptop.com', adminPassword, 'Admin', 'Principal', '+33123456789']
|
||||
);
|
||||
console.log('✅ Admin créé:', adminResult.rows[0].email);
|
||||
|
||||
// Employé 1
|
||||
const employee1Result = await pool.query(
|
||||
`INSERT INTO users (email, password, first_name, last_name, phone, role, is_verified)
|
||||
VALUES ($1, $2, $3, $4, $5, 'EMPLOYEE', TRUE)
|
||||
ON CONFLICT (email) DO UPDATE SET role = 'EMPLOYEE'
|
||||
RETURNING id, email, role`,
|
||||
['employee1@thetiptop.com', employeePassword, 'Marie', 'Dupont', '+33198765432']
|
||||
);
|
||||
console.log('✅ Employé 1 créé:', employee1Result.rows[0].email);
|
||||
|
||||
// Employé 2
|
||||
const employee2Result = await pool.query(
|
||||
`INSERT INTO users (email, password, first_name, last_name, phone, role, is_verified)
|
||||
VALUES ($1, $2, $3, $4, $5, 'EMPLOYEE', TRUE)
|
||||
ON CONFLICT (email) DO UPDATE SET role = 'EMPLOYEE'
|
||||
RETURNING id, email, role`,
|
||||
['employee2@thetiptop.com', employeePassword, 'Pierre', 'Martin', '+33187654321']
|
||||
);
|
||||
console.log('✅ Employé 2 créé:', employee2Result.rows[0].email);
|
||||
|
||||
// Clients
|
||||
const clients = [
|
||||
{
|
||||
email: 'client1@example.com',
|
||||
firstName: 'Jean',
|
||||
lastName: 'Dupuis',
|
||||
phone: '+33612345678',
|
||||
},
|
||||
{
|
||||
email: 'client2@example.com',
|
||||
firstName: 'Sophie',
|
||||
lastName: 'Bernard',
|
||||
phone: '+33623456789',
|
||||
},
|
||||
{
|
||||
email: 'client3@example.com',
|
||||
firstName: 'Luc',
|
||||
lastName: 'Lefevre',
|
||||
phone: '+33634567890',
|
||||
},
|
||||
{
|
||||
email: 'client4@example.com',
|
||||
firstName: 'Emma',
|
||||
lastName: 'Petit',
|
||||
phone: '+33645678901',
|
||||
},
|
||||
{
|
||||
email: 'client5@example.com',
|
||||
firstName: 'Thomas',
|
||||
lastName: 'Robert',
|
||||
phone: '+33656789012',
|
||||
},
|
||||
];
|
||||
|
||||
const clientIds = [];
|
||||
for (const client of clients) {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO users (email, password, first_name, last_name, phone, role, is_verified)
|
||||
VALUES ($1, $2, $3, $4, $5, 'CLIENT', TRUE)
|
||||
ON CONFLICT (email) DO UPDATE SET role = 'CLIENT'
|
||||
RETURNING id`,
|
||||
[
|
||||
client.email,
|
||||
clientPassword,
|
||||
client.firstName,
|
||||
client.lastName,
|
||||
client.phone,
|
||||
]
|
||||
);
|
||||
clientIds.push(result.rows[0].id);
|
||||
console.log(`✅ Client créé: ${client.email}`);
|
||||
}
|
||||
|
||||
console.log(`\n✅ ${clients.length + 3} utilisateurs créés avec succès\n`);
|
||||
|
||||
// ============================================
|
||||
// 2. SEED TICKETS
|
||||
// ============================================
|
||||
console.log('🎟️ Création des tickets de test...');
|
||||
|
||||
// Récupérer les IDs des prix
|
||||
const prizesResult = await pool.query(
|
||||
'SELECT id, type FROM prizes ORDER BY type'
|
||||
);
|
||||
const prizes = prizesResult.rows;
|
||||
|
||||
if (prizes.length === 0) {
|
||||
console.log('⚠️ Aucun prix trouvé. Exécutez d\'abord le schema.sql');
|
||||
return;
|
||||
}
|
||||
|
||||
// Créer des tickets avec différents statuts pour chaque client
|
||||
const _ticketStatuses = ['PENDING', 'CLAIMED', 'REJECTED']; // eslint-disable-line no-unused-vars
|
||||
let ticketCount = 0;
|
||||
|
||||
for (let i = 0; i < clientIds.length; i++) {
|
||||
const clientId = clientIds[i];
|
||||
|
||||
// Chaque client obtient 3-5 tickets
|
||||
const numTickets = Math.floor(Math.random() * 3) + 3;
|
||||
|
||||
for (let j = 0; j < numTickets; j++) {
|
||||
// Sélectionner un prix aléatoire
|
||||
const randomPrize = prizes[Math.floor(Math.random() * prizes.length)];
|
||||
|
||||
// Sélectionner un statut (80% PENDING, 10% CLAIMED, 10% REJECTED)
|
||||
const rand = Math.random();
|
||||
let status;
|
||||
if (rand < 0.8) {
|
||||
status = 'PENDING';
|
||||
} else if (rand < 0.9) {
|
||||
status = 'CLAIMED';
|
||||
} else {
|
||||
status = 'REJECTED';
|
||||
}
|
||||
|
||||
// Générer un code unique
|
||||
const ticketCode = `TT-${Date.now()}-${randomUUID().substring(0, 8).toUpperCase()}`;
|
||||
|
||||
// Créer le ticket
|
||||
const insertQuery = `
|
||||
INSERT INTO tickets (code, user_id, prize_id, status, played_at, claimed_at, validated_by, validated_at, rejection_reason)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
`;
|
||||
|
||||
const playedAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000); // Dans les 30 derniers jours
|
||||
|
||||
let claimedAt = null;
|
||||
let validatedBy = null;
|
||||
let validatedAt = null;
|
||||
let rejectionReason = null;
|
||||
|
||||
if (status === 'CLAIMED') {
|
||||
claimedAt = new Date(playedAt.getTime() + Math.random() * 7 * 24 * 60 * 60 * 1000); // 0-7 jours après
|
||||
validatedBy = employee1Result.rows[0].id;
|
||||
validatedAt = claimedAt;
|
||||
} else if (status === 'REJECTED') {
|
||||
claimedAt = new Date(playedAt.getTime() + Math.random() * 7 * 24 * 60 * 60 * 1000);
|
||||
validatedBy = employee1Result.rows[0].id;
|
||||
validatedAt = claimedAt;
|
||||
rejectionReason = 'Ticket non conforme ou expiré';
|
||||
}
|
||||
|
||||
await pool.query(insertQuery, [
|
||||
ticketCode,
|
||||
clientId,
|
||||
randomPrize.id,
|
||||
status,
|
||||
playedAt,
|
||||
claimedAt,
|
||||
validatedBy,
|
||||
validatedAt,
|
||||
rejectionReason,
|
||||
]);
|
||||
|
||||
ticketCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ ${ticketCount} tickets créés avec succès\n`);
|
||||
|
||||
// ============================================
|
||||
// 3. AFFICHER LES STATISTIQUES
|
||||
// ============================================
|
||||
console.log('📊 Statistiques de la base de données:\n');
|
||||
|
||||
const usersStats = await pool.query(`
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
COUNT(CASE WHEN role = 'ADMIN' THEN 1 END) as admins,
|
||||
COUNT(CASE WHEN role = 'EMPLOYEE' THEN 1 END) as employees,
|
||||
COUNT(CASE WHEN role = 'CLIENT' THEN 1 END) as clients
|
||||
FROM users
|
||||
`);
|
||||
|
||||
const ticketsStats = await pool.query(`
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
COUNT(CASE WHEN status = 'PENDING' THEN 1 END) as pending,
|
||||
COUNT(CASE WHEN status = 'CLAIMED' THEN 1 END) as claimed,
|
||||
COUNT(CASE WHEN status = 'REJECTED' THEN 1 END) as rejected
|
||||
FROM tickets
|
||||
`);
|
||||
|
||||
console.log('👥 Utilisateurs:');
|
||||
console.log(` Total: ${usersStats.rows[0].total}`);
|
||||
console.log(` Admins: ${usersStats.rows[0].admins}`);
|
||||
console.log(` Employés: ${usersStats.rows[0].employees}`);
|
||||
console.log(` Clients: ${usersStats.rows[0].clients}`);
|
||||
|
||||
console.log('\n🎟️ Tickets:');
|
||||
console.log(` Total: ${ticketsStats.rows[0].total}`);
|
||||
console.log(` En attente: ${ticketsStats.rows[0].pending}`);
|
||||
console.log(` Réclamés: ${ticketsStats.rows[0].claimed}`);
|
||||
console.log(` Rejetés: ${ticketsStats.rows[0].rejected}`);
|
||||
|
||||
console.log('\n✅ Seed terminé avec succès!');
|
||||
console.log('\n🔐 Comptes de test créés:');
|
||||
console.log(' Admin: admin@thetiptop.com / Admin123!');
|
||||
console.log(' Employé 1: employee1@thetiptop.com / Employee123!');
|
||||
console.log(' Employé 2: employee2@thetiptop.com / Employee123!');
|
||||
console.log(' Clients: client1@example.com à client5@example.com / Client123!');
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors du seed:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
// Fermer la connexion
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Exécuter le seed
|
||||
seedDatabase();
|
||||
64
database/update-prize-names.sql
Normal file
64
database/update-prize-names.sql
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
-- ============================================
|
||||
-- MISE À JOUR DES NOMS DES LOTS
|
||||
-- Correction selon les probabilités correctes
|
||||
-- ============================================
|
||||
|
||||
-- 60% des tickets offrent un infuseur à thé (INFUSEUR)
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Infuseur à thé',
|
||||
description = 'Un infuseur à thé de qualité supérieure en inox',
|
||||
value = 39.00
|
||||
WHERE type = 'INFUSEUR';
|
||||
|
||||
-- 20% des tickets offrent une boite de 100g d'un thé détox ou d'infusion (THE_GRATUIT)
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Boîte de 100g thé détox ou infusion',
|
||||
description = 'Une boîte de 100g d''un thé détox ou d''infusion au choix',
|
||||
value = 0.00,
|
||||
stock = 100000,
|
||||
probability = 0.20
|
||||
WHERE type = 'THE_GRATUIT';
|
||||
|
||||
-- 10% des tickets offrent une boite de 100g d'un thé signature (THE_SIGNATURE)
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Boîte de 100g thé signature',
|
||||
description = 'Une boîte de 100g d''un thé signature - Mélange exclusif',
|
||||
value = 49.00,
|
||||
stock = 50000,
|
||||
probability = 0.10
|
||||
WHERE type = 'THE_SIGNATURE';
|
||||
|
||||
-- 6% des tickets offrent un coffret découverte d'une valeur de 39€ (COFFRET_DECOUVERTE)
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Coffret découverte 39€',
|
||||
description = 'Coffret découverte avec assortiment de thés d''une valeur de 39€',
|
||||
value = 39.00,
|
||||
stock = 30000,
|
||||
probability = 0.06
|
||||
WHERE type = 'COFFRET_DECOUVERTE';
|
||||
|
||||
-- 4% des tickets offrent un coffret prestige d'une valeur de 69€ (COFFRET_PRESTIGE)
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Coffret découverte 69€',
|
||||
description = 'Coffret découverte premium avec sélection de thés rares d''une valeur de 69€',
|
||||
value = 69.00,
|
||||
stock = 20000,
|
||||
probability = 0.04
|
||||
WHERE type = 'COFFRET_PRESTIGE';
|
||||
|
||||
-- Vérification
|
||||
SELECT
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
value,
|
||||
stock,
|
||||
probability,
|
||||
(probability * 100) || '%' as percentage
|
||||
FROM prizes
|
||||
ORDER BY probability DESC;
|
||||
115
database/update-prizes.js
Normal file
115
database/update-prizes.js
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* Script pour mettre à jour les noms des lots selon les bonnes probabilités
|
||||
*/
|
||||
import { pool } from '../db.js';
|
||||
|
||||
async function updatePrizeNames() {
|
||||
try {
|
||||
console.log('🔄 Mise à jour des noms des lots...\n');
|
||||
|
||||
// 60% - Infuseur à thé
|
||||
await pool.query(`
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Infuseur à thé',
|
||||
description = 'Un infuseur à thé de qualité supérieure en inox',
|
||||
value = 39.00
|
||||
WHERE type = 'INFUSEUR'
|
||||
`);
|
||||
console.log('✅ Infuseur à thé (60%) - Mis à jour');
|
||||
|
||||
// 20% - Boîte de 100g thé détox ou infusion
|
||||
await pool.query(`
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Boîte de 100g thé détox ou infusion',
|
||||
description = 'Une boîte de 100g d''un thé détox ou d''infusion au choix',
|
||||
value = 0.00,
|
||||
stock = 100000,
|
||||
probability = 0.20
|
||||
WHERE type = 'THE_GRATUIT'
|
||||
`);
|
||||
console.log('✅ Boîte de 100g thé détox ou infusion (20%) - Mis à jour');
|
||||
|
||||
// 10% - Boîte de 100g thé signature
|
||||
await pool.query(`
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Boîte de 100g thé signature',
|
||||
description = 'Une boîte de 100g d''un thé signature - Mélange exclusif',
|
||||
value = 49.00,
|
||||
stock = 50000,
|
||||
probability = 0.10
|
||||
WHERE type = 'THE_SIGNATURE'
|
||||
`);
|
||||
console.log('✅ Boîte de 100g thé signature (10%) - Mis à jour');
|
||||
|
||||
// 6% - Coffret découverte 39€
|
||||
await pool.query(`
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Coffret découverte 39€',
|
||||
description = 'Coffret découverte avec assortiment de thés d''une valeur de 39€',
|
||||
value = 39.00,
|
||||
stock = 30000,
|
||||
probability = 0.06
|
||||
WHERE type = 'COFFRET_DECOUVERTE'
|
||||
`);
|
||||
console.log('✅ Coffret découverte 39€ (6%) - Mis à jour');
|
||||
|
||||
// 4% - Coffret découverte 69€
|
||||
await pool.query(`
|
||||
UPDATE prizes
|
||||
SET
|
||||
name = 'Coffret découverte 69€',
|
||||
description = 'Coffret découverte premium avec sélection de thés rares d''une valeur de 69€',
|
||||
value = 69.00,
|
||||
stock = 20000,
|
||||
probability = 0.04
|
||||
WHERE type = 'COFFRET_PRESTIGE'
|
||||
`);
|
||||
console.log('✅ Coffret découverte 69€ (4%) - Mis à jour');
|
||||
|
||||
// Afficher le résultat
|
||||
console.log('\n📊 Vérification des lots mis à jour:\n');
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
value,
|
||||
stock,
|
||||
probability,
|
||||
(probability * 100) || '%' as percentage
|
||||
FROM prizes
|
||||
ORDER BY probability DESC
|
||||
`);
|
||||
|
||||
console.log('┌─────────────────────────┬──────────────────────────────────────┬────────┬──────────┬──────────────┐');
|
||||
console.log('│ TYPE │ NOM │ VALEUR │ STOCK │ PROBABILITÉ │');
|
||||
console.log('├─────────────────────────┼──────────────────────────────────────┼────────┼──────────┼──────────────┤');
|
||||
|
||||
result.rows.forEach(row => {
|
||||
const type = row.type.padEnd(23);
|
||||
const name = row.name.substring(0, 36).padEnd(36);
|
||||
const value = (row.value + '€').padStart(6);
|
||||
const stock = row.stock.toString().padStart(8);
|
||||
const prob = row.percentage.padStart(12);
|
||||
|
||||
console.log(`│ ${type} │ ${name} │ ${value} │ ${stock} │ ${prob} │`);
|
||||
});
|
||||
|
||||
console.log('└─────────────────────────┴──────────────────────────────────────┴────────┴──────────┴──────────────┘');
|
||||
|
||||
console.log('\n✅ Mise à jour terminée avec succès!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la mise à jour:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Exécuter la mise à jour
|
||||
updatePrizeNames();
|
||||
24
db.js
24
db.js
|
|
@ -1,23 +1,21 @@
|
|||
import dotenv from "dotenv";
|
||||
import pkg from "pg";
|
||||
|
||||
// Charger les variables d'environnement depuis .env
|
||||
dotenv.config();
|
||||
import config from "./src/config/env.js";
|
||||
|
||||
const { Pool } = pkg;
|
||||
|
||||
// Log de debug pour vérifier que les variables sont bien lues
|
||||
console.log("🧩 DB Config →", {
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
pass: process.env.DB_PASS,
|
||||
name: process.env.DB_NAME,
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
user: config.db.user,
|
||||
pass: config.db.password ? '***' : undefined,
|
||||
database: config.db.database,
|
||||
});
|
||||
|
||||
export const pool = new Pool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
database: process.env.DB_NAME,
|
||||
port: 5432,
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
user: config.db.user,
|
||||
password: config.db.password,
|
||||
database: config.db.database,
|
||||
});
|
||||
|
|
|
|||
834
docs/BPMN_DIAGRAMS.md
Normal file
834
docs/BPMN_DIAGRAMS.md
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
# Diagrammes BPMN - Thé Tip Top
|
||||
|
||||
Ce document présente les différents processus et procédures du projet Thé Tip Top sous forme de diagrammes BPMN.
|
||||
|
||||
---
|
||||
|
||||
## Table des matières
|
||||
|
||||
1. [Diagramme d'activité principal - Jeu concours](#1-diagramme-dactivité-principal---jeu-concours)
|
||||
2. [Processus de déploiement CI/CD](#2-processus-de-déploiement-cicd)
|
||||
3. [Procédure de sauvegarde du workflow Production](#3-procédure-de-sauvegarde-du-workflow-production)
|
||||
4. [Procédure de restauration](#4-procédure-de-restauration)
|
||||
|
||||
---
|
||||
|
||||
## 1. Diagramme d'activité principal - Jeu concours
|
||||
|
||||
### 1.1 Processus de participation au jeu
|
||||
|
||||
Ce diagramme représente le parcours utilisateur pour participer au jeu-concours.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Client["🧑 Client"]
|
||||
A([Début]) --> B{Utilisateur connecté ?}
|
||||
B -->|Non| C[Afficher page Login]
|
||||
C --> D{Choix authentification}
|
||||
D -->|Formulaire| E[Saisir email/mot de passe]
|
||||
D -->|Google OAuth| F[Redirection Google]
|
||||
D -->|Facebook OAuth| G[Redirection Facebook]
|
||||
E --> H{Credentials valides ?}
|
||||
F --> H
|
||||
G --> H
|
||||
H -->|Non| I[Afficher erreur]
|
||||
I --> C
|
||||
H -->|Oui| J[Créer session JWT]
|
||||
B -->|Oui| K[Accéder page Jeu]
|
||||
J --> K
|
||||
end
|
||||
|
||||
subgraph Jeu["🎮 Participation"]
|
||||
K --> L[Saisir code ticket 10 caractères]
|
||||
L --> M{Code valide ?}
|
||||
M -->|Non| N[Afficher erreur format]
|
||||
N --> L
|
||||
M -->|Oui| O[Envoyer requête API]
|
||||
end
|
||||
|
||||
subgraph Backend["⚙️ Backend API"]
|
||||
O --> P{Ticket existe ?}
|
||||
P -->|Non| Q[Erreur: Ticket inexistant]
|
||||
P -->|Oui| R{Ticket déjà utilisé ?}
|
||||
R -->|Oui| S[Erreur: Ticket déjà joué]
|
||||
R -->|Non| T[Attribuer lot au ticket]
|
||||
T --> U[Associer ticket à l'utilisateur]
|
||||
U --> V[Mettre à jour statut: pending]
|
||||
end
|
||||
|
||||
subgraph Resultat["🎁 Résultat"]
|
||||
Q --> W[Afficher modal erreur]
|
||||
S --> W
|
||||
V --> X[Afficher modal gain]
|
||||
X --> Y[Afficher type de lot gagné]
|
||||
Y --> Z([Fin])
|
||||
W --> L
|
||||
end
|
||||
```
|
||||
|
||||
### 1.2 Processus de validation des gains (Employé)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Employe["👨💼 Employé"]
|
||||
A([Début]) --> B[Connexion espace employé]
|
||||
B --> C{Authentifié + Rôle Employee ?}
|
||||
C -->|Non| D[Redirection Login]
|
||||
D --> B
|
||||
C -->|Oui| E[Afficher dashboard employé]
|
||||
end
|
||||
|
||||
subgraph Verification["🔍 Vérification"]
|
||||
E --> F{Mode de recherche}
|
||||
F -->|Recherche| G[Saisir code ticket]
|
||||
F -->|Liste| H[Afficher tickets en attente]
|
||||
G --> I[Rechercher ticket]
|
||||
H --> J[Sélectionner ticket]
|
||||
I --> K{Ticket trouvé ?}
|
||||
K -->|Non| L[Afficher erreur]
|
||||
L --> F
|
||||
K -->|Oui| M[Afficher détails ticket]
|
||||
J --> M
|
||||
end
|
||||
|
||||
subgraph Validation["✅ Validation"]
|
||||
M --> N{Client présent avec pièce d'identité ?}
|
||||
N -->|Non| O[Refuser validation]
|
||||
O --> F
|
||||
N -->|Oui| P[Vérifier correspondance]
|
||||
P --> Q{Informations correctes ?}
|
||||
Q -->|Non| O
|
||||
Q -->|Oui| R[Cliquer Valider]
|
||||
R --> S[Mettre à jour statut: claimed]
|
||||
S --> T[Enregistrer date remise]
|
||||
T --> U[Enregistrer ID employé]
|
||||
U --> V[Remettre le lot au client]
|
||||
V --> W([Fin])
|
||||
end
|
||||
```
|
||||
|
||||
### 1.3 Processus d'administration
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Admin["👨💻 Administrateur"]
|
||||
A([Début]) --> B[Connexion espace admin]
|
||||
B --> C{Authentifié + Rôle Admin ?}
|
||||
C -->|Non| D[Redirection Login]
|
||||
D --> B
|
||||
C -->|Oui| E[Afficher dashboard admin]
|
||||
end
|
||||
|
||||
subgraph Stats["📊 Statistiques"]
|
||||
E --> F{Action souhaitée}
|
||||
F -->|Statistiques| G[Charger statistiques globales]
|
||||
G --> H[Afficher total participations]
|
||||
H --> I[Afficher distribution lots]
|
||||
I --> J[Afficher taux conversion]
|
||||
end
|
||||
|
||||
subgraph Users["👥 Gestion Utilisateurs"]
|
||||
F -->|Utilisateurs| K[Charger liste utilisateurs]
|
||||
K --> L[Afficher tableau utilisateurs]
|
||||
L --> M{Action sur utilisateur}
|
||||
M -->|Voir détails| N[Afficher profil complet]
|
||||
M -->|Modifier rôle| O[Changer rôle utilisateur]
|
||||
end
|
||||
|
||||
subgraph Tickets["🎫 Gestion Tickets"]
|
||||
F -->|Tickets| P[Charger liste tickets]
|
||||
P --> Q[Afficher tableau tickets]
|
||||
Q --> R{Filtrer par}
|
||||
R -->|Statut| S[Filtrer pending/claimed/unclaimed]
|
||||
R -->|Date| T[Filtrer par période]
|
||||
R -->|Lot| U[Filtrer par type de lot]
|
||||
end
|
||||
|
||||
subgraph Export["📤 Export"]
|
||||
F -->|Export| V[Générer rapport]
|
||||
V --> W[Exporter CSV/Excel]
|
||||
W --> X([Fin])
|
||||
J --> E
|
||||
N --> E
|
||||
O --> E
|
||||
S --> E
|
||||
T --> E
|
||||
U --> E
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Processus de déploiement CI/CD
|
||||
|
||||
### 2.1 Processus global de déploiement
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Trigger["🔔 Déclencheur"]
|
||||
A([Début]) --> B{Type de déclenchement}
|
||||
B -->|Push Git| C[Webhook Gitea]
|
||||
B -->|Manuel| D[Jenkins Dashboard]
|
||||
B -->|Polling| E[Poll SCM chaque minute]
|
||||
C --> F[Détecter branche]
|
||||
D --> F
|
||||
E --> F
|
||||
end
|
||||
|
||||
subgraph Detection["🧭 Détection Environnement"]
|
||||
F --> G{Quelle branche ?}
|
||||
G -->|dev| H[ENV = dev]
|
||||
G -->|preprod| I[ENV = preprod]
|
||||
G -->|main| J[ENV = prod]
|
||||
G -->|autre| K[ENV = paramètre choisi]
|
||||
H --> L[TAG = dev-latest]
|
||||
I --> M[TAG = preprod-latest]
|
||||
J --> N[TAG = prod-latest]
|
||||
K --> O[TAG = {env}-latest]
|
||||
end
|
||||
|
||||
subgraph Pipeline["⚙️ Pipeline Jenkins"]
|
||||
L --> P[Checkout code]
|
||||
M --> P
|
||||
N --> P
|
||||
O --> P
|
||||
P --> Q[Install dépendances]
|
||||
Q --> R[Tests & Qualité]
|
||||
R --> S{Tests OK ?}
|
||||
S -->|Non| T[❌ Pipeline échoué]
|
||||
S -->|Oui| U[Build image Docker]
|
||||
U --> V[Push vers Registry]
|
||||
V --> W[Deploy sur serveur]
|
||||
W --> X[Health Check]
|
||||
X --> Y{Service OK ?}
|
||||
Y -->|Non| T
|
||||
Y -->|Oui| Z[✅ Pipeline réussi]
|
||||
T --> AA([Fin - Échec])
|
||||
Z --> AB([Fin - Succès])
|
||||
end
|
||||
```
|
||||
|
||||
### 2.2 Processus de déploiement DEV
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph TriggerDev["🔔 Déclencheur DEV"]
|
||||
A([Push sur branche dev]) --> B[Webhook → Jenkins]
|
||||
B --> C[Démarrer pipeline dev]
|
||||
end
|
||||
|
||||
subgraph InitDev["🧭 Initialisation"]
|
||||
C --> D[Détecter branche: dev]
|
||||
D --> E[Définir ENV=dev]
|
||||
E --> F[Définir TAG=dev-latest]
|
||||
F --> G["Définir DEPLOY_PATH=/srv/devops/the-tip-top/dev"]
|
||||
end
|
||||
|
||||
subgraph QualityDev["🔍 Qualité Code"]
|
||||
G --> H[npm ci - Installation]
|
||||
H --> I[npm run lint]
|
||||
I --> J{Lint OK ?}
|
||||
J -->|Non| K["⚠️ Warning lint (continue)"]
|
||||
J -->|Oui| L[npm test]
|
||||
K --> L
|
||||
L --> M{Tests OK ?}
|
||||
M -->|Non| N["⚠️ Warning tests (continue)"]
|
||||
M -->|Oui| O[Continuer]
|
||||
N --> O
|
||||
end
|
||||
|
||||
subgraph BuildDev["🐳 Build Docker"]
|
||||
O --> P["docker build -t registry/backend:dev-latest"]
|
||||
P --> Q["docker tag → latest"]
|
||||
end
|
||||
|
||||
subgraph PushDev["📤 Push Registry"]
|
||||
Q --> R[docker login registry]
|
||||
R --> S["docker push :dev-latest"]
|
||||
S --> T["docker push :latest"]
|
||||
end
|
||||
|
||||
subgraph DeployDev["🚀 Déploiement DEV"]
|
||||
T --> U["cd /srv/devops/the-tip-top/dev"]
|
||||
U --> V[docker compose pull]
|
||||
V --> W[docker compose up -d --force-recreate]
|
||||
end
|
||||
|
||||
subgraph HealthDev["🩺 Vérification"]
|
||||
W --> X["curl https://api.dev.dsp5-archi-o24a-15m-g3.fr/health"]
|
||||
X --> Y{HTTP 200 ?}
|
||||
Y -->|Non, retry| Z[Attendre 5s]
|
||||
Z --> X
|
||||
Y -->|Oui| AA[✅ DEV déployé]
|
||||
AA --> AB([Fin])
|
||||
end
|
||||
|
||||
style TriggerDev fill:#e3f2fd
|
||||
style DeployDev fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 2.3 Processus de déploiement PREPROD
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph TriggerPreprod["🔔 Déclencheur PREPROD"]
|
||||
A([Push sur branche preprod]) --> B[Webhook → Jenkins]
|
||||
B --> C[Démarrer pipeline preprod]
|
||||
end
|
||||
|
||||
subgraph InitPreprod["🧭 Initialisation"]
|
||||
C --> D[Détecter branche: preprod]
|
||||
D --> E[Définir ENV=preprod]
|
||||
E --> F[Définir TAG=preprod-latest]
|
||||
F --> G["Définir DEPLOY_PATH=/srv/devops/the-tip-top/preprod"]
|
||||
end
|
||||
|
||||
subgraph QualityPreprod["🔍 Qualité Code - Stricte"]
|
||||
G --> H[npm ci - Installation]
|
||||
H --> I["Parallèle: Lint & Tests"]
|
||||
I --> J[npm run lint]
|
||||
I --> K[npm test]
|
||||
J --> L{Lint OK ?}
|
||||
K --> M{Tests OK ?}
|
||||
L -->|Non| N[❌ Échec - Lint obligatoire]
|
||||
M -->|Non| O[❌ Échec - Tests obligatoires]
|
||||
L -->|Oui| P[✅ Lint passé]
|
||||
M -->|Oui| Q[✅ Tests passés]
|
||||
P --> R{Tous OK ?}
|
||||
Q --> R
|
||||
N --> S([Fin - Échec])
|
||||
O --> S
|
||||
end
|
||||
|
||||
subgraph SonarPreprod["📊 Analyse SonarQube"]
|
||||
R -->|Oui| T[sonar-scanner]
|
||||
T --> U[Envoyer métriques SonarQube]
|
||||
U --> V{Quality Gate ?}
|
||||
V -->|Non| W[⚠️ Warning qualité]
|
||||
V -->|Oui| X[✅ Qualité validée]
|
||||
W --> Y[Continuer avec warning]
|
||||
X --> Y
|
||||
end
|
||||
|
||||
subgraph BuildPreprod["🐳 Build Docker"]
|
||||
Y --> Z["Lire .env.preprod"]
|
||||
Z --> AA["docker build --build-arg NEXT_PUBLIC_*"]
|
||||
AA --> AB["docker tag → preprod-latest"]
|
||||
end
|
||||
|
||||
subgraph DeployPreprod["🚀 Déploiement PREPROD"]
|
||||
AB --> AC[docker login registry]
|
||||
AC --> AD["docker push :preprod-latest"]
|
||||
AD --> AE["cd /srv/devops/the-tip-top/preprod"]
|
||||
AE --> AF[docker compose pull]
|
||||
AF --> AG[docker compose up -d --force-recreate]
|
||||
end
|
||||
|
||||
subgraph HealthPreprod["🩺 Vérification"]
|
||||
AG --> AH["curl https://api.preprod.dsp5-archi-o24a-15m-g3.fr/health"]
|
||||
AH --> AI{HTTP 200 ?}
|
||||
AI -->|Non| AJ[Retry 10x avec 5s intervalle]
|
||||
AJ --> AK{Max retries ?}
|
||||
AK -->|Non| AH
|
||||
AK -->|Oui| AL[❌ Health check échoué]
|
||||
AI -->|Oui| AM[✅ PREPROD déployé]
|
||||
AL --> S
|
||||
AM --> AN([Fin - Succès])
|
||||
end
|
||||
|
||||
style TriggerPreprod fill:#fff3e0
|
||||
style DeployPreprod fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 2.4 Processus de déploiement PROD
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph TriggerProd["🔔 Déclencheur PROD"]
|
||||
A([Push sur branche main]) --> B[Webhook → Jenkins]
|
||||
B --> C{Validation requise ?}
|
||||
C -->|Manuel| D[Approbation manuelle]
|
||||
C -->|Auto| E[Démarrer pipeline prod]
|
||||
D --> E
|
||||
end
|
||||
|
||||
subgraph InitProd["🧭 Initialisation"]
|
||||
E --> F[Détecter branche: main]
|
||||
F --> G[Définir ENV=prod]
|
||||
G --> H[Définir TAG=prod-latest]
|
||||
H --> I["Définir DEPLOY_PATH=/srv/devops/the-tip-top/prod"]
|
||||
end
|
||||
|
||||
subgraph QualityProd["🔍 Qualité Code - Maximum"]
|
||||
I --> J[npm ci - Installation]
|
||||
J --> K["Parallèle: Lint, Tests, Sonar"]
|
||||
K --> L[npm run lint - STRICT]
|
||||
K --> M[npm test - STRICT]
|
||||
K --> N[SonarQube Analysis]
|
||||
L --> O{Lint OK ?}
|
||||
M --> P{Tests OK ?}
|
||||
N --> Q{Quality Gate OK ?}
|
||||
O -->|Non| R[❌ Échec - Aucune tolérance]
|
||||
P -->|Non| R
|
||||
Q -->|Non| S[⚠️ Alerte qualité]
|
||||
O -->|Oui| T[✅]
|
||||
P -->|Oui| T
|
||||
Q -->|Oui| T
|
||||
S --> T
|
||||
R --> U([Fin - Échec])
|
||||
end
|
||||
|
||||
subgraph BackupProd["💾 Sauvegarde pré-déploiement"]
|
||||
T --> V[Créer snapshot DB]
|
||||
V --> W[Backup images Docker actuelles]
|
||||
W --> X[Sauvegarder config]
|
||||
X --> Y[Enregistrer état rollback]
|
||||
end
|
||||
|
||||
subgraph BuildProd["🐳 Build Docker"]
|
||||
Y --> Z["Lire .env.production"]
|
||||
Z --> AA["docker build --build-arg NEXT_PUBLIC_*"]
|
||||
AA --> AB["docker tag → prod-latest"]
|
||||
AB --> AC["docker tag → version semver"]
|
||||
end
|
||||
|
||||
subgraph DeployProd["🚀 Déploiement PROD"]
|
||||
AC --> AD[docker login registry]
|
||||
AD --> AE["docker push :prod-latest"]
|
||||
AE --> AF["cd /srv/devops/the-tip-top/prod"]
|
||||
AF --> AG[docker compose pull]
|
||||
AG --> AH[docker compose up -d --force-recreate]
|
||||
end
|
||||
|
||||
subgraph HealthProd["🩺 Vérification Approfondie"]
|
||||
AH --> AI["curl https://api.dsp5-archi-o24a-15m-g3.fr/health"]
|
||||
AI --> AJ{HTTP 200 ?}
|
||||
AJ -->|Non| AK[Retry 10x]
|
||||
AK --> AL{Échec définitif ?}
|
||||
AL -->|Oui| AM[🔄 Rollback automatique]
|
||||
AM --> AN[Restaurer version précédente]
|
||||
AN --> U
|
||||
AL -->|Non| AI
|
||||
AJ -->|Oui| AO[Tests E2E basiques]
|
||||
AO --> AP{E2E OK ?}
|
||||
AP -->|Non| AM
|
||||
AP -->|Oui| AQ[✅ PROD déployé]
|
||||
AQ --> AR([Fin - Succès])
|
||||
end
|
||||
|
||||
style TriggerProd fill:#ffebee
|
||||
style BackupProd fill:#e8eaf6
|
||||
style DeployProd fill:#e8f5e9
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Procédure de sauvegarde du workflow Production
|
||||
|
||||
### 3.1 Sauvegarde automatique quotidienne
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Trigger["⏰ Déclencheur"]
|
||||
A([Cron: 02:00 UTC]) --> B[Démarrer job sauvegarde]
|
||||
end
|
||||
|
||||
subgraph PreBackup["📋 Pré-sauvegarde"]
|
||||
B --> C[Vérifier espace disque]
|
||||
C --> D{Espace suffisant ?}
|
||||
D -->|Non| E[🚨 Alerte admin]
|
||||
E --> F[Nettoyer anciennes sauvegardes]
|
||||
F --> D
|
||||
D -->|Oui| G[Créer dossier backup timestampé]
|
||||
end
|
||||
|
||||
subgraph DBBackup["🗄️ Sauvegarde Base de Données"]
|
||||
G --> H[Connexion PostgreSQL prod]
|
||||
H --> I["pg_dump --format=custom"]
|
||||
I --> J[Compresser dump .gz]
|
||||
J --> K[Calculer checksum SHA256]
|
||||
K --> L[Vérifier intégrité dump]
|
||||
L --> M{Dump valide ?}
|
||||
M -->|Non| N[🚨 Alerte - Retry]
|
||||
N --> I
|
||||
M -->|Oui| O[✅ DB sauvegardée]
|
||||
end
|
||||
|
||||
subgraph DockerBackup["🐳 Sauvegarde Images Docker"]
|
||||
O --> P[Lister images en production]
|
||||
P --> Q["docker save backend:prod-latest"]
|
||||
Q --> R["docker save frontend:prod-latest"]
|
||||
R --> S[Compresser archives .tar.gz]
|
||||
end
|
||||
|
||||
subgraph ConfigBackup["⚙️ Sauvegarde Configuration"]
|
||||
S --> T[Backup docker-compose.yml]
|
||||
T --> U[Backup .env.production]
|
||||
U --> V[Backup nginx.conf]
|
||||
V --> W[Backup certificats SSL]
|
||||
W --> X[Backup scripts déploiement]
|
||||
end
|
||||
|
||||
subgraph JenkinsBackup["🔧 Sauvegarde Jenkins"]
|
||||
X --> Y[Export jobs Jenkins]
|
||||
Y --> Z[Backup credentials chiffrés]
|
||||
Z --> AA[Backup plugins list]
|
||||
AA --> AB[Backup config globale]
|
||||
end
|
||||
|
||||
subgraph Upload["☁️ Upload Distant"]
|
||||
AB --> AC[Chiffrer archive complète]
|
||||
AC --> AD{Destination}
|
||||
AD --> AE["Upload vers S3/GCS"]
|
||||
AD --> AF["Copie vers serveur backup"]
|
||||
AE --> AG[Vérifier upload]
|
||||
AF --> AG
|
||||
AG --> AH{Upload OK ?}
|
||||
AH -->|Non| AI[🚨 Alerte - Retry]
|
||||
AI --> AC
|
||||
AH -->|Oui| AJ[✅ Backup distant OK]
|
||||
end
|
||||
|
||||
subgraph Cleanup["🧹 Nettoyage"]
|
||||
AJ --> AK[Supprimer backups > 30 jours locaux]
|
||||
AK --> AL[Supprimer backups > 90 jours distants]
|
||||
AL --> AM[Générer rapport backup]
|
||||
AM --> AN[Envoyer notification succès]
|
||||
AN --> AO([Fin])
|
||||
end
|
||||
|
||||
style DBBackup fill:#e3f2fd
|
||||
style Upload fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 3.2 Sauvegarde avant déploiement (Pre-deployment backup)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Trigger["🔔 Déclencheur"]
|
||||
A([Déploiement PROD initié]) --> B[Stage: Pre-deployment backup]
|
||||
end
|
||||
|
||||
subgraph Snapshot["📸 Snapshot rapide"]
|
||||
B --> C[Créer tag timestamp]
|
||||
C --> D["TAG = backup-YYYYMMDD-HHMMSS"]
|
||||
D --> E[docker tag backend:prod-latest → backend:TAG]
|
||||
E --> F[docker tag frontend:prod-latest → frontend:TAG]
|
||||
F --> G[Pousser tags vers registry]
|
||||
end
|
||||
|
||||
subgraph DBSnapshot["🗄️ Snapshot DB"]
|
||||
G --> H[Créer snapshot PostgreSQL]
|
||||
H --> I["pg_dump rapide → backup_pre_deploy.sql"]
|
||||
I --> J[Stocker localement]
|
||||
end
|
||||
|
||||
subgraph StateRecord["📝 Enregistrement État"]
|
||||
J --> K[Enregistrer versions actuelles]
|
||||
K --> L["versions.json: {backend, frontend, db}"]
|
||||
L --> M[Enregistrer docker-compose.yml actuel]
|
||||
M --> N[Enregistrer timestamp rollback point]
|
||||
end
|
||||
|
||||
subgraph Validation["✅ Validation"]
|
||||
N --> O{Tous backups créés ?}
|
||||
O -->|Non| P[❌ Annuler déploiement]
|
||||
P --> Q([Fin - Échec])
|
||||
O -->|Oui| R[✅ Continuer déploiement]
|
||||
R --> S([Fin - Prêt])
|
||||
end
|
||||
|
||||
style Snapshot fill:#fff3e0
|
||||
style StateRecord fill:#e8eaf6
|
||||
```
|
||||
|
||||
### 3.3 Politique de rétention des sauvegardes
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Retention["📅 Politique de Rétention"]
|
||||
A[Backups quotidiens] --> B[Conserver 7 jours]
|
||||
C[Backups hebdomadaires] --> D[Conserver 4 semaines]
|
||||
E[Backups mensuels] --> F[Conserver 12 mois]
|
||||
G[Backups pre-deploy] --> H[Conserver 5 derniers]
|
||||
end
|
||||
|
||||
subgraph Storage["💾 Stockage"]
|
||||
B --> I[Local: /srv/backups/daily/]
|
||||
D --> J[Local: /srv/backups/weekly/]
|
||||
F --> K[Distant: S3/GCS]
|
||||
H --> L[Registry: tags backup-*]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Procédure de restauration
|
||||
|
||||
### 4.1 Restauration DEV
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Trigger["🔔 Déclencheur"]
|
||||
A([Incident DEV détecté]) --> B{Type de problème}
|
||||
B -->|Code cassé| C[Restaurer code]
|
||||
B -->|Container crash| D[Restaurer container]
|
||||
B -->|DB corrompue| E[Restaurer DB]
|
||||
end
|
||||
|
||||
subgraph CodeRestore["📦 Restauration Code"]
|
||||
C --> F[git log - identifier commit stable]
|
||||
F --> G[git revert ou git reset]
|
||||
G --> H[Push vers branche dev]
|
||||
H --> I[Pipeline automatique redéploie]
|
||||
end
|
||||
|
||||
subgraph ContainerRestore["🐳 Restauration Container"]
|
||||
D --> J[Identifier dernière image stable]
|
||||
J --> K["docker pull backend:dev-latest"]
|
||||
K --> L[docker compose up -d --force-recreate]
|
||||
L --> M[Vérifier logs]
|
||||
end
|
||||
|
||||
subgraph DBRestore["🗄️ Restauration DB DEV"]
|
||||
E --> N[Lister backups disponibles]
|
||||
N --> O[Sélectionner backup récent]
|
||||
O --> P[Stopper services dépendants]
|
||||
P --> Q["psql < backup_dev.sql"]
|
||||
Q --> R[Redémarrer services]
|
||||
end
|
||||
|
||||
subgraph Validation["✅ Validation"]
|
||||
I --> S[Health check]
|
||||
M --> S
|
||||
R --> S
|
||||
S --> T{Service OK ?}
|
||||
T -->|Non| U[Escalade vers équipe]
|
||||
T -->|Oui| V[✅ DEV restauré]
|
||||
U --> W([Fin - Manuel requis])
|
||||
V --> X([Fin - Succès])
|
||||
end
|
||||
|
||||
style CodeRestore fill:#e3f2fd
|
||||
style ContainerRestore fill:#fff3e0
|
||||
style DBRestore fill:#fce4ec
|
||||
```
|
||||
|
||||
### 4.2 Restauration PREPROD
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Trigger["🔔 Déclencheur"]
|
||||
A([Incident PREPROD]) --> B[Évaluer impact]
|
||||
B --> C{Sévérité}
|
||||
C -->|Mineure| D[Fix forward - nouveau déploiement]
|
||||
C -->|Majeure| E[Rollback nécessaire]
|
||||
end
|
||||
|
||||
subgraph Analysis["🔍 Analyse"]
|
||||
E --> F[Identifier cause root]
|
||||
F --> G{Cause identifiée}
|
||||
G -->|Code| H[Rollback code]
|
||||
G -->|Config| I[Rollback config]
|
||||
G -->|DB| J[Rollback DB]
|
||||
G -->|Infra| K[Rollback infra]
|
||||
end
|
||||
|
||||
subgraph CodeRollback["📦 Rollback Code PREPROD"]
|
||||
H --> L[Identifier dernier tag stable]
|
||||
L --> M["docker pull backend:preprod-{version}"]
|
||||
M --> N[Mettre à jour docker-compose]
|
||||
N --> O[docker compose up -d]
|
||||
end
|
||||
|
||||
subgraph ConfigRollback["⚙️ Rollback Config"]
|
||||
I --> P[Restaurer .env.preprod backup]
|
||||
P --> Q[Restaurer docker-compose.yml backup]
|
||||
Q --> R[Redéployer avec config précédente]
|
||||
end
|
||||
|
||||
subgraph DBRollback["🗄️ Rollback DB PREPROD"]
|
||||
J --> S[Mettre en maintenance]
|
||||
S --> T[Stopper backend/frontend]
|
||||
T --> U[Restaurer dump PostgreSQL]
|
||||
U --> V["pg_restore -d thetiptop_preprod"]
|
||||
V --> W[Redémarrer services]
|
||||
end
|
||||
|
||||
subgraph InfraRollback["🖥️ Rollback Infra"]
|
||||
K --> X[Vérifier état serveur]
|
||||
X --> Y[Restaurer config nginx]
|
||||
Y --> Z[Renouveler certificats si nécessaire]
|
||||
Z --> AA[Redémarrer services système]
|
||||
end
|
||||
|
||||
subgraph Validation["✅ Validation Post-Rollback"]
|
||||
O --> AB[Health check API]
|
||||
R --> AB
|
||||
W --> AB
|
||||
AA --> AB
|
||||
AB --> AC[Tests de fumée]
|
||||
AC --> AD{Tous tests OK ?}
|
||||
AD -->|Non| AE[🚨 Escalade urgente]
|
||||
AD -->|Oui| AF[✅ PREPROD restauré]
|
||||
AE --> AG([Fin - Intervention manuelle])
|
||||
AF --> AH[Documenter incident]
|
||||
AH --> AI([Fin - Succès])
|
||||
end
|
||||
|
||||
style Analysis fill:#fff3e0
|
||||
style Validation fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 4.3 Restauration PROD (Procédure complète)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Alert["🚨 Alerte Production"]
|
||||
A([Incident PROD détecté]) --> B[Activer procédure incident]
|
||||
B --> C[Notifier équipe on-call]
|
||||
C --> D[Évaluer impact business]
|
||||
end
|
||||
|
||||
subgraph Triage["🔍 Triage"]
|
||||
D --> E{Niveau de sévérité}
|
||||
E -->|P1 - Critique| F[Activation immédiate rollback]
|
||||
E -->|P2 - Majeur| G[Analyse rapide 15min max]
|
||||
E -->|P3 - Mineur| H[Fix forward si possible]
|
||||
G --> I{Fix rapide possible ?}
|
||||
I -->|Oui| J[Appliquer hotfix]
|
||||
I -->|Non| F
|
||||
H --> K[Planifier fix]
|
||||
end
|
||||
|
||||
subgraph RollbackDecision["📋 Décision Rollback"]
|
||||
F --> L[Récupérer point de rollback]
|
||||
L --> M[versions.json du dernier backup]
|
||||
M --> N{Type de rollback}
|
||||
N -->|Complet| O[Rollback full stack]
|
||||
N -->|Partiel Backend| P[Rollback backend seul]
|
||||
N -->|Partiel Frontend| Q[Rollback frontend seul]
|
||||
N -->|DB seulement| R[Rollback DB]
|
||||
end
|
||||
|
||||
subgraph FullRollback["🔄 Rollback Complet PROD"]
|
||||
O --> S[Activer page maintenance]
|
||||
S --> T["Notifier: Maintenance en cours"]
|
||||
T --> U[Stopper tous les services]
|
||||
U --> V[Restaurer images Docker backup]
|
||||
V --> W["docker pull backend:backup-TIMESTAMP"]
|
||||
W --> X["docker pull frontend:backup-TIMESTAMP"]
|
||||
X --> Y[Restaurer DB depuis snapshot]
|
||||
Y --> Z["pg_restore --clean -d thetiptop_prod"]
|
||||
Z --> AA[Restaurer fichiers config]
|
||||
AA --> AB[Redémarrer stack complète]
|
||||
AB --> AC[Désactiver maintenance]
|
||||
end
|
||||
|
||||
subgraph PartialBackend["🔄 Rollback Backend"]
|
||||
P --> AD["docker pull backend:backup-TIMESTAMP"]
|
||||
AD --> AE[docker compose up -d backend]
|
||||
end
|
||||
|
||||
subgraph PartialFrontend["🔄 Rollback Frontend"]
|
||||
Q --> AF["docker pull frontend:backup-TIMESTAMP"]
|
||||
AF --> AG[docker compose up -d frontend]
|
||||
end
|
||||
|
||||
subgraph DBRollback["🗄️ Rollback DB PROD"]
|
||||
R --> AH[Évaluer perte de données]
|
||||
AH --> AI{Perte acceptable ?}
|
||||
AI -->|Non| AJ[Récupération point-in-time]
|
||||
AI -->|Oui| AK[Restauration standard]
|
||||
AJ --> AL[WAL replay jusqu'au point]
|
||||
AK --> AM["pg_restore backup quotidien"]
|
||||
AL --> AN[Synchroniser avec backup]
|
||||
AM --> AN
|
||||
end
|
||||
|
||||
subgraph Verification["✅ Vérification Post-Rollback"]
|
||||
AC --> AO[Health checks complets]
|
||||
AE --> AO
|
||||
AG --> AO
|
||||
AN --> AO
|
||||
AO --> AP["Test: /health endpoint"]
|
||||
AP --> AQ["Test: Login utilisateur"]
|
||||
AQ --> AR["Test: Participation jeu"]
|
||||
AR --> AS["Test: Validation employé"]
|
||||
AS --> AT{Tous tests OK ?}
|
||||
AT -->|Non| AU[🚨 Escalade niveau 2]
|
||||
AT -->|Oui| AV[✅ PROD restauré]
|
||||
end
|
||||
|
||||
subgraph PostIncident["📝 Post-Incident"]
|
||||
AV --> AW[Confirmer stabilité 30min]
|
||||
AW --> AX[Notifier fin incident]
|
||||
AX --> AY[Créer rapport incident]
|
||||
AY --> AZ[Planifier post-mortem]
|
||||
AZ --> BA([Fin])
|
||||
AU --> BB[Intervention manuelle équipe]
|
||||
BB --> BC([Fin - Escalade])
|
||||
end
|
||||
|
||||
style Alert fill:#ffebee
|
||||
style FullRollback fill:#fff3e0
|
||||
style Verification fill:#e8f5e9
|
||||
```
|
||||
|
||||
### 4.4 Matrice de décision de restauration
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Decision["📊 Matrice de Décision"]
|
||||
A[Symptôme] --> B{API down ?}
|
||||
B -->|Oui| C{Frontend OK ?}
|
||||
C -->|Oui| D[Rollback Backend]
|
||||
C -->|Non| E[Rollback Full]
|
||||
B -->|Non| F{Erreurs 500 ?}
|
||||
F -->|Oui| G{Récent déploiement ?}
|
||||
G -->|Oui| H[Rollback dernier déploiement]
|
||||
G -->|Non| I[Analyser logs]
|
||||
F -->|Non| J{Données corrompues ?}
|
||||
J -->|Oui| K[Rollback DB]
|
||||
J -->|Non| L[Investiguer plus]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Justification des processus
|
||||
|
||||
### Pourquoi ces processus ?
|
||||
|
||||
| Processus | Justification |
|
||||
|-----------|---------------|
|
||||
| **Déploiement multi-environnement** | Permet de tester les changements progressivement (dev → preprod → prod) avant la mise en production, réduisant les risques |
|
||||
| **Tests automatisés avant déploiement** | Garantit que le code déployé respecte les standards de qualité et n'introduit pas de régressions |
|
||||
| **Health checks post-déploiement** | Détecte rapidement les problèmes après un déploiement, permettant un rollback automatique si nécessaire |
|
||||
| **Sauvegarde pré-déploiement** | Crée un point de restauration fiable avant chaque changement en production |
|
||||
| **Sauvegarde quotidienne** | Protège contre la perte de données avec une fenêtre de perte maximale de 24h |
|
||||
| **Procédures de rollback documentées** | Réduit le temps de restauration (RTO) en cas d'incident grâce à des procédures claires |
|
||||
| **Politique de rétention** | Optimise l'espace de stockage tout en conservant l'historique nécessaire pour les audits |
|
||||
|
||||
### Indicateurs clés (KPIs)
|
||||
|
||||
| KPI | Objectif | Mesure |
|
||||
|-----|----------|--------|
|
||||
| **RTO** (Recovery Time Objective) | < 30 minutes | Temps pour restaurer le service |
|
||||
| **RPO** (Recovery Point Objective) | < 24 heures | Perte de données maximale acceptable |
|
||||
| **Fréquence de déploiement** | Quotidienne | Nombre de déploiements par jour |
|
||||
| **Taux de succès déploiement** | > 95% | Déploiements réussis / Total |
|
||||
| **MTTR** (Mean Time To Recovery) | < 1 heure | Temps moyen de récupération |
|
||||
|
||||
---
|
||||
|
||||
## Outils recommandés pour visualiser ces diagrammes
|
||||
|
||||
1. **Mermaid Live Editor**: https://mermaid.live/
|
||||
2. **draw.io**: Importer le code Mermaid
|
||||
3. **VS Code**: Extension "Mermaid Preview"
|
||||
4. **GitLab/GitHub**: Rendu natif dans les fichiers Markdown
|
||||
|
||||
---
|
||||
|
||||
*Document généré pour le projet Thé Tip Top - Novembre 2024*
|
||||
|
|
@ -1,17 +1,52 @@
|
|||
// eslint.config.js
|
||||
import js from '@eslint/js';
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.js"],
|
||||
ignores: ["node_modules/**"],
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
// Node.js globals
|
||||
console: 'readonly',
|
||||
process: 'readonly',
|
||||
Buffer: 'readonly',
|
||||
__dirname: 'readonly',
|
||||
__filename: 'readonly',
|
||||
global: 'readonly',
|
||||
module: 'readonly',
|
||||
require: 'readonly',
|
||||
exports: 'writable',
|
||||
fetch: 'readonly',
|
||||
setTimeout: 'readonly',
|
||||
setInterval: 'readonly',
|
||||
clearTimeout: 'readonly',
|
||||
clearInterval: 'readonly',
|
||||
// Jest globals
|
||||
describe: 'readonly',
|
||||
it: 'readonly',
|
||||
test: 'readonly',
|
||||
expect: 'readonly',
|
||||
beforeEach: 'readonly',
|
||||
afterEach: 'readonly',
|
||||
beforeAll: 'readonly',
|
||||
afterAll: 'readonly',
|
||||
jest: 'readonly',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
semi: ["error", "always"],
|
||||
quotes: ["error", "double"],
|
||||
"no-unused-vars": ["warn"],
|
||||
"no-console": "off",
|
||||
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||
'no-console': 'off',
|
||||
'no-undef': 'error',
|
||||
'no-useless-escape': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/**',
|
||||
'dist/**',
|
||||
'coverage/**',
|
||||
'*.config.js',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
259
grafana-http-panel.json
Normal file
259
grafana-http-panel.json
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 1,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 15,
|
||||
"gradientMode": "opacity",
|
||||
"hideFrom": { "legend": false, "tooltip": false, "viz": false },
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 2,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": { "type": "linear" },
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": { "group": "A", "mode": "none" },
|
||||
"thresholdsStyle": { "mode": "off" }
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [{ "color": "green", "value": null }]
|
||||
},
|
||||
"unit": "reqps"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*Frontend.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "blue", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*Backend.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "green", "mode": "fixed" } }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 10, "w": 24, "x": 0, "y": 0 },
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": ["mean", "max", "sum"],
|
||||
"displayMode": "table",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": { "mode": "multi", "sort": "desc" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(http_requests_total{job=\"backend\"}[1m]))",
|
||||
"legendFormat": "Backend - Total",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(http_requests_total{job=\"frontend\"}[1m]))",
|
||||
"legendFormat": "Frontend - Total",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"title": "Historique HTTP - Requêtes/sec",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": { "legend": false, "tooltip": false, "viz": false },
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": { "type": "linear" },
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": { "group": "A", "mode": "normal" },
|
||||
"thresholdsStyle": { "mode": "off" }
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [{ "color": "green", "value": null }]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*2[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "green", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*3[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "blue", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*4[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "yellow", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*5[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 10 },
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": ["sum"],
|
||||
"displayMode": "table",
|
||||
"placement": "right",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": { "mode": "multi", "sort": "desc" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(increase(http_requests_total{job=\"backend\"}[5m])) by (status_code)",
|
||||
"legendFormat": "Backend {{status_code}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Backend - HTTP par Status Code",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": { "legend": false, "tooltip": false, "viz": false },
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": { "type": "linear" },
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": { "group": "A", "mode": "normal" },
|
||||
"thresholdsStyle": { "mode": "off" }
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [{ "color": "green", "value": null }]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*2[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "green", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*3[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "blue", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*4[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "yellow", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": ".*5[0-9]{2}.*" },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 10 },
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": ["sum"],
|
||||
"displayMode": "table",
|
||||
"placement": "right",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": { "mode": "multi", "sort": "desc" }
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(increase(http_requests_total{job=\"frontend\"}[5m])) by (status_code)",
|
||||
"legendFormat": "Frontend {{status_code}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Frontend - HTTP par Status Code",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": ["http", "thetiptop"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Prometheus",
|
||||
"multi": false,
|
||||
"name": "DS_PROMETHEUS",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"type": "datasource"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": { "from": "now-1h", "to": "now" },
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Historique HTTP - TheTipTop",
|
||||
"uid": "historique-http-thetiptop",
|
||||
"version": 1
|
||||
}
|
||||
94
index.js
94
index.js
|
|
@ -1,37 +1,85 @@
|
|||
import express from "express";
|
||||
import cors from "cors";
|
||||
import dotenv from "dotenv";
|
||||
import helmet from "helmet";
|
||||
import morgan from "morgan";
|
||||
import client from "prom-client";
|
||||
import config from "./src/config/env.js";
|
||||
import { pool } from "./db.js";
|
||||
import { errorHandler } from "./src/middleware/errorHandler.js";
|
||||
import { metricsMiddleware } from "./src/middleware/metrics.js";
|
||||
|
||||
// Import routes
|
||||
import authRoutes from "./src/routes/auth.routes.js";
|
||||
import userRoutes from "./src/routes/user.routes.js";
|
||||
import gameRoutes from "./src/routes/game.routes.js";
|
||||
import employeeRoutes from "./src/routes/employee.routes.js";
|
||||
import adminRoutes from "./src/routes/admin.routes.js";
|
||||
import drawRoutes from "./src/routes/draw.routes.js";
|
||||
import newsletterRoutes from "./src/routes/newsletter.routes.js";
|
||||
import contactRoutes from "./src/routes/contact.routes.js";
|
||||
|
||||
dotenv.config();
|
||||
const app = express();
|
||||
|
||||
// CORS doit être configuré AVANT helmet
|
||||
app.use(
|
||||
cors({
|
||||
origin: ["http://localhost:3000", "https://dsp5-archi-o24a-15m-g3.fr"],
|
||||
origin: function (origin, callback) {
|
||||
const allowedOrigins = [
|
||||
// Localhost
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3001",
|
||||
"http://localhost:3002",
|
||||
"http://localhost:3003",
|
||||
"http://localhost:3004",
|
||||
"http://localhost:3005",
|
||||
// Production
|
||||
"https://dsp5-archi-o24a-15m-g3.fr",
|
||||
// Dev
|
||||
"https://dev.dsp5-archi-o24a-15m-g3.fr",
|
||||
// Preprod
|
||||
"https://preprod.dsp5-archi-o24a-15m-g3.fr"
|
||||
];
|
||||
// Autoriser les requêtes sans origin (Postman, curl, etc.)
|
||||
if (!origin) return callback(null, true);
|
||||
if (allowedOrigins.indexOf(origin) !== -1) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(null, true); // En dev, on autorise tout
|
||||
}
|
||||
},
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
||||
exposedHeaders: ['Content-Length', 'X-Request-Id'],
|
||||
maxAge: 86400, // 24h
|
||||
})
|
||||
);
|
||||
app.use(helmet());
|
||||
|
||||
// Helmet avec configuration moins restrictive
|
||||
app.use(helmet({
|
||||
crossOriginResourcePolicy: { policy: "cross-origin" },
|
||||
crossOriginOpenerPolicy: { policy: "same-origin-allow-popups" },
|
||||
}));
|
||||
|
||||
app.use(morgan("tiny"));
|
||||
app.use(express.json());
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok" });
|
||||
});
|
||||
// Middleware de métriques HTTP (doit être avant les routes)
|
||||
app.use(metricsMiddleware);
|
||||
|
||||
// Servir les fichiers statiques depuis le dossier public
|
||||
app.use('/public', express.static('public'));
|
||||
|
||||
|
||||
// ✅ Route racine (pour test ou monitoring)
|
||||
app.get("/", (req, res) => {
|
||||
res.status(200).json({ message: "✅ API The Tip Top en ligne et opérationnelle !" });
|
||||
res.status(200).json({ message: "✅ API The Tip Top en ligne et opérationnelle -branche dev- !" });
|
||||
});
|
||||
// Vérif base de données
|
||||
app.get("/db-check", async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query("SELECT NOW()");
|
||||
res.json({ message: "✅ DB connectée", time: result.rows[0].now });
|
||||
res.json({ message: "✅ DB connectée branche dev", time: result.rows[0].now });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
|
|
@ -45,8 +93,26 @@ app.get("/metrics", async (req, res) => {
|
|||
res.end(await client.register.metrics());
|
||||
});
|
||||
|
||||
// Lancement serveur
|
||||
const PORT = process.env.PORT || 4000;
|
||||
app.listen(PORT, "0.0.0.0", () => {
|
||||
console.log(`🚀 Backend lancé sur 0.0.0.0:${PORT} ✅`);
|
||||
});
|
||||
// API Routes
|
||||
app.use("/api/auth", authRoutes);
|
||||
app.use("/api/users", userRoutes);
|
||||
app.use("/api/game", gameRoutes);
|
||||
app.use("/api/employee", employeeRoutes);
|
||||
app.use("/api/admin", adminRoutes);
|
||||
app.use("/api/draw", drawRoutes);
|
||||
app.use("/api/newsletter", newsletterRoutes);
|
||||
app.use("/api/contact", contactRoutes);
|
||||
|
||||
// Error handler (doit être après les routes)
|
||||
app.use(errorHandler);
|
||||
|
||||
// Export app for testing
|
||||
export default app;
|
||||
|
||||
// Lancement serveur (seulement si pas importé par les tests)
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const PORT = config.server.port;
|
||||
app.listen(PORT, "0.0.0.0", () => {
|
||||
console.log(`🚀 Backend lancé sur 0.0.0.0:${PORT} ✅`);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,6 +1,44 @@
|
|||
// jest.config.js
|
||||
export default {
|
||||
testEnvironment: "node",
|
||||
transform: {}, // pas de transpilation Babel ici
|
||||
// Use Node's experimental ESM support
|
||||
testEnvironment: 'node',
|
||||
|
||||
// Setup files to run before tests
|
||||
setupFiles: ['./jest.setup.js'],
|
||||
|
||||
// Transform ES modules
|
||||
transform: {},
|
||||
|
||||
// Module name mapper for ES modules
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
|
||||
// Test match patterns
|
||||
testMatch: [
|
||||
'**/test/**/*.test.js',
|
||||
'**/__tests__/**/*.js',
|
||||
],
|
||||
|
||||
// Coverage configuration
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.js',
|
||||
'!src/**/*.test.js',
|
||||
'!**/node_modules/**',
|
||||
],
|
||||
|
||||
// Coverage reporters for SonarQube
|
||||
coverageReporters: ['text', 'lcov', 'html'],
|
||||
coverageDirectory: 'coverage',
|
||||
|
||||
// Ignore patterns
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'/dist/',
|
||||
],
|
||||
|
||||
// Verbose output
|
||||
verbose: true,
|
||||
|
||||
// Test timeout (increase for database operations)
|
||||
testTimeout: 10000,
|
||||
};
|
||||
|
|
|
|||
13
jest.setup.js
Normal file
13
jest.setup.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Jest setup file - configure test environment
|
||||
*/
|
||||
|
||||
// Set test environment variables
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-purposes-only';
|
||||
process.env.JWT_EXPIRES_IN = '1h';
|
||||
process.env.PORT = '3001';
|
||||
|
||||
// Disable console logs during tests (optional)
|
||||
// console.log = jest.fn();
|
||||
// console.error = jest.fn();
|
||||
16
node_modules/.bin/acorn
generated
vendored
16
node_modules/.bin/acorn
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../acorn/bin/acorn" "$@"
|
||||
else
|
||||
exec node "$basedir/../acorn/bin/acorn" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/acorn.cmd
generated
vendored
17
node_modules/.bin/acorn.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\acorn\bin\acorn" %*
|
||||
28
node_modules/.bin/acorn.ps1
generated
vendored
28
node_modules/.bin/acorn.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../acorn/bin/acorn" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../acorn/bin/acorn" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../acorn/bin/acorn" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../acorn/bin/acorn" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/baseline-browser-mapping
generated
vendored
16
node_modules/.bin/baseline-browser-mapping
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../baseline-browser-mapping/dist/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../baseline-browser-mapping/dist/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/baseline-browser-mapping.cmd
generated
vendored
17
node_modules/.bin/baseline-browser-mapping.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\baseline-browser-mapping\dist\cli.js" %*
|
||||
28
node_modules/.bin/baseline-browser-mapping.ps1
generated
vendored
28
node_modules/.bin/baseline-browser-mapping.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../baseline-browser-mapping/dist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../baseline-browser-mapping/dist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/browserslist
generated
vendored
16
node_modules/.bin/browserslist
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../browserslist/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../browserslist/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/browserslist.cmd
generated
vendored
17
node_modules/.bin/browserslist.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\browserslist\cli.js" %*
|
||||
28
node_modules/.bin/browserslist.ps1
generated
vendored
28
node_modules/.bin/browserslist.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../browserslist/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/eslint
generated
vendored
16
node_modules/.bin/eslint
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../eslint/bin/eslint.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../eslint/bin/eslint.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/eslint.cmd
generated
vendored
17
node_modules/.bin/eslint.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\eslint\bin\eslint.js" %*
|
||||
28
node_modules/.bin/eslint.ps1
generated
vendored
28
node_modules/.bin/eslint.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../eslint/bin/eslint.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../eslint/bin/eslint.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../eslint/bin/eslint.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../eslint/bin/eslint.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/esparse
generated
vendored
16
node_modules/.bin/esparse
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../esprima/bin/esparse.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../esprima/bin/esparse.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/esparse.cmd
generated
vendored
17
node_modules/.bin/esparse.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esprima\bin\esparse.js" %*
|
||||
28
node_modules/.bin/esparse.ps1
generated
vendored
28
node_modules/.bin/esparse.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../esprima/bin/esparse.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../esprima/bin/esparse.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../esprima/bin/esparse.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../esprima/bin/esparse.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/esvalidate
generated
vendored
16
node_modules/.bin/esvalidate
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../esprima/bin/esvalidate.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../esprima/bin/esvalidate.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/esvalidate.cmd
generated
vendored
17
node_modules/.bin/esvalidate.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esprima\bin\esvalidate.js" %*
|
||||
28
node_modules/.bin/esvalidate.ps1
generated
vendored
28
node_modules/.bin/esvalidate.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../esprima/bin/esvalidate.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../esprima/bin/esvalidate.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../esprima/bin/esvalidate.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../esprima/bin/esvalidate.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/glob
generated
vendored
16
node_modules/.bin/glob
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../glob/dist/esm/bin.mjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../glob/dist/esm/bin.mjs" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/glob.cmd
generated
vendored
17
node_modules/.bin/glob.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\glob\dist\esm\bin.mjs" %*
|
||||
28
node_modules/.bin/glob.ps1
generated
vendored
28
node_modules/.bin/glob.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../glob/dist/esm/bin.mjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../glob/dist/esm/bin.mjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../glob/dist/esm/bin.mjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../glob/dist/esm/bin.mjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/import-local-fixture
generated
vendored
16
node_modules/.bin/import-local-fixture
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../import-local/fixtures/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../import-local/fixtures/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/import-local-fixture.cmd
generated
vendored
17
node_modules/.bin/import-local-fixture.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\import-local\fixtures\cli.js" %*
|
||||
28
node_modules/.bin/import-local-fixture.ps1
generated
vendored
28
node_modules/.bin/import-local-fixture.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../import-local/fixtures/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../import-local/fixtures/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../import-local/fixtures/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../import-local/fixtures/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/jest
generated
vendored
16
node_modules/.bin/jest
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../jest/bin/jest.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../jest/bin/jest.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/jest.cmd
generated
vendored
17
node_modules/.bin/jest.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jest\bin\jest.js" %*
|
||||
28
node_modules/.bin/jest.ps1
generated
vendored
28
node_modules/.bin/jest.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../jest/bin/jest.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../jest/bin/jest.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../jest/bin/jest.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../jest/bin/jest.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/js-yaml
generated
vendored
16
node_modules/.bin/js-yaml
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../js-yaml/bin/js-yaml.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../js-yaml/bin/js-yaml.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/js-yaml.cmd
generated
vendored
17
node_modules/.bin/js-yaml.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\js-yaml\bin\js-yaml.js" %*
|
||||
28
node_modules/.bin/js-yaml.ps1
generated
vendored
28
node_modules/.bin/js-yaml.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/jsesc
generated
vendored
16
node_modules/.bin/jsesc
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
else
|
||||
exec node "$basedir/../jsesc/bin/jsesc" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/jsesc.cmd
generated
vendored
17
node_modules/.bin/jsesc.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\jsesc\bin\jsesc" %*
|
||||
28
node_modules/.bin/jsesc.ps1
generated
vendored
28
node_modules/.bin/jsesc.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../jsesc/bin/jsesc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/json5
generated
vendored
16
node_modules/.bin/json5
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../json5/lib/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../json5/lib/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/json5.cmd
generated
vendored
17
node_modules/.bin/json5.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\json5\lib\cli.js" %*
|
||||
28
node_modules/.bin/json5.ps1
generated
vendored
28
node_modules/.bin/json5.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../json5/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/mime
generated
vendored
16
node_modules/.bin/mime
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../mime/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../mime/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/mime.cmd
generated
vendored
17
node_modules/.bin/mime.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mime\cli.js" %*
|
||||
28
node_modules/.bin/mime.ps1
generated
vendored
28
node_modules/.bin/mime.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../mime/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../mime/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../mime/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../mime/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/napi-postinstall
generated
vendored
16
node_modules/.bin/napi-postinstall
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../napi-postinstall/lib/cli.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../napi-postinstall/lib/cli.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/napi-postinstall.cmd
generated
vendored
17
node_modules/.bin/napi-postinstall.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\napi-postinstall\lib\cli.js" %*
|
||||
28
node_modules/.bin/napi-postinstall.ps1
generated
vendored
28
node_modules/.bin/napi-postinstall.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../napi-postinstall/lib/cli.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../napi-postinstall/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../napi-postinstall/lib/cli.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../napi-postinstall/lib/cli.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/node-gyp-build
generated
vendored
16
node_modules/.bin/node-gyp-build
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../node-gyp-build/bin.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../node-gyp-build/bin.js" "$@"
|
||||
fi
|
||||
16
node_modules/.bin/node-gyp-build-optional
generated
vendored
16
node_modules/.bin/node-gyp-build-optional
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../node-gyp-build/optional.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../node-gyp-build/optional.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/node-gyp-build-optional.cmd
generated
vendored
17
node_modules/.bin/node-gyp-build-optional.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\optional.js" %*
|
||||
28
node_modules/.bin/node-gyp-build-optional.ps1
generated
vendored
28
node_modules/.bin/node-gyp-build-optional.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../node-gyp-build/optional.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../node-gyp-build/optional.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../node-gyp-build/optional.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../node-gyp-build/optional.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/node-gyp-build-test
generated
vendored
16
node_modules/.bin/node-gyp-build-test
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../node-gyp-build/build-test.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../node-gyp-build/build-test.js" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/node-gyp-build-test.cmd
generated
vendored
17
node_modules/.bin/node-gyp-build-test.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\build-test.js" %*
|
||||
28
node_modules/.bin/node-gyp-build-test.ps1
generated
vendored
28
node_modules/.bin/node-gyp-build-test.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../node-gyp-build/build-test.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../node-gyp-build/build-test.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../node-gyp-build/build-test.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../node-gyp-build/build-test.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
17
node_modules/.bin/node-gyp-build.cmd
generated
vendored
17
node_modules/.bin/node-gyp-build.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-gyp-build\bin.js" %*
|
||||
28
node_modules/.bin/node-gyp-build.ps1
generated
vendored
28
node_modules/.bin/node-gyp-build.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../node-gyp-build/bin.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../node-gyp-build/bin.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../node-gyp-build/bin.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../node-gyp-build/bin.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
16
node_modules/.bin/node-which
generated
vendored
16
node_modules/.bin/node-which
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../which/bin/node-which" "$@"
|
||||
else
|
||||
exec node "$basedir/../which/bin/node-which" "$@"
|
||||
fi
|
||||
17
node_modules/.bin/node-which.cmd
generated
vendored
17
node_modules/.bin/node-which.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\which\bin\node-which" %*
|
||||
28
node_modules/.bin/node-which.ps1
generated
vendored
28
node_modules/.bin/node-which.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../which/bin/node-which" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../which/bin/node-which" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../which/bin/node-which" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../which/bin/node-which" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
1
node_modules/.bin/nodemon
generated
vendored
1
node_modules/.bin/nodemon
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../nodemon/bin/nodemon.js
|
||||
17
node_modules/.bin/nodemon.cmd
generated
vendored
17
node_modules/.bin/nodemon.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nodemon\bin\nodemon.js" %*
|
||||
28
node_modules/.bin/nodemon.ps1
generated
vendored
28
node_modules/.bin/nodemon.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../nodemon/bin/nodemon.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../nodemon/bin/nodemon.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../nodemon/bin/nodemon.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../nodemon/bin/nodemon.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
1
node_modules/.bin/nodetouch
generated
vendored
1
node_modules/.bin/nodetouch
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../touch/bin/nodetouch.js
|
||||
17
node_modules/.bin/nodetouch.cmd
generated
vendored
17
node_modules/.bin/nodetouch.cmd
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\touch\bin\nodetouch.js" %*
|
||||
28
node_modules/.bin/nodetouch.ps1
generated
vendored
28
node_modules/.bin/nodetouch.ps1
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../touch/bin/nodetouch.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../touch/bin/nodetouch.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../touch/bin/nodetouch.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../touch/bin/nodetouch.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user