From 32042eb66824b1c230427de1513d217f91782905 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Thu, 10 Jul 2025 14:56:14 +0000 Subject: [PATCH] Enable application deployment by building directly from source code Updates docker-compose and portainer-stack files to build the regisflow image and adds PORTAINER_SOLUTION.md, and portainer-build-stack.yml. Replit-Commit-Author: Agent Replit-Commit-Session-Id: dec54137-d5a2-4406-8ad4-5f2f9fb110bf Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/6766644a-420b-4242-91df-d40e1146cc68.jpg --- PORTAINER_SOLUTION.md | 153 ++++++++++++++++++++++++ attached_assets/image_1752159307475.png | Bin 0 -> 23432 bytes docker-compose.yml | 4 +- portainer-build-stack.yml | 75 ++++++++++++ portainer-stack.yml | 7 +- 5 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 PORTAINER_SOLUTION.md create mode 100644 attached_assets/image_1752159307475.png create mode 100644 portainer-build-stack.yml diff --git a/PORTAINER_SOLUTION.md b/PORTAINER_SOLUTION.md new file mode 100644 index 0000000..34d41df --- /dev/null +++ b/PORTAINER_SOLUTION.md @@ -0,0 +1,153 @@ +# Solution Portainer pour RegisFlow + +## Problème identifié +Portainer ne peut pas télécharger l'image `regisflow` car elle n'existe pas sur Docker Hub. Il faut construire l'image depuis le code source. + +## Solution 1: Stack avec build automatique (RECOMMANDÉE) + +### Étapes dans Portainer: + +1. **Allez dans "Stacks"** +2. **Cliquez sur "Add stack"** +3. **Nom de la stack**: `regisflow` +4. **Méthode de build**: Repository +5. **Repository URL**: Collez l'URL de votre dépôt Git ou utilisez "Upload" +6. **Compose file**: Copiez le contenu de `portainer-build-stack.yml` +7. **Cliquez sur "Deploy the stack"** + +### Contenu à copier dans Portainer: + +```yaml +version: '3.8' + +services: + regisflow-db: + image: postgres:15-alpine + container_name: regisflow-db + environment: + POSTGRES_DB: regisflow + POSTGRES_USER: regisflow + POSTGRES_PASSWORD: RegisFlow2024!PostgreSQL + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5433:5432" + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U regisflow -d regisflow"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + + regisflow: + build: + context: . + dockerfile: Dockerfile + target: production + container_name: regisflow-app + depends_on: + regisflow-db: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 5000 + DATABASE_URL: postgresql://regisflow:RegisFlow2024!PostgreSQL@regisflow-db:5432/regisflow + SESSION_SECRET: RegisFlow2024SessionSecretKey1234567890ABCDEF + TZ: Europe/Paris + volumes: + - backup_data:/app/backups + - logs_data:/app/logs + ports: + - "5000:5000" + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1"] + interval: 30s + timeout: 15s + retries: 3 + start_period: 90s + +volumes: + postgres_data: + backup_data: + logs_data: +``` + +## Solution 2: Construction manuelle puis image + +Si la solution 1 ne fonctionne pas: + +### Étape A: Sur votre serveur +```bash +# Téléchargez les fichiers RegisFlow sur votre serveur +# Puis construisez l'image: +docker build -t regisflow:latest . + +# Vérifiez que l'image existe: +docker images | grep regisflow +``` + +### Étape B: Dans Portainer +Utilisez ensuite le fichier `portainer-stack.yml` original avec `image: regisflow:latest` + +## Solution 3: Docker Hub personnel (avancée) + +Si vous avez un compte Docker Hub: + +```bash +# Taguez l'image avec votre nom d'utilisateur +docker tag regisflow:latest votre-username/regisflow:latest + +# Poussez vers Docker Hub +docker push votre-username/regisflow:latest +``` + +Puis utilisez `votre-username/regisflow:latest` dans Portainer. + +## Vérification du déploiement + +### Dans Portainer: +1. Vérifiez que la stack est "Running" +2. Consultez les logs des conteneurs +3. Vérifiez les health checks + +### Accès à l'application: +- URL: `http://votre-serveur:5000` +- Login: `admin` +- Mot de passe: `admin123` + +### Tests de connectivité: +```bash +# Test de santé de l'application +curl http://localhost:5000/health + +# Vérification PostgreSQL +docker exec regisflow-db pg_isready -U regisflow -d regisflow +``` + +## Résolution des problèmes courants + +### Stack en échec de build: +1. Vérifiez que tous les fichiers sont présents +2. Consultez les logs de build dans Portainer +3. Vérifiez les droits d'accès au Dockerfile + +### Conteneur qui ne démarre pas: +1. Vérifiez les logs du conteneur +2. Vérifiez les variables d'environnement +3. Attendez que PostgreSQL soit prêt (health check) + +### Application inaccessible: +1. Vérifiez que le port 5000 est ouvert +2. Vérifiez les logs de l'application +3. Testez le health check: `/health` + +## Points importants + +✅ **Utilisez la Solution 1 avec build automatique** +✅ **Tous les fichiers sources doivent être disponibles pour Portainer** +✅ **PostgreSQL démarre en premier (health check)** +✅ **Volumes persistants pour les données** +✅ **Réseau dédié pour la communication inter-conteneurs** +✅ **Health checks configurés pour surveillance automatique** \ No newline at end of file diff --git a/attached_assets/image_1752159307475.png b/attached_assets/image_1752159307475.png new file mode 100644 index 0000000000000000000000000000000000000000..564e9bca2b564939843d25fa6b43cf3af29440a3 GIT binary patch literal 23432 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 H(>MPCjvc}( literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml index 2071339..33f3430 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,9 @@ services: # Application RegisFlow regisflow: - image: regisflow:latest + build: + context: . + target: production container_name: regisflow-app depends_on: regisflow-db: diff --git a/portainer-build-stack.yml b/portainer-build-stack.yml new file mode 100644 index 0000000..39f85c3 --- /dev/null +++ b/portainer-build-stack.yml @@ -0,0 +1,75 @@ +version: '3.8' + +# Configuration Stack Portainer pour RegisFlow avec build automatique +# Cette version construit l'image automatiquement depuis le code source +services: + # Base de données PostgreSQL + regisflow-db: + image: postgres:15-alpine + container_name: regisflow-db + environment: + POSTGRES_DB: regisflow + POSTGRES_USER: regisflow + POSTGRES_PASSWORD: RegisFlow2024!PostgreSQL + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5433:5432" + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U regisflow -d regisflow"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + networks: + - regisflow-net + + # Application RegisFlow - Construction automatique + regisflow: + build: + context: . + dockerfile: Dockerfile + target: production + container_name: regisflow-app + depends_on: + regisflow-db: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 5000 + DATABASE_URL: postgresql://regisflow:RegisFlow2024!PostgreSQL@regisflow-db:5432/regisflow + SESSION_SECRET: RegisFlow2024SessionSecretKey1234567890ABCDEF + TZ: Europe/Paris + SECURE_COOKIES: false + DATA_RETENTION_MONTHS: 19 + BACKUP_RETENTION_DAYS: 90 + MAX_BACKUP_COUNT: 20 + volumes: + - backup_data:/app/backups + - logs_data:/app/logs + ports: + - "5000:5000" + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1"] + interval: 30s + timeout: 15s + retries: 3 + start_period: 90s + networks: + - regisflow-net + +# Volumes persistants +volumes: + postgres_data: + driver: local + backup_data: + driver: local + logs_data: + driver: local + +# Réseau dédié +networks: + regisflow-net: + driver: bridge \ No newline at end of file diff --git a/portainer-stack.yml b/portainer-stack.yml index 871e920..a41466e 100644 --- a/portainer-stack.yml +++ b/portainer-stack.yml @@ -23,9 +23,12 @@ services: retries: 5 start_period: 30s - # Application RegisFlow - Image construite localement + # Application RegisFlow - Construction depuis le code source regisflow: - image: regisflow:latest + build: + context: . + dockerfile: Dockerfile + target: production container_name: regisflow-app depends_on: regisflow-db: