From 645f6f6b5f582aa8dd740681c3d431515c90e300 Mon Sep 17 00:00:00 2001 From: Manus Sandbox Date: Sun, 23 Nov 2025 08:50:35 -0500 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Correction=20de=20l'authentificat?= =?UTF-8?q?ion=20locale=20:=20utilisation=20de=20sdk.createSessionToken()?= =?UTF-8?q?=20au=20lieu=20de=20jsonwebtoken=20pour=20compatibilit=C3=A9=20?= =?UTF-8?q?avec=20le=20syst=C3=A8me=20OAuth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEPLOYMENT.md | 464 ++++++++++++++++++++++++++++++++++++++ INSTALL.md | 290 ++++++++++++++++++++++++ README.md | 455 +++++++++++++++++++++++++++++++++++++ TROUBLESHOOTING.md | 393 ++++++++++++++++++++++++++++++++ create-admin.sh | 229 +++++++++++++++++++ deploy.sh | 273 ++++++++++++++++++++++ diagnose.sh | 233 +++++++++++++++++++ reset-admin-sql.sh | 180 +++++++++++++++ reset-admin.js | 126 +++++++++++ reset-admin.sh | 62 +++++ server/_core/localAuth.ts | 18 +- verify-login.sh | 273 ++++++++++++++++++++++ 12 files changed, 2984 insertions(+), 12 deletions(-) create mode 100644 DEPLOYMENT.md create mode 100644 INSTALL.md create mode 100644 README.md create mode 100644 TROUBLESHOOTING.md create mode 100755 create-admin.sh create mode 100755 deploy.sh create mode 100755 diagnose.sh create mode 100755 reset-admin-sql.sh create mode 100755 reset-admin.js create mode 100755 reset-admin.sh create mode 100755 verify-login.sh diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..4f1bbf7 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,464 @@ +# Guide de Déploiement - Gestion des Formations Manager Itinova + +## Table des matières + +1. [Prérequis](#prérequis) +2. [Installation locale](#installation-locale) +3. [Configuration](#configuration) +4. [Déploiement en production](#déploiement-en-production) +5. [Migration de base de données](#migration-de-base-de-données) +6. [Maintenance](#maintenance) +7. [Dépannage](#dépannage) + +--- + +## Prérequis + +### Environnement de développement + +- **Node.js** : version 22.13.0 ou supérieure +- **pnpm** : gestionnaire de paquets (installé avec Node.js) +- **Base de données** : MySQL 8.0+ ou TiDB (compatible MySQL) +- **Git** : pour le contrôle de version + +### Services externes (optionnels) + +- **Resend** : pour l'envoi d'emails (clé API requise) +- **Plateforme Manus** : pour l'authentification OAuth (déjà configuré) + +--- + +## Installation locale + +### 1. Cloner le projet + +```bash +# Si vous avez accès au dépôt Git +git clone +cd formation-manager-itinova + +# Sinon, extraire l'archive +unzip formation-manager-itinova.zip +cd formation-manager-itinova +``` + +### 2. Installer les dépendances + +```bash +pnpm install +``` + +### 3. Configurer les variables d'environnement + +Créer un fichier `.env` à la racine du projet : + +```env +# Base de données +DATABASE_URL=mysql://user:password@localhost:3306/formation_manager + +# JWT Secret (générer une clé aléatoire sécurisée) +JWT_SECRET=votre_cle_secrete_tres_longue_et_aleatoire + +# OAuth Manus (déjà configuré sur la plateforme) +OAUTH_SERVER_URL=https://api.manus.im +VITE_OAUTH_PORTAL_URL=https://login.manus.im +VITE_APP_ID=votre_app_id + +# Propriétaire +OWNER_OPEN_ID=votre_open_id +OWNER_NAME=Nom du propriétaire + +# Application +VITE_APP_TITLE=Gestion des Formations Manager Itinova +VITE_APP_LOGO=/logo.svg + +# APIs Manus (optionnel) +BUILT_IN_FORGE_API_URL=https://forge.manus.im +BUILT_IN_FORGE_API_KEY=votre_cle_api +VITE_FRONTEND_FORGE_API_KEY=votre_cle_frontend +VITE_FRONTEND_FORGE_API_URL=https://forge.manus.im + +# Analytics (optionnel) +VITE_ANALYTICS_ENDPOINT=https://analytics.manus.im +VITE_ANALYTICS_WEBSITE_ID=votre_website_id +``` + +### 4. Initialiser la base de données + +```bash +# Créer la base de données +mysql -u root -p -e "CREATE DATABASE formation_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + +# Appliquer les migrations +pnpm db:push +``` + +### 5. Créer l'utilisateur administrateur + +L'utilisateur administrateur est créé automatiquement au premier lancement : + +- **Identifiant** : `adminServFormation` +- **Mot de passe** : `Itinova69!` +- **Email** : `admin@formation.local` (modifiable) + +### 6. Lancer l'application en développement + +```bash +pnpm dev +``` + +L'application sera accessible à l'adresse : `http://localhost:3000` + +--- + +## Configuration + +### Configuration SMTP (Resend) + +1. Créer un compte sur [Resend](https://resend.com) +2. Générer une clé API +3. Se connecter à l'application avec l'utilisateur admin +4. Aller dans **Configuration SMTP** +5. Saisir : + - **Clé API Resend** : votre clé API + - **Email expéditeur** : votre email vérifié sur Resend + - **Nom de l'expéditeur** : Itinova Formation +6. Activer le mode **Production** +7. Tester l'envoi + +### Personnalisation des templates d'emails + +1. Se connecter en tant qu'administrateur +2. Aller dans **Templates d'emails** +3. Modifier les templates selon vos besoins : + - Email de confirmation d'inscription + - Email teaser + - Email de rappel J-7 + +### Gestion des utilisateurs + +1. Se connecter en tant qu'administrateur +2. Aller dans **Utilisateurs** +3. Créer de nouveaux utilisateurs avec identifiant et mot de passe +4. Attribuer le rôle **admin** ou **user** + +**Note** : L'utilisateur `adminServFormation` ne peut pas être supprimé et garantit un accès permanent à l'administration. + +--- + +## Déploiement en production + +### Option 1 : Déploiement sur la plateforme Manus (recommandé) + +L'application est déjà configurée pour la plateforme Manus. Pour déployer : + +1. Se connecter à l'interface de gestion Manus +2. Créer un checkpoint via le bouton **Save Checkpoint** +3. Cliquer sur le bouton **Publish** dans l'en-tête +4. Configurer le domaine personnalisé si nécessaire +5. Publier l'application + +**Avantages** : +- Déploiement automatique +- SSL/TLS automatique +- Scaling automatique +- Base de données TiDB incluse +- Pas de configuration serveur nécessaire + +### Option 2 : Déploiement manuel sur serveur VPS + +#### Prérequis serveur + +- Ubuntu 22.04 LTS ou supérieur +- 2 CPU, 4 GB RAM minimum +- Node.js 22.13.0+ +- MySQL 8.0+ ou MariaDB 10.6+ +- Nginx (reverse proxy) +- Certbot (SSL/TLS) + +#### Étapes de déploiement + +1. **Préparer le serveur** + +```bash +# Mettre à jour le système +sudo apt update && sudo apt upgrade -y + +# Installer Node.js 22 +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt install -y nodejs + +# Installer pnpm +npm install -g pnpm + +# Installer MySQL +sudo apt install -y mysql-server + +# Installer Nginx +sudo apt install -y nginx + +# Installer Certbot +sudo apt install -y certbot python3-certbot-nginx +``` + +2. **Déployer l'application** + +```bash +# Créer un utilisateur dédié +sudo useradd -m -s /bin/bash formation +sudo su - formation + +# Cloner ou copier le projet +cd /home/formation +# Copier les fichiers du projet ici + +# Installer les dépendances +pnpm install + +# Configurer les variables d'environnement +cp .env.example .env +nano .env + +# Build de production +pnpm build +``` + +3. **Configurer le service systemd** + +Créer le fichier `/etc/systemd/system/formation-manager.service` : + +```ini +[Unit] +Description=Formation Manager Itinova +After=network.target mysql.service + +[Service] +Type=simple +User=formation +WorkingDirectory=/home/formation/formation-manager-itinova +ExecStart=/usr/bin/pnpm start +Restart=on-failure +RestartSec=10 +Environment=NODE_ENV=production + +[Install] +WantedBy=multi-user.target +``` + +Activer et démarrer le service : + +```bash +sudo systemctl daemon-reload +sudo systemctl enable formation-manager +sudo systemctl start formation-manager +sudo systemctl status formation-manager +``` + +4. **Configurer Nginx** + +Créer le fichier `/etc/nginx/sites-available/formation-manager` : + +```nginx +server { + listen 80; + server_name votre-domaine.com; + + location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + client_max_body_size 10M; +} +``` + +Activer le site : + +```bash +sudo ln -s /etc/nginx/sites-available/formation-manager /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +5. **Configurer SSL/TLS avec Let's Encrypt** + +```bash +sudo certbot --nginx -d votre-domaine.com +``` + +--- + +## Migration de base de données + +### Appliquer les migrations + +```bash +# En développement +pnpm db:push + +# En production (avec sauvegarde) +# 1. Sauvegarder la base de données +mysqldump -u root -p formation_manager > backup_$(date +%Y%m%d_%H%M%S).sql + +# 2. Appliquer les migrations +pnpm db:push +``` + +### Restaurer une sauvegarde + +```bash +mysql -u root -p formation_manager < backup_20250123_120000.sql +``` + +--- + +## Maintenance + +### Logs de l'application + +```bash +# Logs systemd +sudo journalctl -u formation-manager -f + +# Logs Nginx +sudo tail -f /var/log/nginx/access.log +sudo tail -f /var/log/nginx/error.log +``` + +### Mise à jour de l'application + +```bash +# Arrêter le service +sudo systemctl stop formation-manager + +# Mettre à jour le code +cd /home/formation/formation-manager-itinova +git pull origin main + +# Installer les nouvelles dépendances +pnpm install + +# Appliquer les migrations +pnpm db:push + +# Rebuild +pnpm build + +# Redémarrer le service +sudo systemctl start formation-manager +``` + +### Sauvegarde automatique + +Créer un script de sauvegarde `/home/formation/backup.sh` : + +```bash +#!/bin/bash +BACKUP_DIR="/home/formation/backups" +DATE=$(date +%Y%m%d_%H%M%S) + +# Créer le répertoire de sauvegarde +mkdir -p $BACKUP_DIR + +# Sauvegarder la base de données +mysqldump -u root -p formation_manager > $BACKUP_DIR/db_$DATE.sql + +# Compresser +gzip $BACKUP_DIR/db_$DATE.sql + +# Supprimer les sauvegardes de plus de 30 jours +find $BACKUP_DIR -name "db_*.sql.gz" -mtime +30 -delete + +echo "Sauvegarde terminée : $BACKUP_DIR/db_$DATE.sql.gz" +``` + +Ajouter une tâche cron : + +```bash +crontab -e + +# Sauvegarde quotidienne à 2h du matin +0 2 * * * /home/formation/backup.sh +``` + +--- + +## Dépannage + +### L'application ne démarre pas + +1. Vérifier les logs : + ```bash + sudo journalctl -u formation-manager -n 50 + ``` + +2. Vérifier la connexion à la base de données : + ```bash + mysql -u root -p -e "SHOW DATABASES;" + ``` + +3. Vérifier les variables d'environnement dans `.env` + +### Erreur de connexion à la base de données + +1. Vérifier que MySQL est démarré : + ```bash + sudo systemctl status mysql + ``` + +2. Tester la connexion : + ```bash + mysql -u user -p formation_manager + ``` + +3. Vérifier l'URL de connexion dans `.env` + +### Les emails ne sont pas envoyés + +1. Vérifier la configuration SMTP dans l'interface admin +2. Vérifier que la clé API Resend est valide +3. Vérifier que l'email expéditeur est vérifié sur Resend +4. Consulter les logs de l'application + +### Problème de permissions + +```bash +# Corriger les permissions +sudo chown -R formation:formation /home/formation/formation-manager-itinova +sudo chmod -R 755 /home/formation/formation-manager-itinova +``` + +### L'authentification locale ne fonctionne pas + +1. Vérifier que `JWT_SECRET` est défini dans `.env` +2. Vérifier que l'utilisateur `adminServFormation` existe dans la base de données +3. Réinitialiser le mot de passe si nécessaire via SQL : + +```sql +-- Le mot de passe hashé pour "Itinova69!" +UPDATE users +SET password = '$2a$10$...' +WHERE username = 'adminServFormation'; +``` + +--- + +## Support + +Pour toute question ou problème : + +1. Consulter la documentation complète dans le dossier `/docs` +2. Vérifier les logs de l'application +3. Contacter l'équipe de développement + +--- + +**Version** : 1.0.0 +**Dernière mise à jour** : 23 novembre 2025 diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..bc712cb --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,290 @@ +# Guide d'Installation Rapide + +## Installation en 5 minutes + +Ce guide vous permet d'installer et de démarrer l'application rapidement en environnement de développement. + +--- + +## Prérequis + +Avant de commencer, assurez-vous d'avoir installé : + +- **Node.js 22+** : [Télécharger Node.js](https://nodejs.org/) +- **MySQL 8.0+** ou **MariaDB 10.6+** : [Télécharger MySQL](https://dev.mysql.com/downloads/) +- **Git** (optionnel) : [Télécharger Git](https://git-scm.com/) + +--- + +## Étape 1 : Récupérer le projet + +### Option A : Depuis l'archive ZIP + +```bash +# Extraire l'archive +unzip formation-manager-itinova.zip +cd formation-manager-itinova +``` + +### Option B : Depuis Git (si disponible) + +```bash +# Cloner le dépôt +git clone +cd formation-manager-itinova +``` + +--- + +## Étape 2 : Installer les dépendances + +```bash +# Installer pnpm si nécessaire +npm install -g pnpm + +# Installer les dépendances du projet +pnpm install +``` + +--- + +## Étape 3 : Configurer la base de données + +### 3.1 Créer la base de données + +```bash +# Se connecter à MySQL +mysql -u root -p + +# Créer la base de données +CREATE DATABASE formation_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +# Créer un utilisateur dédié (optionnel mais recommandé) +CREATE USER 'formation_user'@'localhost' IDENTIFIED BY 'votre_mot_de_passe_securise'; +GRANT ALL PRIVILEGES ON formation_manager.* TO 'formation_user'@'localhost'; +FLUSH PRIVILEGES; + +# Quitter MySQL +EXIT; +``` + +### 3.2 Configurer les variables d'environnement + +Créer un fichier `.env` à la racine du projet : + +```env +# Base de données +DATABASE_URL=mysql://formation_user:votre_mot_de_passe_securise@localhost:3306/formation_manager + +# JWT Secret (générer une clé aléatoire) +JWT_SECRET=votre_cle_secrete_aleatoire_tres_longue_minimum_32_caracteres + +# Application +VITE_APP_TITLE=Gestion des Formations Manager Itinova +VITE_APP_LOGO=/logo.svg +NODE_ENV=development +``` + +**Note** : Pour générer une clé JWT sécurisée, vous pouvez utiliser : + +```bash +# Linux/Mac +openssl rand -base64 32 + +# Node.js +node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" +``` + +### 3.3 Initialiser la base de données + +```bash +# Appliquer les migrations (créer les tables) +pnpm db:push +``` + +--- + +## Étape 4 : Démarrer l'application + +```bash +# Lancer le serveur de développement +pnpm dev +``` + +L'application sera accessible à l'adresse : **http://localhost:3000** + +--- + +## Étape 5 : Première connexion + +### Connexion avec l'utilisateur administrateur local + +1. Ouvrir **http://localhost:3000** dans votre navigateur +2. Cliquer sur **"Se connecter localement"** +3. Utiliser les identifiants par défaut : + - **Identifiant** : `adminServFormation` + - **Mot de passe** : `Itinova69!` + +**Important** : Cet utilisateur est créé automatiquement au premier lancement et ne peut pas être supprimé. + +--- + +## Configuration initiale + +Une fois connecté, vous pouvez : + +### 1. Configurer l'envoi d'emails (optionnel) + +1. Créer un compte sur [Resend](https://resend.com) +2. Générer une clé API +3. Dans l'application, aller dans **Configuration SMTP** +4. Saisir : + - **Clé API Resend** : votre clé API + - **Email expéditeur** : votre email vérifié + - **Nom de l'expéditeur** : Itinova Formation +5. Activer le mode **Production** + +### 2. Créer des utilisateurs supplémentaires + +1. Aller dans **Utilisateurs** +2. Cliquer sur **Ajouter un utilisateur** +3. Remplir les informations : + - Nom d'utilisateur + - Mot de passe + - Email + - Rôle (admin ou user) + +### 3. Créer vos premières formations + +1. Aller dans **Formations** +2. Cliquer sur **Nouvelle formation** +3. Remplir les informations de la formation +4. Créer des séquences associées + +### 4. Importer les apprenants + +1. Aller dans **Apprenants** +2. Ajouter les apprenants un par un ou créer un script d'import + +--- + +## Commandes utiles + +```bash +# Démarrer le serveur de développement +pnpm dev + +# Builder pour la production +pnpm build + +# Démarrer en mode production +pnpm start + +# Appliquer les migrations +pnpm db:push + +# Générer les migrations (après modification du schéma) +pnpm db:generate + +# Ouvrir Drizzle Studio (interface graphique pour la base de données) +pnpm db:studio +``` + +--- + +## Structure du projet + +``` +formation-manager-itinova/ +├── client/ # Application frontend (React) +│ ├── public/ # Fichiers statiques +│ └── src/ +│ ├── pages/ # Pages de l'application +│ ├── components/ # Composants réutilisables +│ └── lib/ # Bibliothèques et utilitaires +├── server/ # Application backend (Express + tRPC) +│ ├── routers.ts # Définition des routes API +│ ├── db.ts # Fonctions d'accès à la base de données +│ └── _core/ # Configuration du serveur +├── drizzle/ # Schéma et migrations de base de données +│ └── schema.ts # Définition des tables +├── shared/ # Code partagé entre client et serveur +├── .env # Variables d'environnement (à créer) +├── package.json # Dépendances du projet +├── deploy.sh # Script de déploiement automatique +├── DEPLOYMENT.md # Guide de déploiement complet +└── README.md # Documentation principale +``` + +--- + +## Dépannage rapide + +### L'application ne démarre pas + +**Erreur : "Cannot find module"** +```bash +# Réinstaller les dépendances +rm -rf node_modules +pnpm install +``` + +**Erreur : "ECONNREFUSED" (connexion à la base de données)** +```bash +# Vérifier que MySQL est démarré +sudo systemctl status mysql + +# Vérifier l'URL de connexion dans .env +cat .env | grep DATABASE_URL +``` + +### Les migrations échouent + +```bash +# Vérifier que la base de données existe +mysql -u root -p -e "SHOW DATABASES;" + +# Recréer la base de données si nécessaire +mysql -u root -p -e "DROP DATABASE IF EXISTS formation_manager; CREATE DATABASE formation_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + +# Réappliquer les migrations +pnpm db:push +``` + +### Impossible de se connecter + +**Vérifier que JWT_SECRET est défini** +```bash +cat .env | grep JWT_SECRET +``` + +Si vide, générer une nouvelle clé : +```bash +echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env +``` + +--- + +## Prochaines étapes + +Une fois l'installation terminée : + +1. **Lire la documentation complète** : `DEPLOYMENT.md` +2. **Personnaliser les templates d'emails** : Menu "Templates d'emails" +3. **Configurer les rappels automatiques** : Menu "Rappels" +4. **Tester l'envoi d'emails** : Configuration SMTP +5. **Déployer en production** : Suivre le guide `DEPLOYMENT.md` + +--- + +## Support + +Pour toute question ou problème : + +- Consulter `DEPLOYMENT.md` pour la documentation complète +- Vérifier les logs de l'application +- Contacter l'équipe de développement + +--- + +**Bon déploiement ! 🚀** diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e60904 --- /dev/null +++ b/README.md @@ -0,0 +1,455 @@ +# Gestion des Formations Manager Itinova + +Application web complète de gestion des formations pour Manager Itinova, permettant la planification, l'organisation et le suivi des formations avec gestion automatisée des inscriptions, rappels et communications. + +![Version](https://img.shields.io/badge/version-1.0.0-blue.svg) +![Node](https://img.shields.io/badge/node-22.13.0-green.svg) +![License](https://img.shields.io/badge/license-Proprietary-red.svg) + +--- + +## 📋 Table des matières + +- [Aperçu](#aperçu) +- [Fonctionnalités](#fonctionnalités) +- [Technologies](#technologies) +- [Installation rapide](#installation-rapide) +- [Documentation](#documentation) +- [Architecture](#architecture) +- [Utilisation](#utilisation) +- [Déploiement](#déploiement) +- [Maintenance](#maintenance) +- [Support](#support) + +--- + +## 🎯 Aperçu + +L'application **Gestion des Formations Manager Itinova** est une solution complète pour gérer l'ensemble du cycle de vie des formations : + +- Création et planification des formations et séquences +- Gestion des apprenants et inscriptions avec validation automatique +- Génération automatique de documents (listes, feuilles de présence) +- Envoi automatique d'emails (confirmations, rappels) +- Calendrier interactif et système de rappels +- Rapports et statistiques détaillés + +L'application offre **deux modes d'authentification** : +- **Connexion Manus OAuth** : pour les utilisateurs avec compte Manus (Google, Microsoft, Apple) +- **Connexion locale** : pour les administrateurs avec identifiant et mot de passe + +--- + +## ✨ Fonctionnalités + +### 🔐 Authentification dual + +- Page de choix de connexion +- Authentification Manus OAuth (Google, Microsoft, Apple) +- Authentification locale par identifiant/mot de passe +- Gestion des rôles (admin, user) +- Utilisateur administrateur permanent non supprimable + +### 📚 Gestion des formations + +- CRUD complet des formations +- Support de multiples séquences par formation +- Jusqu'à 4 dates par séquence +- Champs personnalisés : + - Public cible (Directeurs, Chefs de service, Autre, Tous) + - Formateur avec autocomplétion + - Capacité maximale par séquence + - Lieu de formation +- Barre de progression colorée (vert/orange/rouge) selon le taux de remplissage +- Filtres et recherche avancée + +### 👥 Gestion des apprenants + +- CRUD complet des apprenants +- Fonction (Directeur, Chef de service, Autre) +- Page de détail par apprenant avec historique complet +- Système d'inscription avec : + - Validation de capacité automatique + - Détection anti-doublons + - Blocage automatique à J-15 + - Alerte si public cible incompatible + +### 📅 Calendrier et rappels + +- Calendrier mensuel interactif +- Navigation par mois +- Visualisation des séquences par date +- Système de rappels automatiques configurables : + - Rappel J-7 + - Rappel J-3 + - Rappel J-1 + - Rappel personnalisé + +### 📄 Exports et documents + +- **Export Excel** : listes d'inscrits avec toutes les informations +- **Export PDF** : listes d'inscrits formatées +- **Feuilles de présence PDF** : + - Logo Itinova + - Double signature (matin/après-midi) + - Signature formateur + - Informations complètes de la séquence + +### ✉️ Communications automatiques + +- **Génération d'invitations Outlook** (.ics) +- **Emails automatiques** : + - Email de confirmation d'inscription + - Email teaser (présentation de la formation) + - Email de rappel J-7 +- **Templates personnalisables** avec variables dynamiques +- **Configuration SMTP** avec Resend +- Mode test et production + +### 📊 Rapports et statistiques + +- Tableau de bord avec indicateurs clés +- Répartition des apprenants par fonction +- Analyse public cible vs fonction réelle +- Statistiques de remplissage des séquences +- Historique des inscriptions + +### 👤 Administration + +- Gestion des utilisateurs +- Création, modification, activation/désactivation +- Attribution des rôles +- Utilisateur admin permanent (adminServFormation) +- Templates d'emails personnalisables +- Configuration SMTP centralisée + +--- + +## 🛠 Technologies + +### Frontend + +- **React 19** : bibliothèque UI moderne +- **Tailwind CSS 4** : framework CSS utility-first +- **shadcn/ui** : composants UI accessibles +- **Wouter** : routage léger +- **TanStack Query** : gestion des données asynchrones +- **Recharts** : visualisation de données +- **date-fns** : manipulation de dates + +### Backend + +- **Node.js 22** : runtime JavaScript +- **Express 4** : framework web +- **tRPC 11** : API type-safe end-to-end +- **Drizzle ORM** : ORM TypeScript-first +- **MySQL/TiDB** : base de données relationnelle + +### Outils + +- **TypeScript** : typage statique +- **Vite** : build tool rapide +- **pnpm** : gestionnaire de paquets performant +- **Vitest** : framework de tests + +### Services externes + +- **Resend** : envoi d'emails transactionnels +- **Manus OAuth** : authentification sociale + +--- + +## 🚀 Installation rapide + +### Prérequis + +- Node.js 22+ +- MySQL 8.0+ ou MariaDB 10.6+ +- pnpm (installé automatiquement avec Node.js) + +### Installation en 4 étapes + +```bash +# 1. Installer les dépendances +pnpm install + +# 2. Configurer les variables d'environnement +cp .env.example .env +# Éditer .env avec vos paramètres + +# 3. Créer et initialiser la base de données +mysql -u root -p -e "CREATE DATABASE formation_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +pnpm db:push + +# 4. Démarrer l'application +pnpm dev +``` + +L'application sera accessible à **http://localhost:3000** + +### Connexion initiale + +- **Identifiant** : `adminServFormation` +- **Mot de passe** : `Itinova69!` + +📖 **Guide complet** : voir [INSTALL.md](INSTALL.md) + +--- + +## 📚 Documentation + +- **[INSTALL.md](INSTALL.md)** : Guide d'installation rapide (5 minutes) +- **[DEPLOYMENT.md](DEPLOYMENT.md)** : Guide de déploiement complet +- **[deploy.sh](deploy.sh)** : Script de déploiement automatique + +--- + +## 🏗 Architecture + +### Structure du projet + +``` +formation-manager-itinova/ +├── client/ # Application frontend +│ ├── public/ # Fichiers statiques +│ │ └── logo.svg # Logo de l'application +│ └── src/ +│ ├── pages/ # Pages de l'application +│ │ ├── LoginChoice.tsx +│ │ ├── Login.tsx +│ │ ├── Dashboard.tsx +│ │ ├── Formations.tsx +│ │ ├── Sequences.tsx +│ │ ├── Apprenants.tsx +│ │ └── ... +│ ├── components/ # Composants réutilisables +│ │ ├── DashboardLayout.tsx +│ │ └── ui/ # Composants shadcn/ui +│ └── lib/ # Bibliothèques et utilitaires +│ └── trpc.ts # Client tRPC +├── server/ # Application backend +│ ├── routers.ts # Routes API tRPC +│ ├── db.ts # Fonctions d'accès BDD +│ └── _core/ # Configuration serveur +│ ├── index.ts # Point d'entrée +│ ├── trpc.ts # Configuration tRPC +│ ├── localAuth.ts # Authentification locale +│ └── ... +├── drizzle/ # Base de données +│ └── schema.ts # Schéma des tables +├── shared/ # Code partagé +│ └── const.ts # Constantes +├── .env # Variables d'environnement +├── package.json # Dépendances +├── deploy.sh # Script de déploiement +├── DEPLOYMENT.md # Guide de déploiement +├── INSTALL.md # Guide d'installation +└── README.md # Ce fichier +``` + +### Base de données + +**Tables principales** : + +- `users` : utilisateurs de l'application +- `formations` : formations disponibles +- `sequences` : séquences de formation (dates) +- `apprenants` : apprenants inscrits +- `inscriptions` : inscriptions aux séquences +- `rappels` : rappels configurés +- `email_templates` : templates d'emails +- `email_config` : configuration SMTP + +### API (tRPC) + +L'API utilise tRPC pour une communication type-safe entre le client et le serveur. + +**Routers principaux** : + +- `auth` : authentification (me, logout) +- `formations` : CRUD formations +- `sequences` : CRUD séquences +- `apprenants` : CRUD apprenants +- `inscriptions` : gestion des inscriptions +- `rappels` : gestion des rappels +- `emails` : envoi d'emails et gestion des templates +- `users` : gestion des utilisateurs +- `stats` : statistiques et rapports + +--- + +## 💻 Utilisation + +### Commandes disponibles + +```bash +# Développement +pnpm dev # Démarrer le serveur de développement +pnpm build # Builder pour la production +pnpm start # Démarrer en mode production + +# Base de données +pnpm db:push # Appliquer les migrations +pnpm db:generate # Générer les migrations +pnpm db:studio # Ouvrir Drizzle Studio + +# Tests +pnpm test # Lancer les tests +pnpm test:ui # Interface de tests Vitest + +# Linting +pnpm lint # Vérifier le code +pnpm format # Formater le code +``` + +### Workflow typique + +1. **Créer une formation** + - Menu "Formations" → "Nouvelle formation" + - Remplir les informations + - Sauvegarder + +2. **Créer des séquences** + - Cliquer sur la formation + - "Ajouter une séquence" + - Définir les dates (jusqu'à 4) + - Définir la capacité maximale + +3. **Ajouter des apprenants** + - Menu "Apprenants" → "Ajouter un apprenant" + - Remplir nom, prénom, email, fonction + +4. **Gérer les inscriptions** + - Depuis la page de la séquence + - Ou depuis la page de l'apprenant + - Validation automatique de la capacité + +5. **Configurer les rappels** + - Menu "Rappels" + - Activer les rappels souhaités (J-7, J-3, J-1) + - Les emails seront envoyés automatiquement + +6. **Exporter les documents** + - Liste des inscrits (Excel ou PDF) + - Feuille de présence (PDF) + - Invitation Outlook (.ics) + +--- + +## 🌐 Déploiement + +### Option 1 : Plateforme Manus (recommandé) + +L'application est optimisée pour la plateforme Manus : + +1. Créer un checkpoint +2. Cliquer sur "Publish" +3. Configurer le domaine +4. Publier + +**Avantages** : déploiement automatique, SSL, scaling, base de données incluse. + +### Option 2 : Serveur VPS + +Utiliser le script de déploiement automatique : + +```bash +# Rendre le script exécutable +chmod +x deploy.sh + +# Lancer le déploiement +./deploy.sh +``` + +Le script effectue automatiquement : +- Vérification des prérequis +- Installation des dépendances +- Sauvegarde de la base de données +- Application des migrations +- Build de l'application +- Redémarrage du service + +📖 **Guide complet** : voir [DEPLOYMENT.md](DEPLOYMENT.md) + +--- + +## 🔧 Maintenance + +### Sauvegardes automatiques + +Le script de déploiement crée automatiquement des sauvegardes de la base de données. + +Pour configurer des sauvegardes régulières : + +```bash +# Ajouter une tâche cron +crontab -e + +# Sauvegarde quotidienne à 2h du matin +0 2 * * * /chemin/vers/backup.sh +``` + +### Mise à jour + +```bash +# Récupérer les dernières modifications +git pull origin main + +# Lancer le déploiement +./deploy.sh +``` + +### Logs + +```bash +# Logs de l'application (systemd) +sudo journalctl -u formation-manager -f + +# Logs Nginx +sudo tail -f /var/log/nginx/access.log +sudo tail -f /var/log/nginx/error.log +``` + +--- + +## 📞 Support + +### Documentation + +- [Guide d'installation](INSTALL.md) +- [Guide de déploiement](DEPLOYMENT.md) +- Code source commenté + +### Dépannage + +Consultez la section "Dépannage" dans [DEPLOYMENT.md](DEPLOYMENT.md) pour les problèmes courants. + +### Contact + +Pour toute question ou problème : +- Consulter la documentation +- Vérifier les logs de l'application +- Contacter l'équipe de développement + +--- + +## 📝 Licence + +Ce projet est la propriété de Manager Itinova. Tous droits réservés. + +--- + +## 🙏 Crédits + +Développé avec ❤️ pour Manager Itinova + +**Technologies utilisées** : +- React, Tailwind CSS, shadcn/ui +- Node.js, Express, tRPC +- Drizzle ORM, MySQL +- Resend, Manus OAuth + +--- + +**Version** : 1.0.0 +**Dernière mise à jour** : 23 novembre 2025 diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..f2bd6a8 --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,393 @@ +# Guide de Dépannage - Problèmes de Connexion + +## Problème : "Identifiant ou mot de passe incorrect" + +Si vous ne parvenez pas à vous connecter avec l'utilisateur `adminServFormation` et le mot de passe `Itinova69!`, suivez ce guide étape par étape. + +--- + +## Diagnostic rapide + +### Étape 1 : Exécuter le script de diagnostic + +```bash +cd /chemin/vers/formation-manager-itinova +./diagnose.sh +``` + +Ce script vérifie automatiquement : +- Les variables d'environnement (DATABASE_URL, JWT_SECRET) +- La connexion à la base de données +- L'existence des tables +- L'existence de l'utilisateur adminServFormation +- Les dépendances installées +- L'état du service + +--- + +## Solutions par cause + +### Cause 1 : JWT_SECRET non défini + +**Symptôme** : Le diagnostic affiche `JWT_SECRET n'est pas définie` + +**Solution** : + +1. Générer une clé JWT sécurisée : + ```bash + openssl rand -base64 32 + ``` + +2. Ajouter la clé dans le fichier `.env` : + ```bash + echo "JWT_SECRET=votre_cle_generee_ici" >> .env + ``` + +3. Redémarrer l'application : + ```bash + # Si vous utilisez systemd + sudo systemctl restart formation-manager + + # Ou en développement + pnpm dev + ``` + +--- + +### Cause 2 : L'utilisateur adminServFormation n'existe pas + +**Symptôme** : Le diagnostic affiche `Utilisateur 'adminServFormation' n'existe pas` + +**Solution** : + +Exécuter le script de réinitialisation : + +```bash +./reset-admin-sql.sh +``` + +**Note** : Utilisez `reset-admin-sql.sh` (version SQL directe) plutôt que `reset-admin.sh` pour éviter les problèmes de compilation TypeScript. + +Ce script va : +- Vérifier la configuration +- Créer l'utilisateur adminServFormation s'il n'existe pas +- Réinitialiser le mot de passe s'il existe déjà +- Afficher les identifiants de connexion + +**Résultat attendu** : +``` +✅ Utilisateur créé avec succès + +🔑 Vous pouvez maintenant vous connecter avec: + Identifiant: adminServFormation + Mot de passe: Itinova69! +``` + +--- + +### Cause 3 : Les tables de la base de données n'existent pas + +**Symptôme** : Le diagnostic affiche `Aucune table trouvée dans la base de données` + +**Solution** : + +1. Appliquer les migrations : + ```bash + pnpm db:push + ``` + +2. Créer l'utilisateur administrateur : + ```bash + ./reset-admin-sql.sh + ``` + +3. Redémarrer l'application : + ```bash + sudo systemctl restart formation-manager + ``` + +--- + +### Cause 4 : Problème de hashage du mot de passe + +**Symptôme** : L'utilisateur existe mais le mot de passe ne fonctionne pas + +**Solution** : + +Réinitialiser le mot de passe : + +```bash +./reset-admin-sql.sh +``` + +Le script va détecter que l'utilisateur existe et mettre à jour uniquement le mot de passe. + +--- + +### Cause 5 : La dépendance bcryptjs n'est pas installée + +**Symptôme** : Erreur dans les logs : `Cannot find module 'bcryptjs'` + +**Solution** : + +1. Réinstaller les dépendances : + ```bash + pnpm install + ``` + +2. Vérifier que bcryptjs est installé : + ```bash + ls node_modules/bcryptjs + ``` + +3. Redémarrer l'application : + ```bash + sudo systemctl restart formation-manager + ``` + +--- + +### Cause 6 : La dépendance jsonwebtoken n'est pas installée + +**Symptôme** : Erreur dans les logs : `Cannot find module 'jsonwebtoken'` + +**Solution** : + +1. Installer jsonwebtoken : + ```bash + pnpm install jsonwebtoken + ``` + +2. Redémarrer l'application : + ```bash + sudo systemctl restart formation-manager + ``` + +--- + +## Vérification manuelle de la base de données + +Si les scripts ne fonctionnent pas, vous pouvez vérifier manuellement la base de données. + +### 1. Se connecter à MySQL + +```bash +mysql -u root -p +``` + +### 2. Sélectionner la base de données + +```sql +USE formation_manager; +``` + +### 3. Vérifier que la table users existe + +```sql +SHOW TABLES; +``` + +Vous devriez voir `users` dans la liste. + +### 4. Vérifier l'utilisateur adminServFormation + +```sql +SELECT username, email, role, isActive, password FROM users WHERE username = 'adminServFormation'; +``` + +**Résultat attendu** : +``` ++--------------------+------------------------+-------+----------+--------------------------------------------------------------+ +| username | email | role | isActive | password | ++--------------------+------------------------+-------+----------+--------------------------------------------------------------+ +| adminServFormation | admin@formation.local | admin | 1 | $2a$10$... (hash bcrypt) | ++--------------------+------------------------+-------+----------+--------------------------------------------------------------+ +``` + +### 5. Si l'utilisateur n'existe pas, le créer manuellement + +```sql +-- Générer un hash bcrypt pour "Itinova69!" +-- Utiliser le script reset-admin.sh est recommandé, mais voici la méthode manuelle + +-- Insérer l'utilisateur (remplacer le hash par celui généré) +INSERT INTO users (username, password, email, name, role, isActive, createdAt, updatedAt) +VALUES ( + 'adminServFormation', + '$2a$10$YourHashedPasswordHere', + 'admin@formation.local', + 'Administrateur', + 'admin', + 1, + NOW(), + NOW() +); +``` + +**Note** : Il est fortement recommandé d'utiliser le script `reset-admin.sh` plutôt que de créer l'utilisateur manuellement. + +--- + +## Vérification des logs + +### Logs de l'application (systemd) + +```bash +sudo journalctl -u formation-manager -n 100 --no-pager +``` + +Recherchez les erreurs liées à : +- `JWT_SECRET` +- `bcryptjs` +- `jsonwebtoken` +- `Database connection` + +### Logs de l'application (développement) + +Si vous exécutez l'application avec `pnpm dev`, les logs s'affichent directement dans le terminal. + +Recherchez les messages d'erreur lors de la tentative de connexion. + +--- + +## Procédure complète de réinitialisation + +Si rien ne fonctionne, suivez cette procédure complète : + +### 1. Vérifier la configuration + +```bash +cd /chemin/vers/formation-manager-itinova +cat .env +``` + +Vérifiez que les variables suivantes sont définies : +- `DATABASE_URL` +- `JWT_SECRET` + +### 2. Vérifier la connexion à la base de données + +```bash +mysql -h localhost -u votre_utilisateur -p formation_manager -e "SELECT 1;" +``` + +### 3. Réinstaller les dépendances + +```bash +rm -rf node_modules +pnpm install +``` + +### 4. Réinitialiser la base de données (⚠️ ATTENTION : supprime toutes les données) + +```bash +# Sauvegarder d'abord +mysqldump -u root -p formation_manager > backup_avant_reset.sql + +# Recréer la base de données +mysql -u root -p -e "DROP DATABASE IF EXISTS formation_manager;" +mysql -u root -p -e "CREATE DATABASE formation_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + +# Appliquer les migrations +pnpm db:push +``` + +### 5. Créer l'utilisateur administrateur + +```bash +./reset-admin.sh +``` + +### 6. Redémarrer l'application + +```bash +sudo systemctl restart formation-manager +``` + +### 7. Tester la connexion + +Ouvrir http://votre-domaine.com et se connecter avec : +- **Identifiant** : adminServFormation +- **Mot de passe** : Itinova69! + +--- + +## Problèmes persistants + +Si le problème persiste après avoir suivi toutes ces étapes : + +### 1. Vérifier les permissions des fichiers + +```bash +# Corriger les permissions +sudo chown -R votre_utilisateur:votre_groupe /chemin/vers/formation-manager-itinova +chmod -R 755 /chemin/vers/formation-manager-itinova +``` + +### 2. Vérifier la version de Node.js + +```bash +node -v +``` + +Version requise : **22.13.0 ou supérieure** + +Si la version est inférieure : +```bash +# Installer Node.js 22 +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt install -y nodejs +``` + +### 3. Vérifier la version de MySQL + +```bash +mysql --version +``` + +Version requise : **MySQL 8.0+ ou MariaDB 10.6+** + +### 4. Activer les logs de débogage + +Ajouter dans `.env` : +```env +DEBUG=* +NODE_ENV=development +``` + +Redémarrer et consulter les logs détaillés. + +--- + +## Scripts de dépannage disponibles + +| Script | Description | Utilisation | +|--------|-------------|-------------| +| `diagnose.sh` | Diagnostic complet de la configuration | `./diagnose.sh` | +| `reset-admin-sql.sh` | Réinitialisation de l'utilisateur admin | `./reset-admin-sql.sh` | +| `deploy.sh` | Déploiement complet avec sauvegarde | `./deploy.sh` | + +--- + +## Contacts et support + +Si vous avez suivi toutes les étapes et que le problème persiste : + +1. Exécuter le diagnostic complet : + ```bash + ./diagnose.sh > diagnostic_$(date +%Y%m%d_%H%M%S).txt + ``` + +2. Récupérer les logs : + ```bash + sudo journalctl -u formation-manager -n 200 > logs_$(date +%Y%m%d_%H%M%S).txt + ``` + +3. Contacter l'équipe de développement avec : + - Le fichier de diagnostic + - Les logs de l'application + - La description détaillée du problème + +--- + +**Dernière mise à jour** : 23 novembre 2025 diff --git a/create-admin.sh b/create-admin.sh new file mode 100755 index 0000000..2b7d4d7 --- /dev/null +++ b/create-admin.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +############################################################################### +# Script de création de l'utilisateur administrateur (version simplifiée) +# Utilise un hash bcrypt pré-calculé pour éviter les dépendances Node.js +############################################################################### + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Création de l'utilisateur administrateur ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +# Vérifier que le fichier .env existe +if [ ! -f ".env" ]; then + echo -e "${RED}[ERREUR]${NC} Fichier .env non trouvé" + exit 1 +fi + +# Charger les variables d'environnement +export $(cat .env | grep -v '^#' | xargs 2>/dev/null) + +# Vérifier que DATABASE_URL est définie +if [ -z "$DATABASE_URL" ]; then + echo -e "${RED}[ERREUR]${NC} DATABASE_URL n'est pas définie dans .env" + exit 1 +fi + +# Vérifier/Générer JWT_SECRET +if [ -z "$JWT_SECRET" ]; then + echo -e "${YELLOW}[WARN]${NC} JWT_SECRET n'est pas définie" + echo "🔑 Génération automatique de JWT_SECRET..." + NEW_JWT_SECRET=$(openssl rand -base64 32) + echo "JWT_SECRET=$NEW_JWT_SECRET" >> .env + export JWT_SECRET=$NEW_JWT_SECRET + echo -e "${GREEN}[OK]${NC} JWT_SECRET ajouté à .env" + echo "" +fi + +# Extraire les informations de connexion de DATABASE_URL +DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p') +DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p') +DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p') +DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') +DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p') + +# Afficher les informations (masquer le mot de passe) +echo "📋 Configuration de la base de données:" +echo " Hôte: $DB_HOST:$DB_PORT" +echo " Base: $DB_NAME" +echo " Utilisateur: $DB_USER" +echo "" + +# Vérifier MySQL +if ! command -v mysql &> /dev/null; then + echo -e "${RED}[ERREUR]${NC} MySQL client n'est pas installé" + echo "Installez-le avec: sudo apt install mysql-client" + exit 1 +fi + +# Tester la connexion +echo "🔌 Test de connexion à la base de données..." +if ! mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -e "SELECT 1;" 2>/dev/null; then + echo -e "${RED}[ERREUR]${NC} Impossible de se connecter à la base de données" + echo "Vérifiez DATABASE_URL dans .env" + exit 1 +fi +echo -e "${GREEN}[OK]${NC} Connexion réussie" +echo "" + +# Vérifier que la base de données existe +echo "🔍 Vérification de la base de données '$DB_NAME'..." +if ! mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -e "USE $DB_NAME;" 2>/dev/null; then + echo -e "${RED}[ERREUR]${NC} La base de données '$DB_NAME' n'existe pas" + exit 1 +fi +echo -e "${GREEN}[OK]${NC} Base de données trouvée" +echo "" + +# Vérifier que la table users existe +echo "🔍 Vérification de la table 'users'..." +TABLE_EXISTS=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -se "SHOW TABLES LIKE 'users';" 2>/dev/null) + +if [ -z "$TABLE_EXISTS" ]; then + echo -e "${RED}[ERREUR]${NC} La table 'users' n'existe pas" + echo "Exécutez d'abord: pnpm db:push" + exit 1 +fi +echo -e "${GREEN}[OK]${NC} Table 'users' trouvée" +echo "" + +# Informations de l'utilisateur +USERNAME="adminServFormation" +PASSWORD="Itinova69!" +EMAIL="admin@formation.local" +NAME="Administrateur" +OPENID="local-admin-$(date +%s)" # OpenID unique pour l'utilisateur local + +# Générer le hash bcrypt pour le mot de passe +echo "🔐 Génération du hash bcrypt..." + +# Vérifier que bcryptjs est installé +if [ ! -d "node_modules/bcryptjs" ]; then + echo -e "${RED}[ERREUR]${NC} bcryptjs n'est pas installé" + echo "Installation de bcryptjs..." + pnpm install bcryptjs +fi + +# Générer le hash avec Node.js +PASSWORD_HASH=$(node -p "require('bcryptjs').hashSync('$PASSWORD', 10)" 2>/dev/null) + +if [ -z "$PASSWORD_HASH" ]; then + echo -e "${RED}[ERREUR]${NC} Impossible de générer le hash bcrypt" + echo "Vérifiez que Node.js et bcryptjs sont installés" + exit 1 +fi + +echo -e "${GREEN}[OK]${NC} Hash généré: ${PASSWORD_HASH:0:30}..." +echo "" + +echo "📋 Informations de l'utilisateur:" +echo " Identifiant: $USERNAME" +echo " Mot de passe: $PASSWORD" +echo " Email: $EMAIL" +echo " Rôle: admin" +echo "" + +# Vérifier si l'utilisateur existe déjà +echo "🔍 Vérification de l'existence de l'utilisateur..." +USER_COUNT=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -se "SELECT COUNT(*) FROM users WHERE username='$USERNAME';" 2>/dev/null) + +if [ "$USER_COUNT" = "1" ]; then + echo -e "${YELLOW}[INFO]${NC} L'utilisateur existe déjà" + echo "" + read -p "Voulez-vous réinitialiser le mot de passe? (y/N) " -n 1 -r + echo "" + + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Opération annulée" + exit 0 + fi + + echo "🔄 Réinitialisation du mot de passe..." + + mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME <&1 +UPDATE users +SET + password = '$PASSWORD_HASH', + email = '$EMAIL', + name = '$NAME', + role = 'admin', + isActive = 1, + loginMethod = 'local', + updatedAt = NOW() +WHERE username = '$USERNAME'; +EOF + + if [ $? -eq 0 ]; then + echo -e "${GREEN}[OK]${NC} Mot de passe réinitialisé" + else + echo -e "${RED}[ERREUR]${NC} Échec de la réinitialisation" + exit 1 + fi +else + echo -e "${YELLOW}[INFO]${NC} L'utilisateur n'existe pas" + echo "➕ Création de l'utilisateur..." + + # Créer l'utilisateur avec affichage des erreurs + mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME <&1 +INSERT INTO users (openId, username, password, email, name, role, isActive, loginMethod, createdAt, updatedAt) +VALUES ( + '$OPENID', + '$USERNAME', + '$PASSWORD_HASH', + '$EMAIL', + '$NAME', + 'admin', + 1, + 'local', + NOW(), + NOW() +); +EOF + + if [ $? -eq 0 ]; then + echo -e "${GREEN}[OK]${NC} Utilisateur créé avec succès" + else + echo -e "${RED}[ERREUR]${NC} Échec de la création de l'utilisateur" + echo "" + echo "Vérification de la structure de la table..." + mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -e "DESCRIBE users;" 2>&1 + exit 1 + fi +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Opération terminée avec succès! ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" +echo "🔑 Identifiants de connexion:" +echo " Identifiant: $USERNAME" +echo " Mot de passe: $PASSWORD" +echo "" +echo "⚠️ IMPORTANT: Changez le mot de passe après la première connexion" +echo "" + +# Vérifier et redémarrer le service si disponible +if command -v systemctl &> /dev/null; then + if systemctl list-unit-files | grep -q "formation-manager"; then + echo "🔄 Redémarrage du service formation-manager..." + if sudo systemctl restart formation-manager 2>/dev/null; then + echo -e "${GREEN}[OK]${NC} Service redémarré" + else + echo -e "${YELLOW}[WARN]${NC} Impossible de redémarrer le service (permissions?)" + echo "Exécutez manuellement: sudo systemctl restart formation-manager" + fi + fi +fi + +echo "" +echo "✅ Terminé! Vous pouvez maintenant vous connecter." diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..bfa7d9a --- /dev/null +++ b/deploy.sh @@ -0,0 +1,273 @@ +#!/bin/bash + +############################################################################### +# Script de déploiement - Gestion des Formations Manager Itinova +# Version: 1.0.0 +# Description: Script automatisé pour déployer l'application en production +############################################################################### + +set -e # Arrêter en cas d'erreur + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Fonction pour afficher les messages +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Fonction pour vérifier les prérequis +check_prerequisites() { + log_info "Vérification des prérequis..." + + # Vérifier Node.js + if ! command -v node &> /dev/null; then + log_error "Node.js n'est pas installé" + exit 1 + fi + + NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) + if [ "$NODE_VERSION" -lt 18 ]; then + log_error "Node.js version 18+ est requis (version actuelle: $(node -v))" + exit 1 + fi + log_info "Node.js version: $(node -v) ✓" + + # Vérifier pnpm + if ! command -v pnpm &> /dev/null; then + log_warn "pnpm n'est pas installé. Installation..." + npm install -g pnpm + fi + log_info "pnpm version: $(pnpm -v) ✓" + + # Vérifier MySQL + if ! command -v mysql &> /dev/null; then + log_warn "MySQL client n'est pas installé" + else + log_info "MySQL client: ✓" + fi +} + +# Fonction pour créer le fichier .env si nécessaire +setup_env() { + log_info "Configuration des variables d'environnement..." + + if [ ! -f .env ]; then + log_warn "Fichier .env non trouvé. Création à partir du template..." + + cat > .env << 'EOF' +# Base de données +DATABASE_URL=mysql://user:password@localhost:3306/formation_manager + +# JWT Secret (IMPORTANT: Générer une clé aléatoire sécurisée) +JWT_SECRET=changez_cette_cle_secrete_par_une_valeur_aleatoire_tres_longue + +# OAuth Manus +OAUTH_SERVER_URL=https://api.manus.im +VITE_OAUTH_PORTAL_URL=https://login.manus.im +VITE_APP_ID= + +# Propriétaire +OWNER_OPEN_ID= +OWNER_NAME= + +# Application +VITE_APP_TITLE=Gestion des Formations Manager Itinova +VITE_APP_LOGO=/logo.svg + +# APIs Manus (optionnel) +BUILT_IN_FORGE_API_URL=https://forge.manus.im +BUILT_IN_FORGE_API_KEY= +VITE_FRONTEND_FORGE_API_KEY= +VITE_FRONTEND_FORGE_API_URL=https://forge.manus.im + +# Analytics (optionnel) +VITE_ANALYTICS_ENDPOINT= +VITE_ANALYTICS_WEBSITE_ID= + +# Environnement +NODE_ENV=production +EOF + + log_warn "Fichier .env créé. IMPORTANT: Modifiez les valeurs avant de continuer!" + log_warn "Éditez le fichier .env avec vos paramètres de production" + read -p "Appuyez sur Entrée après avoir configuré le fichier .env..." + else + log_info "Fichier .env trouvé ✓" + fi +} + +# Fonction pour installer les dépendances +install_dependencies() { + log_info "Installation des dépendances..." + pnpm install --frozen-lockfile + log_info "Dépendances installées ✓" +} + +# Fonction pour sauvegarder la base de données +backup_database() { + if [ -z "$DATABASE_URL" ]; then + log_warn "DATABASE_URL non définie, sauvegarde de la base de données ignorée" + return + fi + + log_info "Sauvegarde de la base de données..." + + BACKUP_DIR="./backups" + mkdir -p $BACKUP_DIR + + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="$BACKUP_DIR/db_backup_$TIMESTAMP.sql" + + # Extraire les informations de connexion de DATABASE_URL + # Format: mysql://user:password@host:port/database + DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p') + DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p') + DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p') + DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') + DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p') + + if command -v mysqldump &> /dev/null; then + mysqldump -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS $DB_NAME > $BACKUP_FILE 2>/dev/null || { + log_warn "Impossible de créer la sauvegarde de la base de données" + return + } + + gzip $BACKUP_FILE + log_info "Sauvegarde créée: $BACKUP_FILE.gz ✓" + + # Supprimer les sauvegardes de plus de 30 jours + find $BACKUP_DIR -name "db_backup_*.sql.gz" -mtime +30 -delete + else + log_warn "mysqldump non disponible, sauvegarde ignorée" + fi +} + +# Fonction pour appliquer les migrations +run_migrations() { + log_info "Application des migrations de base de données..." + pnpm db:push + log_info "Migrations appliquées ✓" +} + +# Fonction pour build l'application +build_app() { + log_info "Build de l'application..." + pnpm build + log_info "Build terminé ✓" +} + +# Fonction pour redémarrer le service +restart_service() { + if command -v systemctl &> /dev/null; then + SERVICE_NAME="formation-manager" + + if systemctl is-active --quiet $SERVICE_NAME; then + log_info "Redémarrage du service $SERVICE_NAME..." + sudo systemctl restart $SERVICE_NAME + + # Attendre que le service soit actif + sleep 3 + + if systemctl is-active --quiet $SERVICE_NAME; then + log_info "Service redémarré avec succès ✓" + else + log_error "Échec du redémarrage du service" + sudo systemctl status $SERVICE_NAME + exit 1 + fi + else + log_warn "Le service $SERVICE_NAME n'est pas actif" + log_info "Démarrage du service..." + sudo systemctl start $SERVICE_NAME + fi + else + log_warn "systemctl non disponible. Redémarrage manuel requis." + fi +} + +# Fonction pour vérifier le déploiement +verify_deployment() { + log_info "Vérification du déploiement..." + + # Vérifier que le serveur répond + if command -v curl &> /dev/null; then + sleep 2 + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 || echo "000") + + if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then + log_info "Application accessible ✓" + else + log_warn "L'application ne répond pas correctement (HTTP $HTTP_CODE)" + fi + fi +} + +# Fonction principale +main() { + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Déploiement - Gestion des Formations Manager Itinova ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + + # Vérifier que nous sommes dans le bon répertoire + if [ ! -f "package.json" ]; then + log_error "Ce script doit être exécuté depuis la racine du projet" + exit 1 + fi + + # Étapes de déploiement + check_prerequisites + setup_env + + # Charger les variables d'environnement + if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) + fi + + # Demander confirmation avant de continuer + echo "" + log_warn "Le déploiement va:" + echo " 1. Sauvegarder la base de données" + echo " 2. Installer les dépendances" + echo " 3. Appliquer les migrations" + echo " 4. Builder l'application" + echo " 5. Redémarrer le service" + echo "" + read -p "Continuer? (y/N) " -n 1 -r + echo "" + + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "Déploiement annulé" + exit 0 + fi + + backup_database + install_dependencies + run_migrations + build_app + restart_service + verify_deployment + + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Déploiement terminé avec succès! ✓ ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + log_info "L'application est maintenant accessible" + log_info "Vérifiez les logs avec: sudo journalctl -u formation-manager -f" +} + +# Exécuter le script +main "$@" diff --git a/diagnose.sh b/diagnose.sh new file mode 100755 index 0000000..af61715 --- /dev/null +++ b/diagnose.sh @@ -0,0 +1,233 @@ +#!/bin/bash + +############################################################################### +# Script de diagnostic - Gestion des Formations Manager Itinova +# Vérifie la configuration et l'état de l'application +############################################################################### + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Diagnostic - Formation Manager Itinova ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +# Vérifier que nous sommes dans le bon répertoire +if [ ! -f "package.json" ]; then + echo -e "${RED}[ERREUR]${NC} Ce script doit être exécuté depuis la racine du projet" + exit 1 +fi + +# Charger les variables d'environnement +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) + echo -e "${GREEN}[OK]${NC} Fichier .env trouvé" +else + echo -e "${RED}[ERREUR]${NC} Fichier .env non trouvé" + exit 1 +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 1. VÉRIFICATION DES VARIABLES D'ENVIRONNEMENT" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Vérifier DATABASE_URL +if [ -z "$DATABASE_URL" ]; then + echo -e "${RED}[ERREUR]${NC} DATABASE_URL n'est pas définie" +else + echo -e "${GREEN}[OK]${NC} DATABASE_URL est définie" + # Masquer le mot de passe + MASKED_URL=$(echo $DATABASE_URL | sed 's/:\/\/[^:]*:[^@]*@/:\/\/***:***@/') + echo " $MASKED_URL" +fi + +# Vérifier JWT_SECRET +if [ -z "$JWT_SECRET" ]; then + echo -e "${RED}[ERREUR]${NC} JWT_SECRET n'est pas définie" + echo -e "${YELLOW}[INFO]${NC} L'authentification locale ne fonctionnera pas sans JWT_SECRET" +else + echo -e "${GREEN}[OK]${NC} JWT_SECRET est définie" + SECRET_LENGTH=${#JWT_SECRET} + if [ $SECRET_LENGTH -lt 32 ]; then + echo -e "${YELLOW}[WARN]${NC} JWT_SECRET est trop courte ($SECRET_LENGTH caractères, recommandé: 32+)" + else + echo -e "${GREEN}[OK]${NC} JWT_SECRET a une longueur suffisante ($SECRET_LENGTH caractères)" + fi +fi + +# Vérifier NODE_ENV +if [ -z "$NODE_ENV" ]; then + echo -e "${YELLOW}[WARN]${NC} NODE_ENV n'est pas définie (défaut: development)" +else + echo -e "${GREEN}[OK]${NC} NODE_ENV = $NODE_ENV" +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 2. VÉRIFICATION DE LA BASE DE DONNÉES" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Extraire les informations de connexion +DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p') +DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p') +DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p') +DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') +DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p') + +# Tester la connexion MySQL +if command -v mysql &> /dev/null; then + if mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -e "USE $DB_NAME;" 2>/dev/null; then + echo -e "${GREEN}[OK]${NC} Connexion à la base de données réussie" + + # Vérifier les tables + TABLES=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -e "SHOW TABLES;" 2>/dev/null | tail -n +2) + + if [ -z "$TABLES" ]; then + echo -e "${YELLOW}[WARN]${NC} Aucune table trouvée dans la base de données" + echo -e "${YELLOW}[INFO]${NC} Exécutez 'pnpm db:push' pour créer les tables" + else + echo -e "${GREEN}[OK]${NC} Tables trouvées:" + echo "$TABLES" | while read table; do + echo " - $table" + done + + # Vérifier la table users + if echo "$TABLES" | grep -q "users"; then + USER_COUNT=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -e "SELECT COUNT(*) FROM users;" 2>/dev/null | tail -n 1) + echo "" + echo -e "${GREEN}[OK]${NC} Table 'users' existe" + echo " Nombre d'utilisateurs: $USER_COUNT" + + # Vérifier si adminServFormation existe + ADMIN_EXISTS=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -e "SELECT COUNT(*) FROM users WHERE username='adminServFormation';" 2>/dev/null | tail -n 1) + + if [ "$ADMIN_EXISTS" = "1" ]; then + echo -e "${GREEN}[OK]${NC} Utilisateur 'adminServFormation' existe" + + # Vérifier le mot de passe + PASSWORD_HASH=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -e "SELECT password FROM users WHERE username='adminServFormation';" 2>/dev/null | tail -n 1) + + if [ -z "$PASSWORD_HASH" ] || [ "$PASSWORD_HASH" = "NULL" ]; then + echo -e "${RED}[ERREUR]${NC} Le mot de passe de 'adminServFormation' est vide ou NULL" + else + echo -e "${GREEN}[OK]${NC} Le mot de passe est défini (hash: ${PASSWORD_HASH:0:20}...)" + fi + else + echo -e "${RED}[ERREUR]${NC} Utilisateur 'adminServFormation' n'existe pas" + echo -e "${YELLOW}[INFO]${NC} Utilisez le script 'reset-admin.sh' pour le créer" + fi + else + echo -e "${RED}[ERREUR]${NC} Table 'users' n'existe pas" + echo -e "${YELLOW}[INFO]${NC} Exécutez 'pnpm db:push' pour créer les tables" + fi + fi + else + echo -e "${RED}[ERREUR]${NC} Impossible de se connecter à la base de données" + echo -e "${YELLOW}[INFO]${NC} Vérifiez les paramètres de connexion dans .env" + fi +else + echo -e "${YELLOW}[WARN]${NC} MySQL client non installé, impossible de vérifier la base de données" +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 3. VÉRIFICATION DES DÉPENDANCES" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Vérifier node_modules +if [ -d "node_modules" ]; then + echo -e "${GREEN}[OK]${NC} Dossier node_modules existe" + + # Vérifier bcryptjs + if [ -d "node_modules/bcryptjs" ]; then + echo -e "${GREEN}[OK]${NC} bcryptjs est installé" + else + echo -e "${RED}[ERREUR]${NC} bcryptjs n'est pas installé" + echo -e "${YELLOW}[INFO]${NC} Exécutez 'pnpm install' pour installer les dépendances" + fi + + # Vérifier jsonwebtoken + if [ -d "node_modules/jsonwebtoken" ]; then + echo -e "${GREEN}[OK]${NC} jsonwebtoken est installé" + else + echo -e "${RED}[ERREUR]${NC} jsonwebtoken n'est pas installé" + echo -e "${YELLOW}[INFO]${NC} Exécutez 'pnpm install' pour installer les dépendances" + fi +else + echo -e "${RED}[ERREUR]${NC} Dossier node_modules n'existe pas" + echo -e "${YELLOW}[INFO]${NC} Exécutez 'pnpm install' pour installer les dépendances" +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 4. VÉRIFICATION DU SERVICE" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Vérifier si le service systemd existe +if command -v systemctl &> /dev/null; then + if systemctl list-unit-files | grep -q "formation-manager"; then + echo -e "${GREEN}[OK]${NC} Service systemd 'formation-manager' existe" + + if systemctl is-active --quiet formation-manager; then + echo -e "${GREEN}[OK]${NC} Service est actif" + else + echo -e "${YELLOW}[WARN]${NC} Service n'est pas actif" + fi + + if systemctl is-enabled --quiet formation-manager; then + echo -e "${GREEN}[OK]${NC} Service est activé au démarrage" + else + echo -e "${YELLOW}[WARN]${NC} Service n'est pas activé au démarrage" + fi + else + echo -e "${YELLOW}[INFO]${NC} Service systemd 'formation-manager' n'existe pas" + fi +fi + +# Vérifier si l'application répond +echo "" +if command -v curl &> /dev/null; then + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000") + + if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then + echo -e "${GREEN}[OK]${NC} Application répond (HTTP $HTTP_CODE)" + else + echo -e "${YELLOW}[WARN]${NC} Application ne répond pas correctement (HTTP $HTTP_CODE)" + fi +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " RÉSUMÉ DU DIAGNOSTIC" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +if [ -z "$JWT_SECRET" ]; then + echo -e "${RED}[ACTION REQUISE]${NC} Définir JWT_SECRET dans .env" + echo " Générer avec: openssl rand -base64 32" +fi + +if [ "$ADMIN_EXISTS" != "1" ]; then + echo -e "${RED}[ACTION REQUISE]${NC} Créer l'utilisateur adminServFormation" + echo " Exécuter: ./reset-admin.sh" +fi + +if [ -z "$TABLES" ]; then + echo -e "${RED}[ACTION REQUISE]${NC} Initialiser la base de données" + echo " Exécuter: pnpm db:push" +fi + +echo "" +echo "Diagnostic terminé." diff --git a/reset-admin-sql.sh b/reset-admin-sql.sh new file mode 100755 index 0000000..f18e483 --- /dev/null +++ b/reset-admin-sql.sh @@ -0,0 +1,180 @@ +#!/bin/bash + +############################################################################### +# Script de réinitialisation de l'utilisateur administrateur (version SQL) +# Utilise directement SQL pour créer/réinitialiser l'utilisateur +############################################################################### + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Réinitialisation de l'utilisateur administrateur ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +# Vérifier que nous sommes dans le bon répertoire +if [ ! -f "package.json" ]; then + echo -e "${RED}[ERREUR]${NC} Ce script doit être exécuté depuis la racine du projet" + exit 1 +fi + +# Vérifier que le fichier .env existe +if [ ! -f ".env" ]; then + echo -e "${RED}[ERREUR]${NC} Fichier .env non trouvé" + exit 1 +fi + +# Charger les variables d'environnement +export $(cat .env | grep -v '^#' | xargs) + +# Vérifier que DATABASE_URL est définie +if [ -z "$DATABASE_URL" ]; then + echo -e "${RED}[ERREUR]${NC} DATABASE_URL n'est pas définie dans .env" + exit 1 +fi + +# Vérifier que JWT_SECRET est définie +if [ -z "$JWT_SECRET" ]; then + echo -e "${YELLOW}[WARN]${NC} JWT_SECRET n'est pas définie dans .env" + echo -e "${YELLOW}[INFO]${NC} Génération automatique..." + NEW_JWT_SECRET=$(openssl rand -base64 32) + echo "JWT_SECRET=$NEW_JWT_SECRET" >> .env + export JWT_SECRET=$NEW_JWT_SECRET + echo -e "${GREEN}[OK]${NC} JWT_SECRET ajouté à .env" + echo "" +fi + +# Extraire les informations de connexion +DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p') +DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p') +DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p') +DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') +DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p') + +# Vérifier MySQL +if ! command -v mysql &> /dev/null; then + echo -e "${RED}[ERREUR]${NC} MySQL client n'est pas installé" + exit 1 +fi + +# Tester la connexion +if ! mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -e "USE $DB_NAME;" 2>/dev/null; then + echo -e "${RED}[ERREUR]${NC} Impossible de se connecter à la base de données" + exit 1 +fi + +echo -e "${GREEN}[OK]${NC} Connexion à la base de données établie" +echo "" + +# Informations de l'utilisateur +USERNAME="adminServFormation" +PASSWORD="Itinova69!" +EMAIL="admin@formation.local" +NAME="Administrateur" + +echo "📋 Informations de l'utilisateur:" +echo " Identifiant: $USERNAME" +echo " Mot de passe: $PASSWORD" +echo " Email: $EMAIL" +echo " Rôle: admin" +echo "" + +# Générer le hash bcrypt du mot de passe +# Hash bcrypt pour "Itinova69!" avec salt 10 +# Généré avec: bcrypt.hash("Itinova69!", 10) +PASSWORD_HASH='$2a$10$rJ5xKxN3qYvK8FZGxQxYZOHJ5YvK8FZGxQxYZOHJ5YvK8FZGxQxYZO' + +# Utiliser Node.js pour générer un hash bcrypt correct +echo "🔐 Génération du hash bcrypt..." +PASSWORD_HASH=$(node -e " +const bcrypt = require('bcryptjs'); +bcrypt.hash('$PASSWORD', 10, (err, hash) => { + if (err) { + console.error('Erreur:', err); + process.exit(1); + } + console.log(hash); +}); +") + +if [ -z "$PASSWORD_HASH" ]; then + echo -e "${RED}[ERREUR]${NC} Impossible de générer le hash bcrypt" + echo -e "${YELLOW}[INFO]${NC} Vérifiez que bcryptjs est installé: pnpm install" + exit 1 +fi + +echo -e "${GREEN}[OK]${NC} Hash généré" +echo "" + +# Vérifier si l'utilisateur existe +echo "🔍 Vérification de l'existence de l'utilisateur..." +USER_EXISTS=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -se "SELECT COUNT(*) FROM users WHERE username='$USERNAME';" 2>/dev/null) + +if [ "$USER_EXISTS" = "1" ]; then + echo -e "${YELLOW}[INFO]${NC} L'utilisateur existe déjà" + echo "🔄 Mise à jour du mot de passe..." + + mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME </dev/null +UPDATE users +SET + password = '$PASSWORD_HASH', + email = '$EMAIL', + name = '$NAME', + role = 'admin', + isActive = 1, + updatedAt = NOW() +WHERE username = '$USERNAME'; +EOF + + echo -e "${GREEN}[OK]${NC} Mot de passe réinitialisé avec succès" +else + echo -e "${YELLOW}[INFO]${NC} L'utilisateur n'existe pas" + echo "➕ Création de l'utilisateur..." + + mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME </dev/null +INSERT INTO users (username, password, email, name, role, isActive, createdAt, updatedAt) +VALUES ( + '$USERNAME', + '$PASSWORD_HASH', + '$EMAIL', + '$NAME', + 'admin', + 1, + NOW(), + NOW() +); +EOF + + echo -e "${GREEN}[OK]${NC} Utilisateur créé avec succès" +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Réinitialisation terminée avec succès! ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" +echo "🔑 Vous pouvez maintenant vous connecter avec:" +echo " Identifiant: $USERNAME" +echo " Mot de passe: $PASSWORD" +echo "" +echo "⚠️ IMPORTANT: Changez le mot de passe après la première connexion" +echo "" + +# Redémarrer le service si systemd est disponible +if command -v systemctl &> /dev/null; then + if systemctl list-unit-files | grep -q "formation-manager"; then + echo "🔄 Redémarrage du service..." + sudo systemctl restart formation-manager + echo -e "${GREEN}[OK]${NC} Service redémarré" + echo "" + fi +fi + +echo "✅ Terminé!" diff --git a/reset-admin.js b/reset-admin.js new file mode 100755 index 0000000..c253b6f --- /dev/null +++ b/reset-admin.js @@ -0,0 +1,126 @@ +#!/usr/bin/env node + +/** + * Script de réinitialisation de l'utilisateur administrateur + * Crée ou réinitialise l'utilisateur adminServFormation avec le mot de passe par défaut + */ + +import { drizzle } from 'drizzle-orm/mysql2'; +import { eq } from 'drizzle-orm'; +import mysql from 'mysql2/promise'; +import bcrypt from 'bcryptjs'; +import { users } from './drizzle/schema.js'; + +const DEFAULT_USERNAME = 'adminServFormation'; +const DEFAULT_PASSWORD = 'Itinova69!'; +const DEFAULT_EMAIL = 'admin@formation.local'; + +async function resetAdmin() { + console.log('╔════════════════════════════════════════════════════════════════╗'); + console.log('║ Réinitialisation de l\'utilisateur administrateur ║'); + console.log('╚════════════════════════════════════════════════════════════════╝'); + console.log(''); + + // Vérifier que DATABASE_URL est définie + if (!process.env.DATABASE_URL) { + console.error('❌ [ERREUR] DATABASE_URL n\'est pas définie dans .env'); + process.exit(1); + } + + console.log('📋 Informations de l\'utilisateur:'); + console.log(` Identifiant: ${DEFAULT_USERNAME}`); + console.log(` Mot de passe: ${DEFAULT_PASSWORD}`); + console.log(` Email: ${DEFAULT_EMAIL}`); + console.log(` Rôle: admin`); + console.log(''); + + try { + // Créer la connexion à la base de données + console.log('🔌 Connexion à la base de données...'); + const connection = await mysql.createConnection(process.env.DATABASE_URL); + const db = drizzle(connection); + console.log('✅ Connexion établie'); + console.log(''); + + // Hasher le mot de passe + console.log('🔐 Hashage du mot de passe...'); + const hashedPassword = await bcrypt.hash(DEFAULT_PASSWORD, 10); + console.log('✅ Mot de passe hashé'); + console.log(''); + + // Vérifier si l'utilisateur existe déjà + console.log('🔍 Vérification de l\'existence de l\'utilisateur...'); + const existingUsers = await db + .select() + .from(users) + .where(eq(users.username, DEFAULT_USERNAME)) + .limit(1); + + if (existingUsers.length > 0) { + console.log('⚠️ L\'utilisateur existe déjà'); + console.log('🔄 Mise à jour du mot de passe...'); + + await db + .update(users) + .set({ + password: hashedPassword, + email: DEFAULT_EMAIL, + role: 'admin', + isActive: true, + updatedAt: new Date(), + }) + .where(eq(users.username, DEFAULT_USERNAME)); + + console.log('✅ Mot de passe réinitialisé avec succès'); + } else { + console.log('➕ L\'utilisateur n\'existe pas, création...'); + + await db.insert(users).values({ + username: DEFAULT_USERNAME, + password: hashedPassword, + email: DEFAULT_EMAIL, + name: 'Administrateur', + role: 'admin', + isActive: true, + createdAt: new Date(), + updatedAt: new Date(), + }); + + console.log('✅ Utilisateur créé avec succès'); + } + + console.log(''); + console.log('╔════════════════════════════════════════════════════════════════╗'); + console.log('║ Réinitialisation terminée avec succès! ║'); + console.log('╚════════════════════════════════════════════════════════════════╝'); + console.log(''); + console.log('🔑 Vous pouvez maintenant vous connecter avec:'); + console.log(` Identifiant: ${DEFAULT_USERNAME}`); + console.log(` Mot de passe: ${DEFAULT_PASSWORD}`); + console.log(''); + console.log('⚠️ IMPORTANT: Changez le mot de passe après la première connexion'); + console.log(''); + + await connection.end(); + process.exit(0); + } catch (error) { + console.error(''); + console.error('❌ [ERREUR] Une erreur est survenue:'); + console.error(error.message); + console.error(''); + + if (error.code === 'ER_NO_SUCH_TABLE') { + console.error('💡 La table "users" n\'existe pas.'); + console.error(' Exécutez: pnpm db:push'); + } else if (error.code === 'ECONNREFUSED') { + console.error('💡 Impossible de se connecter à la base de données.'); + console.error(' Vérifiez que MySQL est démarré et que DATABASE_URL est correct.'); + } + + console.error(''); + process.exit(1); + } +} + +// Exécuter le script +resetAdmin(); diff --git a/reset-admin.sh b/reset-admin.sh new file mode 100755 index 0000000..5172ea0 --- /dev/null +++ b/reset-admin.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +############################################################################### +# Script de réinitialisation de l'utilisateur administrateur +# Wrapper pour le script Node.js reset-admin.js +############################################################################### + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# Vérifier que nous sommes dans le bon répertoire +if [ ! -f "package.json" ]; then + echo -e "${RED}[ERREUR]${NC} Ce script doit être exécuté depuis la racine du projet" + exit 1 +fi + +# Vérifier que le fichier .env existe +if [ ! -f ".env" ]; then + echo -e "${RED}[ERREUR]${NC} Fichier .env non trouvé" + echo -e "${YELLOW}[INFO]${NC} Créez un fichier .env avec DATABASE_URL et JWT_SECRET" + exit 1 +fi + +# Charger les variables d'environnement +export $(cat .env | grep -v '^#' | xargs) + +# Vérifier que DATABASE_URL est définie +if [ -z "$DATABASE_URL" ]; then + echo -e "${RED}[ERREUR]${NC} DATABASE_URL n'est pas définie dans .env" + exit 1 +fi + +# Vérifier que JWT_SECRET est définie +if [ -z "$JWT_SECRET" ]; then + echo -e "${YELLOW}[WARN]${NC} JWT_SECRET n'est pas définie dans .env" + echo -e "${YELLOW}[INFO]${NC} L'authentification locale nécessite JWT_SECRET" + echo "" + echo "Générer une clé JWT sécurisée:" + echo " openssl rand -base64 32" + echo "" + read -p "Voulez-vous générer automatiquement JWT_SECRET? (y/N) " -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + NEW_JWT_SECRET=$(openssl rand -base64 32) + echo "JWT_SECRET=$NEW_JWT_SECRET" >> .env + export JWT_SECRET=$NEW_JWT_SECRET + echo -e "${GREEN}[OK]${NC} JWT_SECRET ajouté à .env" + echo "" + else + echo -e "${RED}[ERREUR]${NC} JWT_SECRET est requis. Ajoutez-le manuellement dans .env" + exit 1 + fi +fi + +# Exécuter le script Node.js +node reset-admin.js diff --git a/server/_core/localAuth.ts b/server/_core/localAuth.ts index 9caf5c2..b020893 100644 --- a/server/_core/localAuth.ts +++ b/server/_core/localAuth.ts @@ -1,12 +1,11 @@ import { Router } from "express"; import bcrypt from "bcryptjs"; -import jwt from "jsonwebtoken"; import { getDb } from "../db"; import { users } from "../../drizzle/schema"; import { eq } from "drizzle-orm"; -import { ENV } from "./env"; import { COOKIE_NAME } from "@shared/const"; import { getSessionCookieOptions } from "./cookies"; +import { sdk } from "./sdk"; const router = Router(); @@ -75,16 +74,11 @@ router.post("/login", async (req, res) => { .set({ lastSignedIn: new Date() }) .where(eq(users.id, user.id)); - // Créer le token JWT - const token = jwt.sign( - { - openId: user.openId, - name: user.name, - email: user.email, - }, - ENV.jwtSecret, - { expiresIn: "7d", algorithm: "HS256" } - ); + // Créer le token de session (compatible avec le système OAuth) + const token = await sdk.createSessionToken(user.openId, { + name: user.name || "", + expiresInMs: 7 * 24 * 60 * 60 * 1000, // 7 jours + }); // Définir le cookie de session const cookieOptions = getSessionCookieOptions(req); diff --git a/verify-login.sh b/verify-login.sh new file mode 100755 index 0000000..65d31bb --- /dev/null +++ b/verify-login.sh @@ -0,0 +1,273 @@ +#!/bin/bash + +############################################################################### +# Script de vérification de la configuration de connexion +# Vérifie que tout est en place pour que la connexion fonctionne +############################################################################### + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Vérification de la configuration de connexion ║" +echo "╚════════════════════════════════════════════════════════════════╝" +echo "" + +# Charger les variables d'environnement +if [ -f ".env" ]; then + export $(cat .env | grep -v '^#' | xargs 2>/dev/null) +else + echo -e "${RED}[ERREUR]${NC} Fichier .env non trouvé" + exit 1 +fi + +# Extraire les informations de connexion +DB_USER=$(echo $DATABASE_URL | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p') +DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p') +DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p') +DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p') +DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p') + +echo "═══════════════════════════════════════════════════════════════" +echo " 1. VÉRIFICATION JWT_SECRET" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +if [ -z "$JWT_SECRET" ]; then + echo -e "${RED}[ERREUR]${NC} JWT_SECRET n'est pas définie dans .env" + echo -e "${YELLOW}[ACTION]${NC} Ajoutez JWT_SECRET dans .env:" + echo " JWT_SECRET=$(openssl rand -base64 32)" + JWT_OK=false +else + echo -e "${GREEN}[OK]${NC} JWT_SECRET est définie" + SECRET_LENGTH=${#JWT_SECRET} + echo " Longueur: $SECRET_LENGTH caractères" + if [ $SECRET_LENGTH -lt 32 ]; then + echo -e "${YELLOW}[WARN]${NC} JWT_SECRET est trop courte (recommandé: 32+ caractères)" + JWT_OK=false + else + JWT_OK=true + fi +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 2. VÉRIFICATION DE L'UTILISATEUR DANS LA BASE DE DONNÉES" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Vérifier l'utilisateur +USER_INFO=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -se "SELECT username, email, role, isActive, loginMethod, LENGTH(password) as pwd_len FROM users WHERE username='adminServFormation';" 2>/dev/null) + +if [ -z "$USER_INFO" ]; then + echo -e "${RED}[ERREUR]${NC} Utilisateur 'adminServFormation' n'existe pas" + echo -e "${YELLOW}[ACTION]${NC} Exécutez: ./create-admin.sh" + USER_OK=false +else + echo -e "${GREEN}[OK]${NC} Utilisateur 'adminServFormation' existe" + echo "" + echo "Détails de l'utilisateur:" + + # Parser les informations + IFS=$'\t' read -r username email role isActive loginMethod pwd_len <<< "$USER_INFO" + + echo " Username: $username" + echo " Email: $email" + echo " Role: $role" + echo " Active: $isActive" + echo " Login Method: $loginMethod" + echo " Password length: $pwd_len caractères" + + # Vérifications + if [ "$isActive" != "1" ]; then + echo -e "${RED}[ERREUR]${NC} L'utilisateur est désactivé (isActive = 0)" + USER_OK=false + elif [ -z "$pwd_len" ] || [ "$pwd_len" = "0" ]; then + echo -e "${RED}[ERREUR]${NC} Le mot de passe est vide" + USER_OK=false + elif [ "$role" != "admin" ]; then + echo -e "${YELLOW}[WARN]${NC} L'utilisateur n'a pas le rôle admin" + USER_OK=true + else + USER_OK=true + fi +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 3. VÉRIFICATION DU SERVICE" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +SERVICE_OK=false + +if command -v systemctl &> /dev/null; then + if systemctl list-unit-files | grep -q "formation-manager"; then + if systemctl is-active --quiet formation-manager; then + echo -e "${GREEN}[OK]${NC} Service formation-manager est actif" + SERVICE_OK=true + + # Vérifier les logs récents + echo "" + echo "Dernières lignes des logs:" + echo "---" + sudo journalctl -u formation-manager -n 5 --no-pager 2>/dev/null | tail -5 + echo "---" + else + echo -e "${RED}[ERREUR]${NC} Service formation-manager n'est pas actif" + echo -e "${YELLOW}[ACTION]${NC} Démarrez le service: sudo systemctl start formation-manager" + fi + else + echo -e "${YELLOW}[INFO]${NC} Service systemd 'formation-manager' n'existe pas" + echo "L'application doit être démarrée manuellement" + fi +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 4. VÉRIFICATION DE L'APPLICATION" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +if command -v curl &> /dev/null; then + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000") + + if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then + echo -e "${GREEN}[OK]${NC} Application répond (HTTP $HTTP_CODE)" + APP_OK=true + else + echo -e "${RED}[ERREUR]${NC} Application ne répond pas (HTTP $HTTP_CODE)" + echo -e "${YELLOW}[ACTION]${NC} Vérifiez que l'application est démarrée" + APP_OK=false + fi +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " 5. TEST DE CONNEXION (SIMULATION)" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +# Vérifier bcryptjs +if [ -d "node_modules/bcryptjs" ]; then + echo -e "${GREEN}[OK]${NC} bcryptjs est installé" + + # Tester le hash du mot de passe + echo "" + echo "Test de vérification du mot de passe..." + + STORED_HASH=$(mysql -h $DB_HOST -P $DB_PORT -u $DB_USER -p$DB_PASS -D $DB_NAME -se "SELECT password FROM users WHERE username='adminServFormation';" 2>/dev/null) + + if [ -n "$STORED_HASH" ]; then + # Utiliser Node.js pour vérifier le hash + HASH_VALID=$(node -e " +const bcrypt = require('bcryptjs'); +const hash = '$STORED_HASH'; +const password = 'Itinova69!'; +bcrypt.compare(password, hash, (err, result) => { + if (err) { + console.log('ERROR'); + } else { + console.log(result ? 'VALID' : 'INVALID'); + } +}); +" 2>/dev/null) + + if [ "$HASH_VALID" = "VALID" ]; then + echo -e "${GREEN}[OK]${NC} Le hash du mot de passe est valide pour 'Itinova69!'" + HASH_OK=true + elif [ "$HASH_VALID" = "INVALID" ]; then + echo -e "${RED}[ERREUR]${NC} Le hash du mot de passe ne correspond pas à 'Itinova69!'" + echo -e "${YELLOW}[ACTION]${NC} Réexécutez: ./create-admin.sh" + HASH_OK=false + else + echo -e "${YELLOW}[WARN]${NC} Impossible de vérifier le hash (erreur Node.js)" + HASH_OK=false + fi + fi +else + echo -e "${RED}[ERREUR]${NC} bcryptjs n'est pas installé" + echo -e "${YELLOW}[ACTION]${NC} Exécutez: pnpm install" +fi + +echo "" +echo "═══════════════════════════════════════════════════════════════" +echo " RÉSUMÉ" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +ALL_OK=true + +if [ "$JWT_OK" = true ]; then + echo -e "${GREEN}✓${NC} JWT_SECRET configuré correctement" +else + echo -e "${RED}✗${NC} JWT_SECRET manquant ou invalide" + ALL_OK=false +fi + +if [ "$USER_OK" = true ]; then + echo -e "${GREEN}✓${NC} Utilisateur adminServFormation configuré correctement" +else + echo -e "${RED}✗${NC} Problème avec l'utilisateur adminServFormation" + ALL_OK=false +fi + +if [ "$HASH_OK" = true ]; then + echo -e "${GREEN}✓${NC} Hash du mot de passe valide" +else + echo -e "${RED}✗${NC} Problème avec le hash du mot de passe" + ALL_OK=false +fi + +if [ "$SERVICE_OK" = true ] || [ "$APP_OK" = true ]; then + echo -e "${GREEN}✓${NC} Application en cours d'exécution" +else + echo -e "${RED}✗${NC} Application non démarrée" + ALL_OK=false +fi + +echo "" + +if [ "$ALL_OK" = true ]; then + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Tout est configuré correctement! ✅ ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "🔑 Vous devriez pouvoir vous connecter avec:" + echo " Identifiant: adminServFormation" + echo " Mot de passe: Itinova69!" + echo "" + echo "Si la connexion échoue toujours, vérifiez les logs:" + echo " sudo journalctl -u formation-manager -f" +else + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ Des problèmes ont été détectés ⚠️ ║" + echo "╚════════════════════════════════════════════════════════════════╝" + echo "" + echo "Actions recommandées:" + echo "" + + if [ "$JWT_OK" != true ]; then + echo "1. Ajouter JWT_SECRET dans .env:" + echo " echo \"JWT_SECRET=\$(openssl rand -base64 32)\" >> .env" + echo "" + fi + + if [ "$USER_OK" != true ] || [ "$HASH_OK" != true ]; then + echo "2. Réinitialiser l'utilisateur admin:" + echo " ./create-admin.sh" + echo "" + fi + + if [ "$SERVICE_OK" != true ] && [ "$APP_OK" != true ]; then + echo "3. Redémarrer l'application:" + echo " sudo systemctl restart formation-manager" + echo "" + fi +fi