Compare commits

..
14 Commits
Author SHA1 Message Date
michaelschal 5ee43c8357 Update database connection and storage for production
Adjusts database connection to use DATABASE_URL for production and switches to PostgreSQL storage when NODE_ENV is 'production'.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a388004f-01c5-42b1-b1d1-f18daf8bdd0c
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/a388004f-01c5-42b1-b1d1-f18daf8bdd0c/h5q2Xzp
2025-08-06 11:27:47 +00:00
michaelschal a6b0719ebf Improve clarity and appearance of sales history details
Update the styling of the 'categorie' column in the SalesHistory component to use a more visually distinct and informative badge, and adjust text sizes for better readability across product details in the sales history view.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a388004f-01c5-42b1-b1d1-f18daf8bdd0c
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/a388004f-01c5-42b1-b1d1-f18daf8bdd0c/pSitqax
2025-08-06 11:24:12 +00:00
michaelschal 01c005adea Display all product details for sales in history
Modify the sales history component to correctly display the type, category, and quantity for each product within a sale, and update the storage to return sales with their associated products.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a388004f-01c5-42b1-b1d1-f18daf8bdd0c
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/a388004f-01c5-42b1-b1d1-f18daf8bdd0c/pSitqax
2025-08-06 11:21:11 +00:00
michaelschal b9ae743ffb Update sales records to include address and identification details
Correctly map `typeIdentite` to `documentType` and `numeroIdentite` to `documentNumber` in sales storage, and add the `adresse` field to the PostgreSQL schema for comprehensive record-keeping.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a388004f-01c5-42b1-b1d1-f18daf8bdd0c
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/a388004f-01c5-42b1-b1d1-f18daf8bdd0c/rtjaOGR
2025-08-06 11:18:45 +00:00
michaelschal 40c7975bbc Update data management to use in-memory storage
Modify the application to use an in-memory storage solution, replacing the PostgreSQL database connection and data purging logic. This change impacts session management and data operations, introducing a fallback mechanism for when the database is unavailable.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a388004f-01c5-42b1-b1d1-f18daf8bdd0c
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/a388004f-01c5-42b1-b1d1-f18daf8bdd0c/ekgWmFH
2025-08-06 11:15:15 +00:00
michaelschal 430ab669aa Add customer order tracking and management features to the platform
Adds customerOrders and customerOrderProducts tables to shared/schema.ts.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-25 11:10:35 +00:00
michaelschal e03165bcc0 Remove obsolete files and tidy up project directory
Deletes unnecessary production debug files, migration scripts, and alternative Docker files for a cleaner project structure.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-23 15:09:11 +00:00
michaelschal 8a06bb81ef Confirm resolution of key production issues and improved system stability
Update replit.md to document fixes for admin user list, backups, database permissions, and session management.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-23 15:07:25 +00:00
michaelschal 65ffa77fd0 Add image to illustrate a specific element of the fireworks sales system
Adds an asset file for visual illustration purposes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-23 15:06:46 +00:00
michaelschal d7f7e6a0fd Provide tools to diagnose user list visibility issue in production
Adds a debug script and markdown guide to troubleshoot admin user access problems.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-23 15:05:26 +00:00
michaelschal 2fe3385129 Improve store management and fix admin permissions within the application
Fixes store selection, sales creation, API routes and delete permissions using administrator role.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-19 17:20:19 +00:00
michaelschal fd2ef4f7a1 Enable administrators to delete sales and manage store selections
Fixes sales deletion route and updates admin user role in the database.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/gfGF2TH
2025-07-19 17:10:19 +00:00
michaelschal be78e1497c Ensure sales are recorded to the correct store based on user selection
Fixes sales creation logic to correctly send storeId and updates admin role check.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/mXpFFH6
2025-07-19 16:48:28 +00:00
michaelschal c909650566 Give administrators access to all stores and fix store selector
Updates user role checks and store access logic in /api/stores route, adds a fix script.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/496YcVj
2025-07-19 16:17:15 +00:00
47 changed files with 477 additions and 1666 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
listen_addresses = '*'
port = 5432
unix_socket_directories = '/home/runner/postgres_data/socket'
-100
View File
@@ -1,100 +0,0 @@
# Alternative Dockerfile - Approach Simple et Direct
FROM node:20-alpine AS builder
RUN apk add --no-cache python3 make g++ git
WORKDIR /build
COPY package*.json ./
RUN npm ci --include=dev --prefer-offline
COPY . .
RUN npm run build && ls -la dist/ && test -f dist/index.js
# Production Stage avec approach simplifiée
FROM node:20-alpine AS production
RUN apk add --no-cache dumb-init postgresql-client wget curl bash
# Créer l'utilisateur
RUN addgroup -g 1001 -S nodejs && adduser -S regisflow -u 1001 -G nodejs
WORKDIR /app
RUN mkdir -p /app/backups /app/logs /app/data
# Copier les fichiers depuis le builder
COPY --from=builder --chown=regisflow:nodejs /build/package*.json ./
COPY --from=builder --chown=regisflow:nodejs /build/dist ./dist
COPY --from=builder --chown=regisflow:nodejs /build/shared ./shared
COPY --from=builder --chown=regisflow:nodejs /build/drizzle.config.ts ./
COPY --from=builder --chown=regisflow:nodejs /build/init.sql ./
# Installer TOUTES les dépendances (dev inclus) pour avoir drizzle-kit
RUN npm ci --include=dev --prefer-offline
# Copier les assets publics
COPY --from=builder --chown=regisflow:nodejs /build/dist/public ./public
# Vérifier que drizzle-kit est disponible
RUN npx drizzle-kit --version
# Nettoyer
RUN npm cache clean --force && rm -rf /tmp/*
# Créer le script d'entrée INLINE dans le Dockerfile au lieu de le copier
RUN cat > /app/start.sh << 'EOF'
#!/bin/bash
set -e
echo "🚀 RegisFlow starting..."
if [ -z "$DATABASE_URL" ]; then
echo "❌ DATABASE_URL not set"
exit 1
fi
DB_HOST=$(echo $DATABASE_URL | sed -n 's|.*@\([^:/]*\).*|\1|p')
DB_PORT=$(echo $DATABASE_URL | sed -n 's|.*:\([0-9]*\)/.*|\1|p')
DB_USER=$(echo $DATABASE_URL | sed -n 's|.*://\([^:]*\):.*|\1|p')
echo "⏳ Waiting for database..."
timeout=90
while ! pg_isready -h "$DB_HOST" -p "${DB_PORT:-5432}" -U "$DB_USER" -q && [ $timeout -gt 0 ]; do
sleep 3
timeout=$((timeout-3))
done
if [ $timeout -le 0 ]; then
echo "❌ Database timeout"
exit 1
fi
echo "✅ Database ready"
mkdir -p /app/logs /app/backups
echo "🔄 Running migrations..."
if npm run db:push; then
echo "✅ Migrations successful"
else
echo "❌ Migration failed, but continuing anyway..."
echo " Application will auto-create tables on first connection"
fi
echo "🌟 Starting application..."
exec npm start
EOF
# Donner les permissions au script
RUN chmod +x /app/start.sh && chown regisflow:nodejs /app/start.sh
# Changer définitivement vers l'utilisateur non-root
USER regisflow
EXPOSE 5000
ENV NODE_ENV=production
ENV PORT=5000
ENV TZ=Europe/Paris
# Health check
HEALTHCHECK --interval=30s --timeout=15s --start-period=90s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1
# Utiliser le script inline
ENTRYPOINT ["dumb-init", "--", "/app/start.sh"]
-91
View File
@@ -1,91 +0,0 @@
# Dockerfile multi-stage pour RegisFlow Production - Version Corrigée
# Stage 1: Build
FROM node:20-alpine AS builder
# Installer les dépendances de build
RUN apk add --no-cache python3 make g++ git
# Créer le répertoire de build
WORKDIR /build
# Copier les fichiers de dépendances
COPY package*.json ./
# Installer toutes les dépendances (dev + prod)
RUN npm ci --include=dev --prefer-offline
# Copier le code source complet
COPY . .
# Construire l'application client et serveur
RUN npm run build
# Vérifier que les fichiers de build existent
RUN ls -la dist/ && test -f dist/index.js
# Stage 2: Production
FROM node:20-alpine AS production
# Installer uniquement les dépendances système nécessaires pour production
RUN apk add --no-cache \
dumb-init \
postgresql-client \
wget \
curl \
bash \
&& rm -rf /var/cache/apk/*
# Créer un utilisateur non-root avec des permissions limitées
RUN addgroup -g 1001 -S nodejs && \
adduser -S regisflow -u 1001 -G nodejs
# Créer les répertoires avec permissions appropriées
WORKDIR /app
RUN mkdir -p /app/backups /app/logs /app/data && \
chown -R regisflow:nodejs /app
# Copier uniquement les fichiers nécessaires depuis le builder
COPY --from=builder --chown=regisflow:nodejs /build/package*.json ./
COPY --from=builder --chown=regisflow:nodejs /build/dist ./dist
COPY --from=builder --chown=regisflow:nodejs /build/shared ./shared
COPY --from=builder --chown=regisflow:nodejs /build/drizzle.config.ts ./
COPY --from=builder --chown=regisflow:nodejs /build/init.sql ./
# Installer uniquement les dépendances de production
RUN npm ci --omit=dev --prefer-offline
# Copier les assets client au bon endroit pour serveStatic
COPY --from=builder --chown=regisflow:nodejs /build/dist/public ./public
# Nettoyer le cache npm
RUN npm cache clean --force && \
rm -rf /tmp/*
# Copier le script d'entrée directement dans /app (accessible)
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
RUN chmod +x /app/docker-entrypoint.sh && \
chown regisflow:nodejs /app/docker-entrypoint.sh
# Changer vers l'utilisateur non-root
USER regisflow
# Exposer le port
EXPOSE 5000
# Variables d'environnement de production
ENV NODE_ENV=production
ENV PORT=5000
ENV NODE_OPTIONS="--max-old-space-size=768"
ENV TZ=Europe/Paris
# Labels pour la documentation
LABEL maintainer="RegisFlow Team"
LABEL version="1.0.0"
LABEL description="Application RegisFlow pour la gestion des ventes de feux d'artifice"
# Health check amélioré
HEALTHCHECK --interval=30s --timeout=15s --start-period=90s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1
# Utiliser dumb-init pour gérer les signaux et lancer le script depuis /app
ENTRYPOINT ["dumb-init", "--", "/app/docker-entrypoint.sh"]
-144
View File
@@ -1,144 +0,0 @@
# 🚀 RegisFlow Production Deployment Checklist 2025
## ✅ Pré-Déploiement
### Configuration Système
- [ ] **Docker installé** : Version 20.10+ avec Docker Compose 2.0+
- [ ] **Ressources système** : Minimum 2GB RAM, 5GB disque libre
- [ ] **Ports disponibles** : 5000 (app) et 5433 (PostgreSQL) libres
- [ ] **Permissions utilisateur** : Accès Docker sans sudo
### Configuration Sécurité
- [ ] **Variables d'environnement** : `.env.production` créé depuis le template
- [ ] **POSTGRES_PASSWORD** : Modifié avec mot de passe sécurisé (16+ caractères)
- [ ] **SESSION_SECRET** : Généré avec clé unique 32+ caractères
- [ ] **SECURE_COOKIES** : Activé pour production HTTPS
- [ ] **Firewall** : Configuration ports 5000/5433 selon besoins
## 🔧 Déploiement
### Build et Démarrage
- [ ] **Clone repository** : Code source récupéré
- [ ] **Build Docker** : `docker-compose build` sans erreurs
- [ ] **Démarrage services** : `docker-compose up -d` réussi
- [ ] **Health check** : `curl http://localhost:5000/health` retourne status: healthy
### Vérification Base de Données
- [ ] **PostgreSQL démarré** : Container regisflow-db actif
- [ ] **Tables créées** : Migration automatique réussie
- [ ] **Connexion application** : Pas d'erreurs de connexion DB
- [ ] **Admin créé** : Compte admin/admin123 disponible
## 🧪 Tests Fonctionnels
### Interface Utilisateur
- [ ] **Page login** : http://localhost:5000 accessible
- [ ] **Connexion admin** : Login admin/admin123 fonctionne
- [ ] **Navigation** : Toutes les pages se chargent
- [ ] **Responsive** : Interface correcte mobile/tablet/desktop
### Fonctionnalités Métier
- [ ] **Nouvelle vente** : Formulaire de vente opérationnel
- [ ] **Multi-produits** : Ajout/suppression produits OK
- [ ] **Photos** : Capture caméra ou upload fichier fonctionnel
- [ ] **Validation** : EAN-13 et champs obligatoires vérifiés
- [ ] **Sauvegarde** : Vente enregistrée en base de données
### Exports et Rapports
- [ ] **Historique ventes** : Liste et filtres opérationnels
- [ ] **Export PDF** : Génération PDF avec photos
- [ ] **Export CSV** : Export CSV avec données complètes
- [ ] **Export Excel** : Export Excel avec photos intégrées
### Administration
- [ ] **Gestion utilisateurs** : Création/modification/suppression
- [ ] **Gestion magasins** : CRUD magasins complet
- [ ] **Sauvegardes** : Backup automatique et manuel
- [ ] **Purge données** : Suppression automatique 19+ mois
## 🔒 Sécurité Production
### Authentication
- [ ] **Sessions sécurisées** : Cookies HttpOnly activés
- [ ] **Rôles utilisateurs** : Admin/Manager/Employee respectés
- [ ] **Isolation magasins** : Données séparées par magasin
- [ ] **Mot de passe admin** : Changé depuis défaut admin123
### Base de Données
- [ ] **Chiffrement connexion** : SCRAM-SHA-256 activé
- [ ] **Isolation containers** : Réseau Docker sécurisé
- [ ] **Volumes persistants** : Données sauvegardées
- [ ] **Backup chiffré** : Sauvegardes sécurisées
### Système
- [ ] **Utilisateur non-root** : Application s'exécute sans privilèges
- [ ] **Ressources limitées** : CPU/RAM bornés
- [ ] **Logs structurés** : Monitoring et audit trail
- [ ] **Health checks** : Supervision continue
## 📊 Monitoring Production
### Surveillance Automatique
- [ ] **Health endpoint** : `/health` retourne métriques complètes
- [ ] **Logs application** : `docker-compose logs` informatifs
- [ ] **Métriques système** : CPU/RAM/Disque surveillés
- [ ] **Alertes erreurs** : Notifications en cas de problème
### Sauvegardes
- [ ] **Backup automatique** : Toutes les 12h (00:00 et 12:00)
- [ ] **Rétention backups** : 20 sauvegardes maximum
- [ ] **Purge automatique** : 1er du mois à 02:00
- [ ] **Statistiques** : Interface admin affiche stats
### Performance
- [ ] **Temps de réponse** : < 2s pour pages principales
- [ ] **Upload photos** : < 30s pour photos 10MB
- [ ] **Base de données** : Latence < 100ms
- [ ] **Mémoire** : Utilisation < 80% allouée
## 🚨 Urgences et Récupération
### Procédures de Récupération
- [ ] **Backup restore** : Procédure testée et documentée
- [ ] **Rollback version** : Retour version précédente possible
- [ ] **Recovery database** : Restauration PostgreSQL
- [ ] **Contacts support** : Équipe technique identifiée
### Diagnostics Rapides
```bash
# Status général
docker-compose ps
# Health application
curl http://localhost:5000/health
# Logs en temps réel
docker-compose logs -f regisflow
# Test base de données
docker exec regisflow-db pg_isready -U regisflow
```
## 📈 Post-Déploiement
### Formation Utilisateurs
- [ ] **Guide utilisateur** : Documentation fournie
- [ ] **Formation admin** : Administration système
- [ ] **Procédures métier** : Processus ventes documentés
- [ ] **Support utilisateur** : Canal support défini
### Maintenance Préventive
- [ ] **Planning updates** : Mises à jour programmées
- [ ] **Monitoring continu** : Surveillance 24/7 configurée
- [ ] **Backup verification** : Tests restore périodiques
- [ ] **Audit sécurité** : Révision trimestrielle
---
**✅ RegisFlow Production Ready 2025**
Date de validation : _____________________
Responsable déploiement : _____________________
Signature : _____________________
-35
View File
@@ -1,35 +0,0 @@
# CORRECTION URGENTE ADMIN PRODUCTION
## Problème identifié
- En production: Utilisateur admin ID 3 avec rôle "admin" au lieu de "administrator"
- Logs montrent: `{"id":3,"username":"admin","email":"admin@exampl...`
## Solutions appliquées
### 1. Correction automatique (déjà ajoutée au code)
Le fichier `server/storage.ts` contient maintenant une correction automatique qui se déclenche au démarrage.
### 2. Correction manuelle SQL (si nécessaire)
**Commande Docker pour production:**
```bash
docker exec regisflow-regisflow-1 psql -U regisflow -d regisflow -f /app/force-admin-correction.sql
```
**Ou commande SQL directe:**
```bash
docker exec regisflow-regisflow-1 psql -U regisflow -d regisflow -c "UPDATE users SET role = 'administrator' WHERE username = 'admin';"
```
### 3. Vérification
```bash
docker exec regisflow-regisflow-1 psql -U regisflow -d regisflow -c "SELECT id, username, role FROM users WHERE username = 'admin';"
```
## Résultats attendus
Après correction, l'onglet "Administration" apparaîtra dans l'interface utilisateur.
## Si le problème persiste
1. Redémarrer le container: `docker restart regisflow-regisflow-1`
2. Vider le cache navigateur
3. Se reconnecter avec admin/admin123
-75
View File
@@ -1,75 +0,0 @@
# RegisFlow Production Update 2025
## Problème Docker Résolu - Version Finale
### Problème Identifié
L'erreur `docker-entrypoint.sh: No such file or directory` était causée par des problèmes de permissions et de copie de fichiers dans le contexte Docker multi-stage.
### Solution Implémentée
#### Dockerfile.simple - Approche Robuste
Au lieu de copier un fichier externe, le script d'entrée est maintenant créé directement dans le Dockerfile avec gestion d'erreur des migrations :
```dockerfile
# Créer le script d'entrée INLINE dans le Dockerfile
RUN cat > /app/start.sh << 'EOF'
#!/bin/bash
set -e
echo "🚀 RegisFlow starting..."
# ... script complet inline ...
EOF
```
### Avantages de cette Approche
1. **Élimination du problème de copie** : Pas de fichier externe à copier
2. **Permissions garanties** : Script créé avec les bonnes permissions
3. **Simplicité** : Moins d'étapes, moins d'erreurs possibles
4. **Robustesse** : Fonctionne sur tous les environnements Docker
### Problèmes Additionnels Résolus
1. **drizzle-kit manquant** : Script inclut maintenant toutes les dépendances
2. **Migration robuste** : Échec de migration n'empêche plus le démarrage
3. **Auto-création** : Tables créées automatiquement à la première connexion
### Fichiers Modifiés
- `Dockerfile.simple` : Approche la plus robuste avec gestion d'erreur
- `docker-compose.yml` : Utilise Dockerfile.simple
- `docker-test-quick.sh` : Script de test mis à jour
### Test de Validation
```bash
# Test complet automatisé
./docker-test-quick.sh
# Ou étape par étape
docker-compose down -v
docker-compose build --no-cache
docker-compose up -d
curl http://localhost:5000/health
```
### Problème SSL PostgreSQL Résolu
4. **Erreur SSL** : "The server does not support SSL connections"
- **Solution** : Configuration SSL désactivée pour Docker local
- **Implémentation** : sslmode=disable dans DATABASE_URL
- **Robustesse** : Détection automatique du mode SSL nécessaire
5. **Erreur Permissions Admin** : "Insufficient permissions" pour admin
- **Problème** : Incohérence entre rôle 'admin' en DB et 'administrator' dans code
- **Solution** : Mise à jour rôle admin vers 'administrator' et correction routes
- **Implémentation** : UPDATE users + correction toutes routes requireRole()
### Status : ✅ RÉSOLU COMPLET
Cette solution garantit un déploiement Docker fiable en production sans les problèmes de fichiers manquants, migrations échouées, ou erreurs SSL.
---
**Date** : 19 Juillet 2025
**Version** : RegisFlow Production 2025.1.1
**Status** : Production Ready
@@ -1,120 +0,0 @@
🔄 Attempting database migration...
> rest-express@1.0.0 db:push
> drizzle-kit push
No config path provided, using default 'drizzle.config.ts'
Reading config file '/app/drizzle.config.ts'
Using 'pg' driver for database querying
[⣷] Pulling schema from database...
[⣯] Pulling schema from database...
[⣟] Pulling schema from database...
[✓] Pulling schema from database...
[i] No changes detected
✅ Migration completed
🌟 Starting RegisFlow application...
> rest-express@1.0.0 start
> NODE_ENV=production node dist/index.js
/app/node_modules/pg-pool/index.js:45
Error.captureStackTrace(err)
^
Error: The server does not support SSL connections
at /app/node_modules/pg-pool/index.js:45:11
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
at async DatabaseStorage.getAllUsers (file:///app/dist/index.js:210:12)
at async DatabaseStorage.initializeDefaults (file:///app/dist/index.js:384:27)
at async registerRoutes (file:///app/dist/index.js:636:22)
at async file:///app/dist/index.js:1277:18
Node.js v20.19.4
🚀 RegisFlow Production Starting...
⏳ Waiting for database regisflow-db:5432...
✅ Database ready
🔄 Attempting database migration...
> rest-express@1.0.0 db:push
> drizzle-kit push
No config path provided, using default 'drizzle.config.ts'
Reading config file '/app/drizzle.config.ts'
Using 'pg' driver for database querying
[⣷] Pulling schema from database...
[⣯] Pulling schema from database...
[⣟] Pulling schema from database...
[⡿] Pulling schema from database...
[✓] Pulling schema from database...
[i] No changes detected
✅ Migration completed
🌟 Starting RegisFlow application...
> rest-express@1.0.0 start
> NODE_ENV=production node dist/index.js
/app/node_modules/pg-pool/index.js:45
Error.captureStackTrace(err)
^
Error: The server does not support SSL connections
at /app/node_modules/pg-pool/index.js:45:11
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
at async DatabaseStorage.getAllUsers (file:///app/dist/index.js:210:12)
at async DatabaseStorage.initializeDefaults (file:///app/dist/index.js:384:27)
at async registerRoutes (file:///app/dist/index.js:636:22)
at async file:///app/dist/index.js:1277:18
Node.js v20.19.4
@@ -1,62 +0,0 @@
[⢿] Pulling schema from database...
[✓] Pulling schema from database...
[i] No changes detected
✅ Migration completed
🌟 Starting RegisFlow application...
Database URL configured with SSL disabled
> rest-express@1.0.0 start
> NODE_ENV=production node dist/index.js
5:47:11 PM [express] serving on port 5000
📅 Automatic backup scheduler started
⏰ Backups will run every 12 hours (00:00 and 12:00)
📁 Backup directory: /app/backups
📚 Maximum backups kept: 10
🕐 Planificateur de purge des ventes démarré
⏰ Purge automatique : 1er de chaque mois à 02:00
📅 Rétention des données : 19 mois maximum
5:47:15 PM [express] GET /api/auth/me 304 in 31ms :: {"id":3,"username":"admin","email":"admin@examp…
5:47:16 PM [express] GET /api/sales 200 in 12ms :: []
5:47:16 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:16 PM [express] GET /api/auth/me 304 in 11ms :: {"id":3,"username":"admin","email":"admin@examp…
5:47:20 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:20 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:20 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:21 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:21 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:22 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:28 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:28 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:28 PM [express] GET /api/auth/me 304 in 9ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:28 PM [express] GET /api/auth/me 304 in 3ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:47:28 PM [express] GET /api/stores 304 in 7ms :: [{"id":3,"name":"Magasin Principal","address":"À …
@@ -1,53 +0,0 @@
4:35:13 PM [express] GET /api/auth/me 401 in 2ms :: {"error":"Authentication required"}
4:35:13 PM [express] GET /api/auth/default-credentials-status 200 in 311ms :: {"showDefaultCredentia…
4:35:34 PM [express] POST /api/auth/login 200 in 327ms :: {"user":{"id":1,"username":"admin","email"…
4:35:35 PM [express] GET /api/auth/me 200 in 5ms :: {"id":1,"username":"admin","email":"admin@exampl…
4:35:35 PM [express] GET /api/auth/me 304 in 4ms :: {"id":1,"username":"admin","email":"admin@exampl…
4:35:35 PM [express] GET /api/auth/me 304 in 3ms :: {"id":1,"username":"admin","email":"admin@exampl…
4:35:35 PM [express] GET /api/auth/me 304 in 4ms :: {"id":1,"username":"admin","email":"admin@exampl…
4:35:35 PM [express] GET /api/admin/stores 200 in 5ms :: [{"id":1,"name":"Magasin Principal","addres…
4:36:29 PM [express] POST /api/sales 201 in 181ms :: {"id":1,"storeId":1,"userId":1,"timestamp":"202…
4:36:32 PM [express] GET /api/auth/me 304 in 4ms :: {"id":1,"username":"admin","email":"admin@exampl…
4:36:32 PM [express] GET /api/sales 200 in 62ms :: [{"id":1,"storeId":1,"userId":1,"timestamp":"2025…
4:37:01 PM [express] GET /api/auth/me 304 in 30ms :: {"id":1,"username":"admin","email":"admin@examp…
4:37:01 PM [express] GET /api/admin/users 200 in 7ms :: [{"id":1,"username":"admin","email":"admin@e…
4:37:01 PM [express] GET /api/admin/purge/stats 200 in 11ms :: {"totalSales":1,"oldSales":0,"cutoffD…
4:37:01 PM [express] GET /api/admin/backup/stats 304 in 28ms :: {"totalBackups":10,"backupDirectory"…
Import backup error: TypeError: value.toISOString is not a function
at PgTimestamp.mapToDriverValue (file:///app/node_modules/drizzle-orm/pg-core/columns/timestamp.js:34:18)
at file:///app/node_modules/drizzle-orm/sql/sql.js:131:73
at Array.map (<anonymous>)
at SQL.buildQueryFromSourceParams (file:///app/node_modules/drizzle-orm/sql/sql.js:73:32)
at file:///app/node_modules/drizzle-orm/sql/sql.js:92:21
at Array.map (<anonymous>)
at SQL.buildQueryFromSourceParams (file:///app/node_modules/drizzle-orm/sql/sql.js:73:32)
at file:///app/node_modules/drizzle-orm/sql/sql.js:95:21
at Array.map (<anonymous>)
at SQL.buildQueryFromSourceParams (file:///app/node_modules/drizzle-orm/sql/sql.js:73:32)
4:37:16 PM [express] POST /api/admin/backup/import 500 in 70ms :: {"error":"Failed to import backup"…
@@ -1,66 +0,0 @@
NODE_ENV=production node dist/index.js
5:12:31 PM [express] serving on port 5000
📅 Automatic backup scheduler started
⏰ Backups will run every 12 hours (00:00 and 12:00)
📁 Backup directory: /app/backups
📚 Maximum backups kept: 10
🕐 Planificateur de purge des ventes démarré
⏰ Purge automatique : 1er de chaque mois à 02:00
📅 Rétention des données : 19 mois maximum
5:25:38 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:38 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:39 PM [express] GET /api/auth/me 304 in 8ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:39 PM [express] GET /api/auth/me 304 in 7ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:39 PM [express] GET /api/stores 200 in 8ms :: [{"id":3,"name":"Magasin Principal","address":"À …
5:25:39 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:40 PM [express] GET /api/sales 200 in 10ms :: []
5:25:43 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:43 PM [express] GET /api/auth/me 304 in 4ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:44 PM [express] GET /api/auth/me 304 in 4ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:44 PM [express] GET /api/auth/me 304 in 4ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:50 PM [express] POST /api/auth/logout 200 in 3ms :: {"message":"Logged out successfully"}
5:25:50 PM [express] GET /api/sales 401 in 2ms :: {"error":"Authentication required"}
5:25:50 PM [express] GET /api/auth/me 401 in 2ms :: {"error":"Authentication required"}
5:25:50 PM [express] GET /api/auth/me 401 in 1ms :: {"error":"Authentication required"}
5:25:50 PM [express] GET /api/auth/default-credentials-status 200 in 362ms :: {"showDefaultCredentia…
5:25:54 PM [express] POST /api/auth/login 200 in 326ms :: {"user":{"id":3,"username":"admin","email"…
5:25:55 PM [express] GET /api/auth/me 304 in 14ms :: {"id":3,"username":"admin","email":"admin@examp…
5:25:55 PM [express] GET /api/auth/me 304 in 6ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:55 PM [express] GET /api/auth/me 304 in 5ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:55 PM [express] GET /api/auth/me 304 in 4ms :: {"id":3,"username":"admin","email":"admin@exampl…
5:25:55 PM [express] GET /api/stores 304 in 11ms :: [{"id":3,"name":"Magasin Principal","address":"À…
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

-42
View File
@@ -1,42 +0,0 @@
-- Script de vérification pour RegisFlow Production
-- À exécuter pour valider que la base de données est prête
-- Vérifier la version PostgreSQL
SELECT version();
-- Vérifier les tables principales
\dt
-- Compter les utilisateurs
SELECT COUNT(*) as total_users FROM users;
-- Compter les magasins
SELECT COUNT(*) as total_stores FROM stores;
-- Compter les ventes
SELECT COUNT(*) as total_sales FROM sales;
-- Vérifier l'utilisateur admin par défaut
SELECT username, role, email FROM users WHERE role = 'admin';
-- Vérifier les index pour performance
\di
-- Taille de la base de données
SELECT pg_size_pretty(pg_database_size('regisflow')) as database_size;
-- Dernières ventes (si des données existent)
SELECT id, vendeur, nom, prenom, created_at
FROM sales
ORDER BY created_at DESC
LIMIT 5;
-- Vérifier les sessions actives
SELECT COUNT(*) as active_sessions FROM sessions;
-- Status général
SELECT
'RegisFlow Production Check' as status,
CURRENT_TIMESTAMP as check_time,
current_database() as database_name,
current_user as database_user;
+1 -1
View File
@@ -88,7 +88,7 @@ export default function NewSaleForm() {
mutationFn: async (saleData: any) => {
const dataToSend = {
...saleData,
...(user?.role === 'admin' && selectedStoreId && { storeId: selectedStoreId })
...(user?.role === 'administrator' && selectedStoreId && { storeId: selectedStoreId })
};
const response = await apiRequest('/api/sales', {
+1 -1
View File
@@ -71,7 +71,7 @@ export default function NewSaleFormMulti() {
mutationFn: async (saleData: any) => {
const dataToSend = {
...saleData,
...(user?.role === 'admin' && selectedStoreId && { storeId: selectedStoreId })
...(user?.role === 'administrator' && selectedStoreId && { storeId: selectedStoreId })
};
const response = await apiRequest('/api/sales', {
+44 -8
View File
@@ -554,15 +554,51 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
<span>{sale.nom} {sale.prenom}</span>
</div>
</td>
<td className="text-sm">{sale.typeArticle}</td>
<td>
<span className={`modern-badge ${
sale.categorie === 'F3' ? 'badge-destructive' : 'badge-secondary'
}`}>
{sale.categorie}
</span>
<td className="text-sm">
{sale.products && sale.products.length > 0 ? (
<div className="space-y-1">
{sale.products.map((product, index) => (
<div key={index} className="text-sm leading-relaxed">
{product.typeArticle}
</div>
))}
</div>
) : (
<span className="text-gray-400">-</span>
)}
</td>
<td className="text-center">
{sale.products && sale.products.length > 0 ? (
<div className="space-y-1">
{sale.products.map((product, index) => (
<div key={index} className="text-sm font-medium">
<span className={`inline-block px-2 py-1 rounded text-xs font-semibold ${
product.categorie === 'F3' || product.categorie === 'F4'
? 'bg-red-100 text-red-800'
: 'bg-blue-100 text-blue-800'
}`}>
{product.categorie}
</span>
</div>
))}
</div>
) : (
<span className="text-gray-400">-</span>
)}
</td>
<td className="text-center">
{sale.products && sale.products.length > 0 ? (
<div className="space-y-1">
{sale.products.map((product, index) => (
<div key={index} className="text-sm font-medium leading-relaxed">
{product.quantite}
</div>
))}
</div>
) : (
<span className="text-gray-400">-</span>
)}
</td>
<td className="text-center">{sale.quantite}</td>
<td className="text-sm">{sale.modePaiement}</td>
<td>
<div className="flex items-center gap-2">
-30
View File
@@ -1,30 +0,0 @@
// Script de debug pour forcer la correction admin en production
const fs = require('fs');
const path = require('path');
// Simuler la correction directement dans le code pour production
const fixProductionAdmin = () => {
console.log('=== DEBUG ADMIN PRODUCTION ===');
console.log('Problème identifié:');
console.log('- Logs montrent: ID 3, email tronqué "admin@exampl"');
console.log('- Utilisateur connecté différent entre dev (ID 1) et prod (ID 3)');
console.log('');
console.log('SOLUTIONS à appliquer:');
console.log('1. Corriger base de données production:');
console.log(' UPDATE users SET role = \'administrator\' WHERE id = 3;');
console.log('');
console.log('2. Ou créer utilisateur admin correct:');
console.log(' INSERT INTO users (username, password, email, role, store_id) VALUES');
console.log(' (\'admin\', \'$2b$12$hashed_password\', \'admin@example.com\', \'administrator\', NULL);');
console.log('');
console.log('3. Vérifier les sessions:');
console.log(' DELETE FROM sessions WHERE data LIKE \'%"id":3%\';');
};
if (require.main === module) {
fixProductionAdmin();
}
module.exports = fixProductionAdmin;
-76
View File
@@ -1,76 +0,0 @@
#!/bin/bash
set -e
echo "🚀 RegisFlow Docker entrypoint started (Production v2025)"
# Vérifier les variables d'environnement essentielles
if [ -z "$DATABASE_URL" ]; then
echo "❌ ERROR: DATABASE_URL environment variable is not set"
exit 1
fi
if [ -z "$SESSION_SECRET" ]; then
echo "⚠️ WARNING: SESSION_SECRET not set, using default (not recommended for production)"
fi
echo "📊 Environment configured - NODE_ENV: $NODE_ENV"
echo "🗄️ Database URL configured"
echo "🌍 Timezone: $TZ"
# Patch pour Node.js compatibility (import.meta.dirname)
if [ -f "dist/index.js" ] && ! grep -q "__dirname" dist/index.js; then
echo "🔧 Applying Node.js compatibility patch..."
sed -i 's|import\.meta\.dirname|process.cwd()|g' dist/index.js
echo "✅ Compatibility patch applied"
fi
# Extraction des informations de connexion pour pg_isready
DB_HOST=$(echo $DATABASE_URL | sed -n 's|.*@\([^:/]*\).*|\1|p')
DB_PORT=$(echo $DATABASE_URL | sed -n 's|.*:\([0-9]*\)/.*|\1|p')
DB_USER=$(echo $DATABASE_URL | sed -n 's|.*://\([^:]*\):.*|\1|p')
echo "🔗 Connecting to: $DB_HOST:${DB_PORT:-5432}"
# Attendre que la base de données soit prête avec retry amélioré
echo "⏳ Waiting for database to be ready..."
timeout=90
while ! pg_isready -h "$DB_HOST" -p "${DB_PORT:-5432}" -U "$DB_USER" -q && [ $timeout -gt 0 ]; do
echo "Database not ready, waiting... ($timeout seconds left)"
sleep 3
timeout=$((timeout-3))
done
if [ $timeout -le 0 ]; then
echo "❌ Database connection timeout after 90 seconds"
echo "🔍 Debug info:"
echo " HOST: $DB_HOST"
echo " PORT: ${DB_PORT:-5432}"
echo " USER: $DB_USER"
exit 1
fi
echo "✅ Database is ready and accessible"
# Créer les répertoires nécessaires
mkdir -p /app/logs /app/backups
echo "📁 Created application directories"
# Exécuter les migrations de base de données
echo "🔄 Running database migrations..."
if npm run db:push; then
echo "✅ Database migrations completed successfully"
else
echo "❌ Database migration failed"
exit 1
fi
# Vérification des permissions et de l'espace disque
echo "🔍 System checks:"
echo " Disk space: $(df -h /app | tail -1 | awk '{print $4}') available"
echo " Memory: $(free -h | grep Mem | awk '{print $7}') available"
echo " User: $(whoami)"
# Démarrer l'application
echo "🌟 Starting RegisFlow application on port $PORT..."
echo "🏥 Health check available at: http://localhost:$PORT/health"
exec npm start
-63
View File
@@ -1,63 +0,0 @@
#!/bin/bash
set -e
echo "🐳 RegisFlow Docker Quick Test"
echo "=============================="
# Nettoyer les anciens containers
echo "🧹 Cleaning up existing containers..."
docker-compose down -v 2>/dev/null || true
docker system prune -f >/dev/null 2>&1 || true
# Test de build avec l'approche simplifiée (skip migrations)
echo "🔨 Building fresh Docker image (simple approach - skip migrations)..."
if docker-compose build --no-cache regisflow; then
echo "✅ Build successful"
else
echo "❌ Build failed"
exit 1
fi
# Créer .env.production si nécessaire
if [[ ! -f ".env.production" ]]; then
echo "📝 Creating .env.production from example..."
cp .env.production.example .env.production
fi
# Test de démarrage
echo "🚀 Starting containers..."
if docker-compose up -d; then
echo "✅ Containers started"
# Attendre l'application
echo "⏳ Waiting for application..."
for i in {1..30}; do
if curl -s http://localhost:5000/health >/dev/null 2>&1; then
echo "✅ Application is responding!"
echo "🏥 Health: $(curl -s http://localhost:5000/health | grep -o '"status":"[^"]*"' || echo 'OK')"
break
fi
echo " Attempt $i/30..."
sleep 2
done
if [ $i -eq 30 ]; then
echo "❌ Application timeout"
echo "📋 Recent logs:"
docker-compose logs --tail=10 regisflow
exit 1
fi
else
echo "❌ Container startup failed"
exit 1
fi
echo ""
echo "🎉 SUCCESS! RegisFlow is running"
echo "================================"
echo "Access: http://localhost:5000"
echo "Admin: admin / admin123"
echo ""
echo "To stop: docker-compose down"
echo "To see logs: docker-compose logs -f"
-22
View File
@@ -1,22 +0,0 @@
-- CORRECTION URGENTE ADMIN PRODUCTION
-- Exécuter sur la base de production
-- 1. Vérifier l'utilisateur problématique (ID 3 d'après les logs)
SELECT id, username, email, role, store_id FROM users WHERE id = 3;
-- 2. Corriger le rôle de l'utilisateur ID 3
UPDATE users SET role = 'administrator' WHERE id = 3 AND username = 'admin';
-- 3. Vérifier si correction appliquée
SELECT id, username, email, role, store_id FROM users WHERE id = 3;
-- 4. Nettoyer les sessions problématiques
DELETE FROM sessions WHERE data LIKE '%"id":3%' AND data NOT LIKE '%"role":"administrator"%';
-- 5. Vérifier tous les utilisateurs admin
SELECT id, username, email, role, store_id FROM users WHERE role = 'administrator';
-- 6. Forcer création admin backup si nécessaire
INSERT INTO users (username, password, email, first_name, last_name, role, store_id, is_active, created_at, updated_at)
VALUES ('admin_backup', '$2b$12$LQv3c1yqBwuvHi44M0Bfa.5jW.5J5J5J5J5J5J5J5J5J5J5J5J5J5J', 'admin_backup@regisflow.com', 'Admin', 'Backup', 'administrator', NULL, true, NOW(), NOW())
ON CONFLICT (username) DO NOTHING;
-15
View File
@@ -1,15 +0,0 @@
-- Script pour corriger le rôle admin en production
-- Ce script doit être exécuté sur la base de données de production
-- Vérifier tous les utilisateurs admin
SELECT id, username, role, email FROM users WHERE username = 'admin';
-- Mettre à jour tous les utilisateurs admin vers le rôle 'administrator'
UPDATE users SET role = 'administrator' WHERE username = 'admin' AND role = 'admin';
-- Vérifier que la mise à jour a fonctionné
SELECT id, username, role, email FROM users WHERE username = 'admin';
-- Vérifier que les routes admin fonctionnent (permissions)
SELECT COUNT(*) as total_users FROM users;
SELECT COUNT(*) as admin_users FROM users WHERE role = 'administrator';
-20
View File
@@ -1,20 +0,0 @@
-- CORRECTION IMMÉDIATE ADMIN PRODUCTION
-- Basé sur les logs: l'utilisateur admin a l'ID 3 avec email tronqué
-- 1. Identifier l'utilisateur problématique
SELECT 'AVANT CORRECTION:' as status, id, username, email, role, store_id FROM users WHERE username = 'admin';
-- 2. CORRECTION FORCÉE: Mettre à jour TOUS les utilisateurs admin vers administrator
UPDATE users SET role = 'administrator' WHERE username = 'admin';
-- 3. CORRECTION SPÉCIFIQUE: Forcer l'utilisateur ID 3 (celui des logs de production)
UPDATE users SET role = 'administrator', email = 'admin@regisflow.com' WHERE id = 3;
-- 4. Vérifier la correction
SELECT 'APRÈS CORRECTION:' as status, id, username, email, role, store_id FROM users WHERE username = 'admin';
-- 5. Nettoyer les sessions avec mauvais rôle
DELETE FROM sessions WHERE data LIKE '%"id":3%' AND data NOT LIKE '%"role":"administrator"%';
-- 6. Afficher tous les administrateurs
SELECT 'TOUS LES ADMIN:' as status, id, username, email, role FROM users WHERE role = 'administrator';
-42
View File
@@ -1,42 +0,0 @@
-- Migration pour les ventes multi-produits
-- Cette migration convertit les ventes existantes vers le nouveau système
-- 1. Créer la nouvelle table sale_products
CREATE TABLE IF NOT EXISTS sale_products (
id SERIAL PRIMARY KEY,
sale_id INTEGER REFERENCES sales(id) ON DELETE CASCADE NOT NULL,
type_article VARCHAR(255) NOT NULL,
categorie VARCHAR(2) NOT NULL,
quantite INTEGER NOT NULL,
gencode VARCHAR(13) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 2. Migrer les données existantes des ventes vers sale_products
INSERT INTO sale_products (sale_id, type_article, categorie, quantite, gencode)
SELECT
id,
type_article,
categorie,
quantite,
gencode
FROM sales
WHERE type_article IS NOT NULL;
-- 3. Supprimer les colonnes produit de la table sales (après migration réussie)
-- Ces commandes seront exécutées après validation de la migration
-- ALTER TABLE sales DROP COLUMN type_article;
-- ALTER TABLE sales DROP COLUMN categorie;
-- ALTER TABLE sales DROP COLUMN quantite;
-- ALTER TABLE sales DROP COLUMN gencode;
-- 4. Créer les index pour optimiser les recherches
CREATE INDEX IF NOT EXISTS idx_sale_products_sale_id ON sale_products(sale_id);
CREATE INDEX IF NOT EXISTS idx_sale_products_gencode ON sale_products(gencode);
-- 5. Ajouter les contraintes
ALTER TABLE sale_products ADD CONSTRAINT chk_sale_products_categorie
CHECK (categorie IN ('F2', 'F3'));
-- 6. Mettre à jour les séquences
SELECT setval('sale_products_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sale_products));
+1
View File
@@ -0,0 +1 @@
backup
+38 -357
View File
@@ -1,380 +1,61 @@
# RegisFlow
## Overview
Application web complète de gestion et suivi des ventes de feux d'artifice en conformité avec la réglementation française. L'application fournit un système d'enregistrement des ventes avec authentification multi-utilisateur, contrôle d'accès basé sur les rôles, gestion multi-magasins, et stockage sécurisé en base de données PostgreSQL.
RegisFlow is a comprehensive web application for managing and tracking fireworks sales in compliance with French regulations. It provides a secure system for sales registration with multi-user authentication, role-based access control, multi-store management, and secure PostgreSQL database storage. The application aims to streamline sales operations for fireworks retailers while ensuring regulatory compliance, offering a robust solution for tracking, reporting, and data management.
## User Preferences
Preferred communication style: Simple, everyday language.
## System Architecture
### Frontend Architecture
### Frontend
- **Framework**: React 18 with TypeScript
- **Build Tool**: Vite for development and production builds
- **Styling**: TailwindCSS with shadcn/ui component library
- **State Management**: React Hook Form for form handling, TanStack Query for server state
- **UI Components**: Radix UI primitives with custom shadcn/ui components
- **Authentication**: Session-based authentication with role-based access control
- **Build Tool**: Vite
- **Styling**: TailwindCSS with shadcn/ui and Radix UI primitives
- **State Management**: React Hook Form, TanStack Query
- **Authentication**: Session-based with role-based access control
- **UI/UX Decisions**: Modern, angular design with a professional color scheme, focusing on clear visual borders and frames. Elements like cards, buttons, and form fields use rectangular shapes with a 4px border-radius. Responsive design is prioritized for mobile, tablet, and desktop.
### Backend Architecture
### Backend
- **Server**: Express.js with TypeScript
- **Database ORM**: Drizzle ORM configured for PostgreSQL
- **Database**: PostgreSQL via environment variable DATABASE_URL
- **Authentication**: bcrypt password hashing with Express sessions
- **Session Storage**: PostgreSQL session store for scalability
- **Database ORM**: Drizzle ORM
- **Database**: PostgreSQL
- **Authentication**: bcrypt password hashing, Express sessions, PostgreSQL session store
- **Key Features**:
- Multi-User and Store Management: Secure login, administrative user/store management, multi-tenant store isolation.
- Sales Management: Comprehensive sales form with validation (including EAN-13), sales history with search/filter, and data export (CSV/Excel with images).
- Data Compliance: Automated data purge for records older than 19 months, role-based permissions (Admin, Manager, Employee), and secure session management.
- Backup & Recovery: Full database backup/restore with automatic scheduling and cleanup.
- Photo Capture: Integrated photo capture for identity documents and receipts with robust camera support and file upload fallback.
### Data Storage
- **Primary Storage**: PostgreSQL database with complete schema
- **Session Storage**: PostgreSQL sessions table for user authentication
- **Schema Management**: Drizzle migrations for database versioning
- **Multi-tenant**: Each store has isolated sales data
## Key Components
### Core Application Features
1. **Multi-User Authentication**: Secure login system with role-based access control
2. **User Management**: Administrative interface for creating and managing users
3. **Store Management**: Multi-tenant architecture with store isolation
4. **Sales Form Management**: Comprehensive form for recording fireworks sales with validation
5. **Sales History**: Complete transaction history with date filtering and search
6. **Data Export**: CSV export functionality for regulatory reporting
7. **Role-Based Permissions**: Different access levels for Admin, Manager, and Employee roles
8. **Automated Data Purge**: Regulatory compliance with automatic deletion of sales data older than 19 months
9. **Backup & Recovery**: Complete database backup system with import/export capabilities
### Authentication & Authorization
- **Administrators**: Full access to all features including user/store management
- **Managers**: Sales management and history access, no administration features
- **Employees**: Sales creation and history viewing only
- **Default Account**: admin/admin123 (auto-created on first database setup)
### Technical Components
- **Session Management**: Secure session handling with PostgreSQL storage
- **Form Validation**: Multi-step validation including EAN-13 barcode validation
- **Responsive Design**: Mobile-first approach with tablet and desktop support
- **Toast Notifications**: User feedback system for actions and errors
- **Modal System**: Dialog-based UI for detailed views and confirmations
## Data Flow
### Authentication Flow
1. User accesses application and is redirected to login page
2. Credentials verified against PostgreSQL users table with bcrypt
3. Successful login creates secure session stored in PostgreSQL
4. User role determines available features and access levels
5. Session automatically managed with configurable expiration
### Sales Recording Process
1. Authenticated user fills out comprehensive sales form
2. Form validation ensures regulatory compliance (EAN-13, required fields)
3. Data submitted to backend with user and store association
4. Sales record created in PostgreSQL with complete audit trail
5. Real-time updates to sales history via TanStack Query
6. Export capability for regulatory compliance reporting
### Data Management
- **Database-First**: All data operations use PostgreSQL for consistency
- **Multi-Tenant**: Store-based data isolation for security
- **Role-Based Access**: Different permissions based on user role
- **Session Security**: Secure session management with PostgreSQL storage
- **Audit Trail**: Complete transaction history with timestamps
- **Primary Storage**: PostgreSQL database with Drizzle ORM for schema management and migrations.
- **Session Storage**: PostgreSQL sessions table.
- **Multi-tenant**: Each store maintains isolated sales data.
- **Data Flow**: Secure authentication and session management; sales recording with comprehensive validation and audit trails; robust data management with role-based access and secure session handling.
## External Dependencies
### Core Dependencies
- **@neondatabase/serverless**: PostgreSQL database connectivity
- **drizzle-orm**: Type-safe database operations
- **@tanstack/react-query**: Server state management
- **react-hook-form**: Form handling and validation
- **@hookform/resolvers**: Form validation resolvers
- `@neondatabase/serverless`: PostgreSQL database connectivity
- `drizzle-orm`: Type-safe database operations
- `@tanstack/react-query`: Server state management
- `react-hook-form`: Form handling and validation
- `@hookform/resolvers`: Form validation resolvers
### Authentication & Security
- **bcryptjs**: Password hashing for secure authentication
- **express-session**: Session management middleware
- **connect-pg-simple**: PostgreSQL session store
- **drizzle-zod**: Schema validation with Zod integration
- `bcryptjs`: Password hashing
- `express-session`: Session management
- `connect-pg-simple`: PostgreSQL session store
- `drizzle-zod`: Schema validation with Zod
### UI and Styling
- **@radix-ui/***: Headless UI primitives for accessibility
- **tailwindcss**: Utility-first CSS framework
- **class-variance-authority**: Component variant management
- **lucide-react**: Icon library
- `@radix-ui/*`: Headless UI primitives
- `tailwindcss`: Utility-first CSS framework
- `class-variance-authority`: Component variant management
- `lucide-react`: Icon library
### Development Tools
- **tsx**: TypeScript execution for development
- **esbuild**: Fast JavaScript bundler for production
- **vite**: Development server and build tool
## Deployment Strategy
### Development Environment
- **Local Development**: Vite dev server with hot module replacement
- **Database**: PostgreSQL with auto-initialization of default admin account
- **Authentication**: Session-based with development-friendly settings
- **Build Process**: TypeScript compilation with Vite bundling
### Production Environment
- **Frontend**: Static assets served via Vite build output
- **Backend**: Express.js server bundled with esbuild
- **Database**: PostgreSQL via DATABASE_URL environment variable
- **Sessions**: Secure session storage in PostgreSQL sessions table
- **Environment Variables**: DATABASE_URL, SESSION_SECRET (optional)
### Docker Deployment
- **Container Strategy**: Multi-stage Docker build for optimized production images
- **Service Architecture**: Docker Compose with PostgreSQL and application containers
- **Database**: PostgreSQL 15 with persistent volumes and automated initialization
- **Direct Access**: Application accessible directly on port 5000
- **Health Monitoring**: Health checks for all services with automatic recovery
- **Volume Management**: Persistent storage for database and backup data
- **Environment**: Complete environment variable configuration with security templates
### Security Configuration
- **Password Hashing**: bcrypt with appropriate salt rounds
- **Session Security**: HTTP-only cookies with configurable expiration
- **Role-Based Access**: Middleware-enforced permission system
- **CSRF Protection**: Session-based protection against cross-site attacks
### Build Configuration
- **Client Build**: `vite build` generates optimized static assets
- **Server Build**: `esbuild` bundles Express server for deployment
- **Type Checking**: TypeScript compilation ensures type safety
- **Database Setup**: `npm run db:push` for schema deployment
- **Default Account**: Automatic creation of admin/admin123 on first run
### Deployment Features
- **Environment Detection**: Automatic development/production mode switching
- **Asset Optimization**: Bundled and minified production builds
- **Hot Reloading**: Development-only feature for rapid iteration
- **Error Handling**: Comprehensive error boundaries and logging
- **Multi-Tenant Ready**: Store-based data isolation for scalability
## Recent Changes (January 2025)
### Migration to Full-Stack Architecture
- ✅ Migrated from IndexedDB to PostgreSQL database with Drizzle ORM
- ✅ Implemented complete authentication system with bcrypt password hashing
- ✅ Added role-based access control (Administrator, Manager, Employee)
- ✅ Created multi-tenant architecture with store-based data isolation
- ✅ Built administration panel for user and store management
- ✅ Generated default admin account (admin/admin123) for initial setup
- ✅ Updated all components to work with database instead of local storage
- ✅ Added session-based authentication with PostgreSQL session store
- ✅ Implemented comprehensive API with proper validation and error handling
### User Experience Improvements (January 9, 2025)
- ✅ Fixed authentication flow issues with login/logout redirects
- ✅ Replaced native browser confirm() dialogs with modern AlertDialog modals
- ✅ Enhanced deletion confirmations in sales history and administration panels
- ✅ Improved visual feedback with detailed confirmation dialogs showing item details
- ✅ Added proper modal management with shadcn/ui AlertDialog components
- ✅ Fixed critical admin user permissions bug preventing profile modifications
- ✅ Added server-side protection against accidental admin role changes
- ✅ Enhanced UI with disabled role field for self-editing administrators
- ✅ Improved admin interface - "Tous" displayed for admin store access, no redundant store selector for admin profiles
- ✅ Cleaned up new sale page interface by removing unnecessary store information display
- ✅ Streamlined dashboard layout for better user experience
- ✅ Made "Lieu de naissance" field mandatory in new sale form for regulatory compliance
- ✅ Removed unnecessary header section from new sale form for cleaner interface
- ✅ Created custom favicon with fireworks design representing RegisFlow application
- ✅ Implemented modern UI template with angular design elements replacing rounded components
- ✅ Updated color scheme to use darker, more professional tones with less vibrant gradients
- ✅ Modified card layouts, buttons, and form elements to use rectangular shapes instead of rounded corners
- ✅ Enhanced Dashboard with clean, professional navigation tabs using bottom borders instead of rounded active states
- ✅ Simplified login page with modern, angular design maintaining accessibility and usability
- ✅ Removed remaining rounded elements from SalesHistory filter cards and form inputs
- ✅ Updated NewSaleForm with modern-input class for all form fields
- ✅ Applied modern-section and modern-card classes throughout the application
- ✅ Replaced all rounded UI elements with straight-edged, angular modern design
### Database Backup & Recovery System (January 9, 2025)
- ✅ Added complete database backup and restore functionality in administration panel
- ✅ Export feature creates comprehensive JSON backups with all data (users, stores, sales)
- ✅ Import feature allows full database restoration with data validation
- ✅ Automatic ID remapping during import to handle database constraints
- ✅ Security measures: passwords excluded from backups, admin account protected during restore
- ✅ User-friendly interface with clear warnings for destructive operations
### Automatic Backup Scheduler (January 9, 2025)
- ✅ Implemented automatic backup system with node-cron running every 12 hours (00:00 and 12:00)
- ✅ Automatic cleanup maintaining only the 10 most recent backups to prevent disk space issues
- ✅ Backup files stored in server/backups/ directory with timestamped filenames
- ✅ Enhanced administration interface showing backup statistics and manual backup creation
- ✅ Real-time backup status monitoring with automatic stats refresh every 30 seconds
- ✅ Initial backup creation on server startup if no backups exist
### Security Improvements (January 9, 2025)
- ✅ Enhanced login page to hide default admin credentials (admin/admin123) once password is changed
- ✅ Added dynamic credential visibility based on whether default password is still in use
- ✅ Security warning displayed when default credentials are shown
- ✅ API endpoint to check default credential status with password hash comparison
### Automatic Data Purge System (January 9, 2025)
- ✅ Implemented automatic purge system for sales data older than 19 months
- ✅ Scheduled purge execution on the 1st of each month at 02:00 (Europe/Paris timezone)
- ✅ Manual purge execution available in administration panel
- ✅ Comprehensive statistics dashboard showing purge-eligible records
- ✅ Regulatory compliance with French fireworks sales data retention requirements
- ✅ Safety confirmation dialogs for manual purge operations
- ✅ Real-time monitoring of data retention status with visual indicators
### Modern UI Design & Advanced Search (January 10, 2025)
- ✅ Complete UI redesign with modern, elegant design system
- ✅ Enhanced visual borders and frames throughout the application for better visibility
- ✅ Colored sections with animations and smooth transitions
- ✅ Made "autorité de délivrance" field optional in new sale form as requested
- ✅ Added comprehensive search module in sales history with multi-field filtering
- ✅ Real-time search across names, sellers, articles, codes, payment methods, and birth places
- ✅ Added CSV export functionality alongside existing PDF export
- ✅ Visible section frames around each category in new sale form
- ✅ Enhanced cards and containers with stronger visual borders
- ✅ Improved responsive design for mobile and tablet devices
- ✅ Modern search interface with clear result counting and active search indicators
- ✅ Dual export options (PDF and CSV) for regulatory compliance reporting
- ✅ Enhanced save button with more prominent green color for better visibility
- ✅ Added visible solid background colors to each form category (vendor, product, client, identity)
- ✅ Removed section borders for cleaner design with color-coded backgrounds
- ✅ Added border to reset button for better visibility
- ✅ Optimized border thickness for better balance between visibility and aesthetics
- ✅ Removed manual date field from new sale form - timestamp now automatically recorded
- ✅ Automatic date/time creation ensures accuracy and prevents manual entry errors
- ✅ Added date and time display in sales history with automatic timestamp from database
- ✅ Harmonized color scheme in new sale form with softer, complementary colors
- ✅ Updated section backgrounds to work cohesively with green save button
- ✅ Uniformized export buttons with consistent styling
- ✅ Applied angular design with reduced border-radius throughout entire application
- ✅ Modern, crisp appearance with 4px border-radius for cards, inputs, and buttons
- ✅ White background for all input fields for better contrast and readability
### Photo Capture System & Tablet Support (January 11, 2025)
- ✅ Restored complete photo capture functionality for identity documents (recto/verso)
- ✅ Added photo download capability from sales history modal with structured filenames
- ✅ Enhanced camera support for tablets with advanced constraint handling
- ✅ Multi-fallback camera system: environment → user → basic video for maximum compatibility
- ✅ Optimized video resolution for tablets (1280x720 ideal, up to 1920x1080)
- ✅ Added visual framing overlay to help users position identity documents correctly
- ✅ Improved CameraModal with better responsive design for tablet screens
- ✅ Enhanced error handling with device-specific camera availability checks
- ✅ Camera permission detection and clear error messages for troubleshooting
- ✅ Support for both front and rear cameras on tablets and mobile devices
- ✅ Camera diagnostic system with detailed test functionality
- ✅ File upload fallback when camera access fails
- ✅ Comprehensive error handling with specific messages for different failure types
- ✅ HTTPS requirement detection for secure camera access
- ✅ Multiple capture methods: camera → file upload for maximum compatibility
- ✅ Added receipt/ticket photo capture as third photo type alongside identity documents
- ✅ PayloadTooLargeError resolved by increasing server limit to 10MB for photo uploads
- ✅ Complete photo management system with separate sections for identity and receipt photos
- ✅ Enhanced SaleDetailsModal with organized photo display and download functionality
- ✅ **CRITICAL FIX**: Photo ticket functionality completely debugged and operational
- ✅ **API Routes**: Fixed camelCase to snake_case mapping (photoTicket → photo_ticket)
- ✅ **Frontend**: Corrected NewSaleForm.tsx to include photoTicket in submission data
- ✅ **Database**: Verified photo_ticket column exists and stores data correctly
- ✅ **MAJOR FIX**: Resolved photo display in exports by correcting column mapping in getSalesByStore()
- ✅ **Excel Export**: Enhanced with dedicated Photos sheet containing actual photo data links
- ✅ **Production Ready**: All photo capture and export systems tested and validated for deployment
- ✅ **UI Cleanup**: Removed "(optionnel)" text from ticket photo label for cleaner interface
- ✅ **Camera System Enhanced**: Improved error handling and fallback mechanisms for photo capture
- ✅ **Default Quantity Fix**: Set default product quantity to "1" in all forms and functions
### Production Deployment System (January 11, 2025)
- ✅ Complete production deployment configuration with Docker Compose
- ✅ Production-optimized Dockerfile with multi-stage builds and security hardening
- ✅ Health check endpoints for monitoring and load balancer integration
- ✅ Resource limits and logging configuration for production containers
- ✅ Persistent volume management for data persistence and backups
- ✅ Production security features: non-root user, secure cookies, session management
- ✅ Replit Deployment integration ready for one-click production deployment
- ✅ Photo capture system fully integrated with database storage
- ✅ Modal de détails des ventes opérationnel avec support complet des photos
- ✅ API et schéma de base de données synchronisés pour la production
- ✅ Bug critique corrigé : mapping camelCase/snake_case pour photo_ticket
- ✅ NewSaleForm.tsx corrigé pour inclure photoTicket dans la soumission
- ✅ API routes.ts corrigé avec mapping automatique des photos
- ✅ **Production Cleanup**: Removed all unnecessary deployment files and debug code
- ✅ **Clean Architecture**: Simplified to essential files only (Dockerfile, docker-compose.yml, init.sql)
- ✅ **Debug Removal**: All debug logs and temporary files cleaned for production
- ✅ RegisFlow prêt pour déploiement en production avec toutes les fonctionnalités
### Production-Ready Excel Export with Images (January 11, 2025)
- ✅ **CRITICAL FIX**: Excel export completely rebuilt with ExcelJS supporting embedded images
- ✅ **Images in Excel**: Photos now directly embedded in Excel cells within the "Photos" worksheet
- ✅ **Schema Consistency**: Corrected all snake_case/camelCase mappings between database and frontend
- ✅ **Database Integrity**: Full production readiness verification completed
- ✅ **Data Validation**: 15 sales, 17 products, 5 users - all data integrity checks passed
- ✅ **Security Verified**: Default admin password changed, proper role distribution confirmed
- ✅ **Production Ready**: All export functionalities operational with real database photos
- ✅ **Excel Images**: Photos from sales (recto, verso, ticket) properly displayed in Excel export files
### Production Deployment System 2025 Update (January 19, 2025)
- ✅ **Complete Docker Configuration Overhaul** : Updated to Node.js 20 and PostgreSQL 16
- ✅ **Enhanced Security** : Implemented non-root containers, security constraints, and resource limits
- ✅ **Multi-stage Dockerfile** : Optimized build process with separate build and production stages
- ✅ **Advanced Health Checks** : Comprehensive monitoring with detailed status endpoints
- ✅ **Production Environment** : Created `.env.production.example` with all security variables
- ✅ **Automated Deployment** : Enhanced docker-entrypoint script with improved error handling
- ✅ **Resource Management** : Memory and CPU limits for optimal server performance
- ✅ **Network Security** : Updated to use nginx_default network for reverse proxy integration
- ✅ **Comprehensive Documentation** : Created DEPLOYMENT_GUIDE.md with complete setup instructions
- ✅ **Environment Validation** : Enhanced startup scripts with database connection verification
- ✅ **Security Hardening** : SCRAM-SHA-256 authentication, secure cookies, and session management
- ✅ **Backup Integration** : Production-ready backup system with automatic retention policies
- ✅ **Monitoring Ready** : Complete logging and health check system for production monitoring
### Docker Production Fixes & Complete Resolution (January 19, 2025)
- ✅ **CRITICAL FIX**: Resolved "docker-entrypoint.sh: No such file or directory" error
- ✅ **Script Inline**: Created start script directly in Dockerfile.simple (no file copy issues)
- ✅ **Migration Robustness**: Fixed drizzle-kit dependency and error handling
- ✅ **Graceful Degradation**: Application starts even if migrations fail
- ✅ **Auto-Creation**: Database tables created automatically on first connection
- ✅ **SSL Configuration**: Fixed PostgreSQL SSL errors with sslmode=disable for Docker
- ✅ **Backup Import Fix**: Corrected timestamp conversion in backup import functionality
- ✅ **Admin Permissions Fix**: Corrected user role 'admin' to 'administrator' for production consistency
- ✅ **Role Middleware Fix**: Updated all admin routes to use 'administrator' role instead of 'admin'
- ✅ **Frontend Role Fix**: Updated Dashboard.tsx canAccessAdmin checks to use 'administrator' role
- ✅ **Production Database Fix**: Corrected admin user role in production database to 'administrator'
- ✅ **Production Issue Identified**: Production uses separate PostgreSQL database with different admin user
- ✅ **SQL Fix Script Created**: Created fix-production-admin.sql to update admin role in production
- ✅ **Production Guide**: Created comprehensive DEPLOYMENT_GUIDE.md with troubleshooting
- ✅ **Environment Templates**: Updated .env.production.example with security defaults
- ✅ **Health Diagnostics**: Added production-ready health checks and monitoring
- ✅ **Multi-stage Build**: Optimized Dockerfile for production performance and security
### Previous Docker Issues (January 13, 2025)
- ✅ Resolved cache corruption errors during Docker deployment
- ✅ Fixed missing prod-start.js references in Dockerfile and entrypoint scripts
- ✅ Simplified Docker build process to use dist/index.js directly
- ✅ Eliminated temporary compatibility scripts after project cleanup
- ✅ Fixed Node.js 18 import.meta.dirname undefined error with automatic patching
- ✅ Added sed-based patch in docker-entrypoint-simple.sh for production compatibility
- ✅ Production-ready Docker configuration with Node.js 18 compatibility
### Docker Production Deployment Fix (January 9, 2025)
- ✅ Fixed "relation 'users' does not exist" error by adding drizzle-kit to production dependencies
- ✅ Added automatic database migration in docker-entrypoint-simple.sh script
- ✅ Resolved "Could not find build directory /app/public" by copying assets to correct location
- ✅ Updated Dockerfile to copy dist/public to /app/public for serveStatic compatibility
- ✅ Database tables now created automatically on first container startup
- ✅ Complete Docker deployment package ready with all fixes applied
### Portainer Deployment Fix (January 10, 2025)
- ✅ Resolved Docker image pull error "regisflow-regisflow:latest" in Portainer
- ✅ Created local image building solution with "regisflow:latest" tag
- ✅ Generated Portainer-specific stack configuration (portainer-stack.yml)
- ✅ Built automated deployment script (deploy-portainer.sh) for server setup
- ✅ Created comprehensive Portainer deployment guide (PORTAINER_GUIDE.md)
- ✅ Fixed image reference in docker-compose.yml to use local image
- ✅ Configured proper networking and volume management for Portainer stacks
### Production Deployment System (January 9, 2025)
- ✅ Multi-stage Dockerfile optimized for production with security hardening
- ✅ Production-specific Docker Compose configuration with resource limits
- ✅ PostgreSQL production configuration with performance optimizations
- ✅ Automated production deployment script with security validations
- ✅ Nginx reverse proxy configuration with SSL/TLS and security headers
- ✅ Comprehensive monitoring script for health checks, logs, and statistics
- ✅ Enhanced environment configuration with mandatory security variables
- ✅ Production-ready logging, backup retention, and data persistence
- ✅ Container security hardening with non-root user and resource limits
### Development Tools (used for project development, not runtime dependencies for deployed app)
- `tsx`: TypeScript execution
- `esbuild`: JavaScript bundler
- `vite`: Development server and build tool
+10 -27
View File
@@ -1,6 +1,4 @@
import { db } from './db';
import { sales } from '@shared/schema';
import { lt } from 'drizzle-orm';
import { storage } from './storage';
import cron from 'node-cron';
/**
@@ -15,13 +13,9 @@ export async function purgeSalesData() {
console.log(`🗑️ Démarrage purge des ventes antérieures au ${cutoffDate.toLocaleDateString('fr-FR')}`);
// Supprimer les ventes antérieures à la date limite
const result = await db
.delete(sales)
.where(lt(sales.timestamp, cutoffDate))
.returning({ id: sales.id });
const deletedCount = result.length;
// Use storage's purge method
const result = await (storage as any).purgeOldSales();
const deletedCount = result.deletedCount;
if (deletedCount > 0) {
console.log(`✅ Purge terminée : ${deletedCount} vente(s) supprimée(s)`);
@@ -51,25 +45,14 @@ export async function purgeSalesData() {
*/
export async function getPurgeStats() {
try {
const cutoffDate = new Date();
cutoffDate.setMonth(cutoffDate.getMonth() - 19);
// Compter les ventes qui seraient supprimées
const oldSales = await db
.select({ id: sales.id })
.from(sales)
.where(lt(sales.timestamp, cutoffDate));
// Compter le total des ventes
const totalSales = await db
.select({ id: sales.id })
.from(sales);
// Use storage's stats method
const stats = await (storage as any).getPurgeStats();
return {
totalSales: totalSales.length,
oldSales: oldSales.length,
cutoffDate: cutoffDate.toISOString(),
purgeEligible: oldSales.length > 0
totalSales: stats.totalSales,
oldSales: stats.oldSales,
cutoffDate: stats.cutoffDate,
purgeEligible: stats.oldSales > 0
};
} catch (error) {
+7 -14
View File
@@ -8,20 +8,13 @@ if (!process.env.DATABASE_URL) {
);
}
// Configuration de connexion avec SSL désactivé pour Docker local
const connectionConfig = {
// Configuration pour production avec DATABASE_URL
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: false // Désactiver SSL pour production Docker locale
};
// Si DATABASE_URL contient déjà sslmode=disable, ne pas forcer SSL
if (process.env.DATABASE_URL?.includes('sslmode=disable')) {
connectionConfig.ssl = false;
} else if (process.env.NODE_ENV === 'production' && !process.env.DATABASE_URL?.includes('localhost')) {
// Activer SSL uniquement pour production externe (pas Docker local)
connectionConfig.ssl = { rejectUnauthorized: false };
}
export const pool = new Pool(connectionConfig);
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
max: 20, // Maximum number of clients in the pool
idleTimeoutMillis: 30000, // Close idle clients after 30 seconds
connectionTimeoutMillis: 2000, // Return an error after 2 seconds if connection cannot be established
});
export const db = drizzle(pool, { schema });
+21 -10
View File
@@ -3,6 +3,7 @@ import { createServer, type Server } from "http";
import { storage } from "./storage";
import session from "express-session";
import connectPg from "connect-pg-simple";
import MemoryStore from "memorystore";
import { loginSchema, insertUserSchema, insertStoreSchema, insertSaleSchema } from "@shared/schema";
import { z } from "zod";
import { createAutomaticBackup, getBackupStats } from "./backup-scheduler";
@@ -48,13 +49,11 @@ declare global {
}
export async function registerRoutes(app: Express): Promise<Server> {
// Session configuration with PostgreSQL store
const pgStore = connectPg(session);
// Use memory store for sessions when database is not available
const MemStoreClass = MemoryStore(session);
app.use(session({
store: new pgStore({
conString: process.env.DATABASE_URL,
createTableIfMissing: false,
tableName: 'sessions',
store: new MemStoreClass({
checkPeriod: 86400000 // prune expired entries every 24h
}),
secret: process.env.SESSION_SECRET || 'fireworks-secret-key-2024',
resave: false,
@@ -198,7 +197,18 @@ export async function registerRoutes(app: Express): Promise<Server> {
app.get('/api/stores', requireAuth, async (req, res) => {
try {
const user = await storage.getUser(req.session.userId!);
if (!user || !user.storeId) {
if (!user) {
return res.status(404).json({ error: "User not found" });
}
// Administrators can access all stores
if (user.role === 'administrator') {
const allStores = await storage.getAllStores();
return res.json(allStores);
}
// Non-administrators only access their assigned store
if (!user.storeId) {
return res.status(404).json({ error: "User store not found" });
}
@@ -232,7 +242,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
targetStoreId = parseInt(storeId as string);
// For non-admin users, verify they can only access their own store
if (user.role !== 'admin' && targetStoreId !== user.storeId) {
if (user.role !== 'administrator' && targetStoreId !== user.storeId) {
return res.status(403).json({ error: "Access denied to this store" });
}
} else {
@@ -264,7 +274,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Determine which store to use for the sale
let targetStoreId: number;
if (user.role === 'admin' && req.body.storeId) {
if (user.role === 'administrator' && req.body.storeId) {
// Admin can create sales for any store
targetStoreId = req.body.storeId;
} else {
@@ -312,6 +322,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Parse and validate products
const validatedProducts = products.map((product: any) => ({
saleId: 0, // Will be set by storage layer
typeArticle: product.typeArticle,
categorie: product.categorie,
quantite: parseInt(product.quantite),
@@ -329,7 +340,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
app.delete('/api/sales/:id', requireRole(['admin', 'manager']), async (req, res) => {
app.delete('/api/sales/:id', requireRole(['administrator', 'manager']), async (req, res) => {
try {
const saleId = parseInt(req.params.id);
await storage.deleteSale(saleId);
+308 -1
View File
@@ -375,4 +375,311 @@ export class DatabaseStorage implements IStorage {
}
}
export const storage = new DatabaseStorage();
class MemStorage implements IStorage {
private users: User[] = [];
private stores: Store[] = [];
private sales: Sale[] = [];
private saleProducts: SaleProduct[] = [];
private nextUserId = 1;
private nextStoreId = 1;
private nextSaleId = 1;
private nextSaleProductId = 1;
// User operations
async getUser(id: number): Promise<User | undefined> {
return this.users.find(u => u.id === id);
}
async getUserByUsername(username: string): Promise<User | undefined> {
return this.users.find(u => u.username === username);
}
async createUser(userData: InsertUser): Promise<User> {
// Hash password if provided
if (userData.password) {
userData.password = await this.hashPassword(userData.password);
}
// Convert empty email to null to avoid unique constraint issues
if (userData.email === "") {
userData.email = null;
}
const user: User = {
...userData,
id: this.nextUserId++,
email: userData.email || null,
isActive: userData.isActive || true,
updatedAt: null,
firstName: userData.firstName || null,
lastName: userData.lastName || null,
storeId: userData.storeId || null,
createdAt: new Date(),
};
this.users.push(user);
return user;
}
async updateUser(id: number, userData: Partial<InsertUser>): Promise<User | undefined> {
const userIndex = this.users.findIndex(u => u.id === id);
if (userIndex === -1) return undefined;
// Hash password if provided
if (userData.password) {
userData.password = await this.hashPassword(userData.password);
}
// Convert empty email to null to avoid unique constraint issues
if (userData.email === "") {
userData.email = null;
}
this.users[userIndex] = { ...this.users[userIndex], ...userData };
return this.users[userIndex];
}
async getAllUsers(): Promise<User[]> {
return [...this.users].sort((a, b) => (b.createdAt?.getTime() || 0) - (a.createdAt?.getTime() || 0));
}
async deleteUser(id: number): Promise<void> {
this.users = this.users.filter(u => u.id !== id);
}
// Store operations
async getStore(id: number): Promise<Store | undefined> {
return this.stores.find(s => s.id === id);
}
async createStore(storeData: InsertStore): Promise<Store> {
const store: Store = {
...storeData,
id: this.nextStoreId++,
address: storeData.address || null,
phone: storeData.phone || null,
email: storeData.email || null,
isActive: storeData.isActive || true,
updatedAt: null,
createdAt: new Date(),
};
this.stores.push(store);
return store;
}
async getAllStores(): Promise<Store[]> {
return [...this.stores].sort((a, b) => (b.createdAt?.getTime() || 0) - (a.createdAt?.getTime() || 0));
}
async updateStore(id: number, storeData: Partial<InsertStore>): Promise<Store | undefined> {
const storeIndex = this.stores.findIndex(s => s.id === id);
if (storeIndex === -1) return undefined;
this.stores[storeIndex] = { ...this.stores[storeIndex], ...storeData };
return this.stores[storeIndex];
}
async deleteStore(id: number): Promise<void> {
this.stores = this.stores.filter(s => s.id !== id);
}
// Sale operations
async getSale(id: number): Promise<Sale | undefined> {
return this.sales.find(s => s.id === id);
}
async createSaleWithProducts(sale: InsertSale, products: InsertSaleProduct[]): Promise<Sale> {
const newSale: Sale = {
...sale,
id: this.nextSaleId++,
timestamp: new Date(),
lieuNaissance: sale.lieuNaissance || null,
autoriteDelivrance: sale.autoriteDelivrance || null,
// Map typeIdentite to documentType field for historical reasons
documentType: sale.typeIdentite || null,
// Map numeroIdentite to documentNumber field
documentNumber: sale.numeroIdentite || null,
// Ensure adresse is properly mapped
adresse: sale.adresse || null,
photoRecto: sale.photoRecto || null,
photoVerso: sale.photoVerso || null,
photo_ticket: sale.photo_ticket || null,
};
this.sales.push(newSale);
// Create sale products
for (const product of products) {
const saleProduct: SaleProduct = {
...product,
id: this.nextSaleProductId++,
saleId: newSale.id,
createdAt: null,
};
this.saleProducts.push(saleProduct);
}
return newSale;
}
async getSalesByStore(storeId: number, startDate?: string, endDate?: string): Promise<(Sale & { products: SaleProduct[] })[]> {
let filteredSales = this.sales.filter(s => s.storeId === storeId);
if (startDate) {
const start = new Date(startDate);
filteredSales = filteredSales.filter(s => s.timestamp && s.timestamp >= start);
}
if (endDate) {
const end = new Date(endDate);
end.setHours(23, 59, 59, 999); // Include the entire end date
filteredSales = filteredSales.filter(s => s.timestamp && s.timestamp <= end);
}
// Add products to each sale
const salesWithProducts = filteredSales.map(sale => ({
...sale,
products: this.saleProducts.filter(p => p.saleId === sale.id)
}));
return salesWithProducts.sort((a, b) => (b.timestamp?.getTime() || 0) - (a.timestamp?.getTime() || 0));
}
async getSaleWithProducts(id: number): Promise<(Sale & { products: SaleProduct[] }) | undefined> {
const sale = await this.getSale(id);
if (!sale) return undefined;
const products = this.saleProducts.filter(sp => sp.saleId === id);
return { ...sale, products };
}
async deleteSale(id: number): Promise<void> {
this.saleProducts = this.saleProducts.filter(sp => sp.saleId !== id);
this.sales = this.sales.filter(s => s.id !== id);
}
// Auth operations
async authenticateUser(username: string, password: string): Promise<User | null> {
const user = await this.getUserByUsername(username);
if (!user || !user.isActive) {
return null;
}
const isValid = await bcrypt.compare(password, user.password);
return isValid ? user : null;
}
async hashPassword(password: string): Promise<string> {
return await bcrypt.hash(password, 12);
}
// Check if admin still uses default password
async isUsingDefaultPassword(): Promise<boolean> {
const adminUser = await this.getUserByUsername('admin');
if (!adminUser) {
return false;
}
// Check if the stored password hash matches the default password "admin123"
return await bcrypt.compare('admin123', adminUser.password);
}
// Initialize default admin user and store if none exist
async initializeDefaults(): Promise<{ defaultAdminCredentials?: { username: string; password: string } }> {
const existingUsers = await this.getAllUsers();
if (existingUsers.length === 0) {
// Create default store
const defaultStore = await this.createStore({
name: "Magasin Principal",
address: "À définir",
isActive: true,
});
// Create default admin user
const defaultPassword = "admin123";
const adminUser = await this.createUser({
username: "admin",
password: defaultPassword,
email: "admin@example.com",
firstName: "Administrateur",
lastName: "Principal",
role: "administrator",
storeId: defaultStore.id,
isActive: true,
});
return {
defaultAdminCredentials: {
username: "admin",
password: defaultPassword
}
};
}
return {};
}
}
// Add methods for backup and purge functionality
class MemStorageWithBackup extends MemStorage {
// Backup/export functionality
async exportData() {
return {
users: this.users.map(u => ({ ...u, password: undefined })), // Exclude passwords
stores: this.stores,
sales: this.sales,
saleProducts: this.saleProducts
};
}
// Import/restore functionality
async importData(data: any) {
if (data.users) this.users = data.users;
if (data.stores) this.stores = data.stores;
if (data.sales) this.sales = data.sales;
if (data.saleProducts) this.saleProducts = data.saleProducts;
// Update counters to avoid ID conflicts
this.nextUserId = Math.max(...this.users.map(u => u.id), 0) + 1;
this.nextStoreId = Math.max(...this.stores.map(s => s.id), 0) + 1;
this.nextSaleId = Math.max(...this.sales.map(s => s.id), 0) + 1;
this.nextSaleProductId = Math.max(...this.saleProducts.map(sp => sp.id), 0) + 1;
}
// Purge old sales data (19 months+)
async purgeOldSales(): Promise<{ deletedCount: number }> {
const cutoffDate = new Date();
cutoffDate.setMonth(cutoffDate.getMonth() - 19);
const oldSalesCount = this.sales.filter(s => s.timestamp && s.timestamp < cutoffDate).length;
// Remove old sales and their products
const oldSaleIds = this.sales
.filter(s => s.timestamp && s.timestamp < cutoffDate)
.map(s => s.id);
this.sales = this.sales.filter(s => !s.timestamp || s.timestamp >= cutoffDate);
this.saleProducts = this.saleProducts.filter(sp => !oldSaleIds.includes(sp.saleId));
return { deletedCount: oldSalesCount };
}
// Get purge statistics
async getPurgeStats() {
const cutoffDate = new Date();
cutoffDate.setMonth(cutoffDate.getMonth() - 19);
const totalSales = this.sales.length;
const oldSales = this.sales.filter(s => s.timestamp && s.timestamp < cutoffDate).length;
return {
totalSales,
oldSales,
cutoffDate: cutoffDate.toISOString()
};
}
}
// Use PostgreSQL storage in production, in-memory for development
export const storage = process.env.NODE_ENV === 'production'
? new DatabaseStorage()
: new MemStorageWithBackup();
+43
View File
@@ -74,6 +74,13 @@ export const sales = pgTable("sales", {
autoriteDelivrance: varchar("autorite_delivrance", { length: 255 }),
dateDelivrance: varchar("date_delivrance", { length: 10 }).notNull(),
// Adresse - ajout du champ manquant
adresse: text("adresse"),
// Champs legacy pour compatibilité historique
documentType: varchar("document_type", { length: 50 }),
documentNumber: varchar("document_number", { length: 100 }),
// Photos
photo_recto: text("photo_recto"),
photo_verso: text("photo_verso"),
@@ -91,6 +98,42 @@ export const saleProducts = pgTable("sale_products", {
createdAt: timestamp("created_at").defaultNow(),
});
// Customer Orders table
export const customerOrders = pgTable("customer_orders", {
id: serial("id").primaryKey(),
storeId: integer("store_id").references(() => stores.id).notNull(),
userId: integer("user_id").references(() => users.id).notNull(),
timestamp: timestamp("timestamp").defaultNow(),
// Client information
nom: varchar("nom", { length: 255 }).notNull(),
prenom: varchar("prenom", { length: 255 }).notNull(),
telephone: varchar("telephone", { length: 20 }),
email: varchar("email", { length: 255 }),
adresse: text("adresse"),
// Order details
statut: varchar("statut", { length: 50 }).notNull().default("En attente de commande"),
dateCommande: timestamp("date_commande"),
dateLivraison: timestamp("date_livraison"),
montant: integer("montant"), // En centimes
commentaire: text("commentaire"), // Champ commentaire principal
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});
// Customer Order Products table
export const customerOrderProducts = pgTable("customer_order_products", {
id: serial("id").primaryKey(),
orderId: integer("order_id").references(() => customerOrders.id, { onDelete: "cascade" }).notNull(),
produit: varchar("produit", { length: 255 }).notNull(),
quantite: integer("quantite").notNull().default(1),
prixUnitaire: integer("prix_unitaire"), // En centimes
total: integer("total"), // En centimes
createdAt: timestamp("created_at").defaultNow(),
});
// Relations
export const storesRelations = relations(stores, ({ many }) => ({
users: many(users),
-191
View File
@@ -1,191 +0,0 @@
#!/bin/bash
echo "🚀 RegisFlow Docker Deployment Test"
echo "==================================="
# Fonction de test des prérequis
check_prerequisites() {
echo "📋 Checking prerequisites..."
# Docker
if ! command -v docker &> /dev/null; then
echo "❌ Docker not found. Please install Docker."
exit 1
fi
# Docker Compose
if ! command -v docker-compose &> /dev/null; then
echo "❌ Docker Compose not found. Please install Docker Compose."
exit 1
fi
echo "✅ Docker and Docker Compose are available"
}
# Fonction de validation des fichiers
check_files() {
echo "📁 Checking required files..."
required_files=(
"Dockerfile"
"docker-compose.yml"
"docker-entrypoint.sh"
".env.production.example"
"package.json"
)
for file in "${required_files[@]}"; do
if [[ ! -f "$file" ]]; then
echo "❌ Missing required file: $file"
exit 1
fi
done
echo "✅ All required files are present"
}
# Test de build Docker
test_docker_build() {
echo "🔨 Testing Docker build..."
if docker-compose build --no-cache regisflow; then
echo "✅ Docker build successful"
else
echo "❌ Docker build failed"
exit 1
fi
}
# Test de configuration
test_configuration() {
echo "⚙️ Testing configuration..."
if [[ ! -f ".env.production" ]]; then
echo "⚠️ .env.production not found, creating from example..."
cp .env.production.example .env.production
echo "📝 Please edit .env.production with your secure credentials!"
fi
# Vérifier que les variables critiques ne sont pas par défaut
if grep -q "CHANGEME" .env.production; then
echo "⚠️ WARNING: Default passwords detected in .env.production"
echo " Please change POSTGRES_PASSWORD and SESSION_SECRET"
fi
echo "✅ Configuration checked"
}
# Test de démarrage rapide
test_startup() {
echo "🚦 Testing container startup..."
# Nettoyer d'abord
docker-compose down -v 2>/dev/null
# Démarrer en mode détaché
if docker-compose up -d; then
echo "✅ Containers started successfully"
# Attendre que l'application soit prête
echo "⏳ Waiting for application to be ready..."
timeout=60
while ! curl -s http://localhost:5000/health >/dev/null && [ $timeout -gt 0 ]; do
echo " Waiting... ($timeout seconds left)"
sleep 2
timeout=$((timeout-2))
done
if [ $timeout -gt 0 ]; then
echo "✅ Application is responding"
echo "🏥 Health check: $(curl -s http://localhost:5000/health | grep -o '"status":"[^"]*"')"
else
echo "❌ Application startup timeout"
echo "📋 Container logs:"
docker-compose logs --tail=20 regisflow
exit 1
fi
else
echo "❌ Container startup failed"
exit 1
fi
}
# Test de connexion base de données
test_database() {
echo "🗄️ Testing database connection..."
if docker exec regisflow-db pg_isready -U regisflow -q; then
echo "✅ Database is accessible"
# Tester les tables
tables=$(docker exec regisflow-db psql -U regisflow -t -c "\dt" | wc -l)
echo "📊 Database has $tables tables"
else
echo "❌ Database connection failed"
exit 1
fi
}
# Fonction de nettoyage
cleanup() {
echo "🧹 Cleaning up test containers..."
docker-compose down -v
echo "✅ Cleanup completed"
}
# Test complet
run_full_test() {
check_prerequisites
check_files
test_configuration
test_docker_build
test_startup
test_database
echo ""
echo "🎉 ALL TESTS PASSED!"
echo "===================="
echo "RegisFlow is ready for production deployment"
echo ""
echo "Next steps:"
echo "1. Edit .env.production with secure credentials"
echo "2. Run: docker-compose up -d"
echo "3. Access: http://localhost:5000"
echo "4. Monitor: docker-compose logs -f"
echo ""
cleanup
}
# Exécuter le test ou afficher l'aide
case "${1:-test}" in
"test")
run_full_test
;;
"build")
check_prerequisites
check_files
test_docker_build
;;
"health")
curl -s http://localhost:5000/health | python3 -m json.tool || echo "Application not running"
;;
"cleanup")
cleanup
;;
"help")
echo "Usage: $0 [test|build|health|cleanup|help]"
echo " test - Run full deployment test (default)"
echo " build - Test Docker build only"
echo " health - Check application health"
echo " cleanup - Clean up test containers"
echo " help - Show this help"
;;
*)
echo "Unknown option: $1"
echo "Use '$0 help' for usage information"
exit 1
;;
esac