refactor: structure docker-compose/ + scripts deploy.sh et setup.sh
This commit is contained in:
+7
-86
@@ -1,86 +1,7 @@
|
|||||||
# ═══════════════════════════════════════════════════════════════
|
# Ce fichier est déprécié.
|
||||||
# Imago — Configuration
|
# Le template de configuration se trouve désormais dans :
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# docker-compose/.env.example
|
||||||
# Copiez ce fichier en .env et ajustez les valeurs.
|
#
|
||||||
# cp .env.example .env
|
# Pour déployer :
|
||||||
|
# 1. sudo ./scripts/setup.sh (une seule fois, prépare le serveur)
|
||||||
# ── Application ──────────────────────────────────────────────
|
# 2. ./scripts/deploy.sh (déploiement ou mise à jour)
|
||||||
APP_NAME=Imago
|
|
||||||
APP_VERSION=2.0.0
|
|
||||||
DEBUG=false
|
|
||||||
SECRET_KEY=changez-moi-par-une-valeur-longue-et-aleatoire
|
|
||||||
|
|
||||||
# ── Serveur (pour dev seulement) ──────────────────────────────
|
|
||||||
# HOST=0.0.0.0
|
|
||||||
# PORT=8000
|
|
||||||
|
|
||||||
# ── Base de données ──────────────────────────────────────────
|
|
||||||
# SQLite (dev): sqlite+aiosqlite:///./data/imago.db
|
|
||||||
PostgreSQL: postgresql+asyncpg://user:pass@host:5432/imago
|
|
||||||
# DATABASE_URL=sqlite+aiosqlite:///./data/imago.db
|
|
||||||
|
|
||||||
# ── Stockage ─────────────────────────────────────────────────
|
|
||||||
# local (fichiers sur disque) ou s3 (MinIO / AWS S3 / Cloudflare R2)
|
|
||||||
STORAGE_BACKEND=local
|
|
||||||
UPLOAD_DIR=/app/data/uploads
|
|
||||||
THUMBNAILS_DIR=/app/data/thumbnails
|
|
||||||
MAX_UPLOAD_SIZE_MB=50
|
|
||||||
SIGNED_URL_SECRET=changez-moi-en-prod
|
|
||||||
|
|
||||||
# ── Stockage S3 (si STORAGE_BACKEND=s3) ──────────────────────
|
|
||||||
S3_BUCKET=imago
|
|
||||||
S3_PREFIX=
|
|
||||||
S3_REGION=us-east-1
|
|
||||||
S3_ENDPOINT_URL=http://localhost:9000
|
|
||||||
S3_ENDPOINT_PUBLIC=http://localhost:9000
|
|
||||||
S3_ACCESS_KEY=minioadmin
|
|
||||||
S3_SECRET_KEY=minioadmin
|
|
||||||
SIGNED_URL_SECRET=changez-moi-en-prod
|
|
||||||
|
|
||||||
# ── AI ───────────────────────────────────────────────────────
|
|
||||||
AI_ENABLED=true
|
|
||||||
AI_PROVIDER=openrouter
|
|
||||||
|
|
||||||
# Google Gemini
|
|
||||||
GEMINI_API_KEY=
|
|
||||||
GEMINI_MODEL=gemini-3.1-pro-preview
|
|
||||||
GEMINI_MAX_TOKENS=1024
|
|
||||||
|
|
||||||
# OpenRouter
|
|
||||||
OPENROUTER_API_KEY=
|
|
||||||
OPENROUTER_MODEL=qwen/qwen2.5-vl-72b-instruct
|
|
||||||
|
|
||||||
# AI Comportement
|
|
||||||
AI_TAGS_MIN=5
|
|
||||||
AI_TAGS_MAX=10
|
|
||||||
AI_DESCRIPTION_LANGUAGE=francais
|
|
||||||
AI_CACHE_DAYS=30
|
|
||||||
AI_REQUEST_TIMEOUT=60
|
|
||||||
AI_MAX_RETRIES=2
|
|
||||||
|
|
||||||
# ── OCR ──────────────────────────────────────────────────────
|
|
||||||
OCR_ENABLED=true
|
|
||||||
TESSERACT_CMD=/usr/bin/tesseract
|
|
||||||
OCR_LANGUAGES=fra+eng
|
|
||||||
|
|
||||||
# ── CORS ─────────────────────────────────────────────────────
|
|
||||||
# Format JSON : ["http://localhost:3000", "http://localhost:5173"]
|
|
||||||
CORS_ORIGINS=["http://localhost:3000", "http://localhost:5173"]
|
|
||||||
|
|
||||||
# ── Authentification ─────────────────────────────────────────
|
|
||||||
ADMIN_API_KEY=
|
|
||||||
JWT_SECRET_KEY=changez-moi-jwt-secret
|
|
||||||
JWT_ALGORITHM=HS256
|
|
||||||
|
|
||||||
# ── Rate Limiting ────────────────────────────────────────────
|
|
||||||
RATE_LIMIT_FREE_UPLOAD=20
|
|
||||||
RATE_LIMIT_FREE_AI=50
|
|
||||||
RATE_LIMIT_STANDARD_UPLOAD=100
|
|
||||||
RATE_LIMIT_STANDARD_AI=200
|
|
||||||
RATE_LIMIT_PREMIUM_UPLOAD=500
|
|
||||||
RATE_LIMIT_PREMIUM_AI=1000
|
|
||||||
# Redis pour persistence des compteurs (optionnel, mémoire par défaut)
|
|
||||||
# Format: redis://host:***@RATE_LIMIT_STORAGE_URL=
|
|
||||||
|
|
||||||
# ── Redis + ARQ Worker ───────────────────────────────────────
|
|
||||||
REDIS_URL=redis://localhost:***@PIPELINE_TIMEOUT=300
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
version: '3.9'
|
|
||||||
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: imago
|
|
||||||
POSTGRES_PASSWORD: imago
|
|
||||||
POSTGRES_DB: imago
|
|
||||||
ports:
|
|
||||||
- '5432:5432'
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD-SHELL', 'pg_isready -U imago']
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
ports:
|
|
||||||
- '6379:6379'
|
|
||||||
volumes:
|
|
||||||
- redisdata:/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'redis-cli', 'ping']
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
minio:
|
|
||||||
image: minio/minio:latest
|
|
||||||
command: server /data --console-address ':9001'
|
|
||||||
environment:
|
|
||||||
MINIO_ROOT_USER: minioadmin
|
|
||||||
MINIO_ROOT_PASSWORD: minioadmin
|
|
||||||
ports:
|
|
||||||
- '9000:9000'
|
|
||||||
- '9001:9001'
|
|
||||||
volumes:
|
|
||||||
- miniodata:/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'mc', 'ready', 'local']
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
api:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- '8000:8000'
|
|
||||||
environment:
|
|
||||||
- DATABASE_URL=postgresql+asyncpg://imago:imago@postgres:5432/imago
|
|
||||||
- REDIS_URL=redis://redis:6379/0
|
|
||||||
- STORAGE_BACKEND=s3
|
|
||||||
- S3_BUCKET=imago
|
|
||||||
- S3_ENDPOINT_URL=http://minio:9000
|
|
||||||
- S3_ENDPOINT_PUBLIC=http://localhost:9000
|
|
||||||
- S3_ACCESS_KEY=minioadmin
|
|
||||||
- S3_SECRET_KEY=minioadmin
|
|
||||||
- S3_REGION=us-east-1
|
|
||||||
- SIGNED_URL_SECRET=dev-secret-change-in-prod
|
|
||||||
- SECRET_KEY=dev-secret-change-in-prod
|
|
||||||
- TESSERACT_CMD=/usr/bin/tesseract
|
|
||||||
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
|
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
minio:
|
|
||||||
condition: service_healthy
|
|
||||||
volumes:
|
|
||||||
- uploads:/app/data/uploads
|
|
||||||
- thumbnails:/app/data/thumbnails
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
redisdata:
|
|
||||||
miniodata:
|
|
||||||
uploads:
|
|
||||||
thumbnails:
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# Imago — Configuration de production
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# Copiez ce fichier en .env dans le répertoire racine du projet :
|
||||||
|
# cp docker-compose/.env.example .env
|
||||||
|
#
|
||||||
|
# Puis éditez .env avec vos valeurs.
|
||||||
|
|
||||||
|
# ── Application ──────────────────────────────────────────────
|
||||||
|
APP_NAME=Imago
|
||||||
|
DEBUG=false
|
||||||
|
SECRET_KEY=changez-moi-par-une-valeur-longue-et-aleatoire
|
||||||
|
|
||||||
|
# ── Ports (exposés sur l'hôte) ───────────────────────────────
|
||||||
|
ADMIN_PORT=3001
|
||||||
|
BACKEND_PORT=8001
|
||||||
|
|
||||||
|
# ── Stockage ─────────────────────────────────────────────────
|
||||||
|
# "local" = fichiers sur disque
|
||||||
|
# "s3" = MinIO / AWS S3 / Cloudflare R2
|
||||||
|
STORAGE_BACKEND=local
|
||||||
|
UPLOAD_DIR=/app/data/uploads
|
||||||
|
THUMBNAILS_DIR=/app/data/thumbnails
|
||||||
|
MAX_UPLOAD_SIZE_MB=50
|
||||||
|
|
||||||
|
# Volume de stockage (défaut: volume Docker nommé "uploads"/"thumbnails")
|
||||||
|
# Pour NFS : UPLOAD_VOLUME=/NFS/IMAGO/uploads
|
||||||
|
# UPLOAD_VOLUME=
|
||||||
|
# THUMBNAIL_VOLUME=
|
||||||
|
|
||||||
|
# URLs signées pour le stockage local
|
||||||
|
SIGNED_URL_SECRET=changez-moi-en-prod
|
||||||
|
|
||||||
|
# ── Sauvegardes DB ───────────────────────────────────────────
|
||||||
|
# BACKUP_DIR=./backups
|
||||||
|
|
||||||
|
# ── AI ───────────────────────────────────────────────────────
|
||||||
|
AI_ENABLED=true
|
||||||
|
AI_PROVIDER=openrouter
|
||||||
|
OPENROUTER_API_KEY=sk-or-v1-votre-cle
|
||||||
|
OPENROUTER_MODEL=qwen/qwen2.5-vl-72b-instruct
|
||||||
|
|
||||||
|
# ── OCR ──────────────────────────────────────────────────────
|
||||||
|
OCR_ENABLED=true
|
||||||
|
TESSERACT_CMD=/usr/bin/tesseract
|
||||||
|
OCR_LANGUAGES=fra+eng
|
||||||
|
|
||||||
|
# ── CORS ─────────────────────────────────────────────────────
|
||||||
|
# Format JSON : ["http://localhost:3000", "https://votre-domaine.com"]
|
||||||
|
CORS_ORIGINS=["http://localhost:3000", "http://localhost:3001"]
|
||||||
|
|
||||||
|
# ── Authentification ─────────────────────────────────────────
|
||||||
|
ADMIN_API_KEY=
|
||||||
|
JWT_SECRET_KEY=changez-moi-jwt-secret
|
||||||
|
JWT_ALGORITHM=HS256
|
||||||
|
|
||||||
|
# ── Rate Limiting ────────────────────────────────────────────
|
||||||
|
RATE_LIMIT_FREE_UPLOAD=20
|
||||||
|
RATE_LIMIT_FREE_AI=50
|
||||||
|
RATE_LIMIT_STANDARD_UPLOAD=100
|
||||||
|
RATE_LIMIT_STANDARD_AI=200
|
||||||
|
RATE_LIMIT_PREMIUM_UPLOAD=500
|
||||||
|
RATE_LIMIT_PREMIUM_AI=1000
|
||||||
|
|
||||||
|
# ── Redis (interne Docker — ne pas changer sauf besoin spécifique)
|
||||||
|
# REDIS_URL=redis://redis:***@REDIS_URL=redis://redis:***@...
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
# Imago — Docker Compose Production
|
# =============================================================================
|
||||||
|
# Imago — Production (images pré-buildées, stockage local)
|
||||||
|
# =============================================================================
|
||||||
# Usage:
|
# Usage:
|
||||||
# 1. Copier .env.example → .env et remplir les secrets
|
# 1. Copier .env.example → .env et remplir les secrets
|
||||||
# 2. docker compose -f docker-compose.production.yml up -d
|
# 2. docker compose -f docker-compose/docker-compose.prod.yml up -d
|
||||||
#
|
#
|
||||||
# Docker Compose lit automatiquement le fichier .env du répertoire courant.
|
# Docker Compose lit automatiquement le fichier .env du répertoire courant.
|
||||||
# Les variables d'environnement sont référencées avec ${VAR} ou ${VAR:-défaut}.
|
# =============================================================================
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# ── Admin Panel (React + Nginx) ───────────────────────────
|
# ── Admin Panel (React + Nginx) ───────────────────────────
|
||||||
@@ -19,23 +21,22 @@ services:
|
|||||||
# ── API Backend ────────────────────────────────────────────
|
# ── API Backend ────────────────────────────────────────────
|
||||||
backend:
|
backend:
|
||||||
image: docker-registry.dev.home:5000/imago-backend:latest
|
image: docker-registry.dev.home:5000/imago-backend:latest
|
||||||
user: "1000:4000"
|
|
||||||
ports:
|
ports:
|
||||||
- "${BACKEND_PORT:-8001}:8000"
|
- "${BACKEND_PORT:-8001}:8000"
|
||||||
env_file: .env
|
env_file:
|
||||||
|
- .env
|
||||||
environment:
|
environment:
|
||||||
# Connexions internes Docker (hostnames fixes)
|
|
||||||
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
||||||
|
REDIS_URL: redis://redis:***@db:5432/imago
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
# Variables de l'application (lues depuis .env, défaut local)
|
|
||||||
STORAGE_BACKEND: ${STORAGE_BACKEND:-local}
|
STORAGE_BACKEND: ${STORAGE_BACKEND:-local}
|
||||||
UPLOAD_DIR: ${UPLOAD_DIR:-/app/data/uploads}
|
UPLOAD_DIR: ${UPLOAD_DIR:-/app/data/uploads}
|
||||||
THUMBNAILS_DIR: ${THUMBNAILS_DIR:-/app/data/thumbnails}
|
THUMBNAILS_DIR: ${THUMBNAILS_DIR:-/app/data/thumbnails}
|
||||||
MAX_UPLOAD_SIZE_MB: ${MAX_UPLOAD_SIZE_MB:-50}
|
MAX_UPLOAD_SIZE_MB: ${MAX_UPLOAD_SIZE_MB:-50}
|
||||||
SIGNED_URL_SECRET: ${SIGNED_URL_SECRET:-changez-moi-en-prod}
|
SIGNED_URL_SECRET: ${SIGNED_URL_SECRET:-changez-moi-en-prod}
|
||||||
volumes:
|
volumes:
|
||||||
- /NFS/IMAGO/uploads:/app/data/uploads
|
- ${UPLOAD_VOLUME:-uploads}:/app/data/uploads
|
||||||
- /NFS/IMAGO/thumbnails:/app/data/thumbnails
|
- ${THUMBNAIL_VOLUME:-thumbnails}:/app/data/thumbnails
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -46,11 +47,12 @@ services:
|
|||||||
# ── ARQ Worker ────────────────────────────────────────────
|
# ── ARQ Worker ────────────────────────────────────────────
|
||||||
worker:
|
worker:
|
||||||
image: docker-registry.dev.home:5000/imago-backend:latest
|
image: docker-registry.dev.home:5000/imago-backend:latest
|
||||||
user: "1000:4000"
|
|
||||||
command: python worker.py
|
command: python worker.py
|
||||||
env_file: .env
|
env_file:
|
||||||
|
- .env
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
||||||
|
REDIS_URL: redis://redis:***@db:5432/imago
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
STORAGE_BACKEND: ${STORAGE_BACKEND:-local}
|
STORAGE_BACKEND: ${STORAGE_BACKEND:-local}
|
||||||
UPLOAD_DIR: ${UPLOAD_DIR:-/app/data/uploads}
|
UPLOAD_DIR: ${UPLOAD_DIR:-/app/data/uploads}
|
||||||
@@ -58,8 +60,8 @@ services:
|
|||||||
MAX_UPLOAD_SIZE_MB: ${MAX_UPLOAD_SIZE_MB:-50}
|
MAX_UPLOAD_SIZE_MB: ${MAX_UPLOAD_SIZE_MB:-50}
|
||||||
SIGNED_URL_SECRET: ${SIGNED_URL_SECRET:-changez-moi-en-prod}
|
SIGNED_URL_SECRET: ${SIGNED_URL_SECRET:-changez-moi-en-prod}
|
||||||
volumes:
|
volumes:
|
||||||
- /NFS/IMAGO/uploads:/app/data/uploads
|
- ${UPLOAD_VOLUME:-uploads}:/app/data/uploads
|
||||||
- /NFS/IMAGO/thumbnails:/app/data/thumbnails
|
- ${THUMBNAIL_VOLUME:-thumbnails}:/app/data/thumbnails
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -70,14 +72,13 @@ services:
|
|||||||
# ── Base de données ───────────────────────────────────────
|
# ── Base de données ───────────────────────────────────────
|
||||||
db:
|
db:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
user: "1000:4000"
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: imago
|
POSTGRES_USER: imago
|
||||||
POSTGRES_PASSWORD: imago
|
POSTGRES_PASSWORD: imago
|
||||||
POSTGRES_DB: imago
|
POSTGRES_DB: imago
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
- /NFS/IMAGO/DB/backups:/backups
|
- ${BACKUP_DIR:-./backups}:/backups
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U imago -d imago"]
|
test: ["CMD-SHELL", "pg_isready -U imago -d imago"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
@@ -98,18 +99,8 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# ── MinIO (optionnel — utilisé seulement si STORAGE_BACKEND=s3)
|
|
||||||
# minio:
|
|
||||||
# image: minio/minio
|
|
||||||
# command: server /data --console-address ':9001'
|
|
||||||
# environment:
|
|
||||||
# MINIO_ROOT_USER: minioadmin
|
|
||||||
# MINIO_ROOT_PASSWORD: minioadmin
|
|
||||||
# volumes:
|
|
||||||
# - miniodata:/data
|
|
||||||
# restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
redisdata:
|
redisdata:
|
||||||
miniodata:
|
uploads:
|
||||||
|
thumbnails:
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Imago — Développement local (build from source, stack complet)
|
||||||
|
# =============================================================================
|
||||||
|
# Usage:
|
||||||
|
# docker compose -f docker-compose/docker-compose.yml up -d
|
||||||
|
#
|
||||||
|
# Ce compose build les images depuis le code source et démarre tous les
|
||||||
|
# services nécessaires : PostgreSQL, Redis, MinIO (S3), API, Worker.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ── Base de données ───────────────────────────────────────
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: imago
|
||||||
|
POSTGRES_PASSWORD: imago
|
||||||
|
POSTGRES_DB: imago
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- pgdata_dev:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U imago -d imago"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# ── Redis ─────────────────────────────────────────────────
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redisdata_dev:/data
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# ── MinIO (stockage S3 local) ─────────────────────────────
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: minioadmin
|
||||||
|
MINIO_ROOT_PASSWORD: minioadmin
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
volumes:
|
||||||
|
- miniodata_dev:/data
|
||||||
|
|
||||||
|
# ── API Backend ───────────────────────────────────────────
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
STORAGE_BACKEND: s3
|
||||||
|
S3_BUCKET: imago
|
||||||
|
S3_ENDPOINT_URL: http://minio:9000
|
||||||
|
S3_ENDPOINT_PUBLIC: http://localhost:9000
|
||||||
|
S3_ACCESS_KEY: minioadmin
|
||||||
|
S3_SECRET_KEY: minioadmin
|
||||||
|
S3_REGION: us-east-1
|
||||||
|
volumes:
|
||||||
|
- uploads_dev:/app/data/uploads
|
||||||
|
- thumbnails_dev:/app/data/thumbnails
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# ── ARQ Worker ────────────────────────────────────────────
|
||||||
|
worker:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
command: python worker.py
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql+asyncpg://imago:imago@db:5432/imago
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
STORAGE_BACKEND: s3
|
||||||
|
S3_BUCKET: imago
|
||||||
|
S3_ENDPOINT_URL: http://minio:9000
|
||||||
|
S3_ACCESS_KEY: minioadmin
|
||||||
|
S3_SECRET_KEY: minioadmin
|
||||||
|
S3_REGION: us-east-1
|
||||||
|
volumes:
|
||||||
|
- uploads_dev:/app/data/uploads
|
||||||
|
- thumbnails_dev:/app/data/thumbnails
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata_dev:
|
||||||
|
redisdata_dev:
|
||||||
|
miniodata_dev:
|
||||||
|
uploads_dev:
|
||||||
|
thumbnails_dev:
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# Imago — Script de déploiement automatisé
|
||||||
|
# =============================================================================
|
||||||
|
# Usage :
|
||||||
|
# ./scripts/deploy.sh # Premier déploiement
|
||||||
|
# ./scripts/deploy.sh --update # Mise à jour (pull + recréation)
|
||||||
|
# ./scripts/deploy.sh --status # Afficher l'état des conteneurs
|
||||||
|
# ./scripts/deploy.sh --logs # Logs en temps réel
|
||||||
|
#
|
||||||
|
# Prérequis : Docker + Docker Compose installés (voir scripts/setup.sh)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Couleurs ──────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# ── Configuration ─────────────────────────────────────────────
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
COMPOSE_FILE="${PROJECT_DIR}/docker-compose/docker-compose.prod.yml"
|
||||||
|
ENV_FILE="${PROJECT_DIR}/.env"
|
||||||
|
ENV_EXAMPLE="${PROJECT_DIR}/docker-compose/.env.example"
|
||||||
|
SERVICE_NAME="imago"
|
||||||
|
|
||||||
|
# ── Fonctions ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
check_prerequisites() {
|
||||||
|
info "Vérification des prérequis..."
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
err "Docker n'est pas installé. Lancez : scripts/setup.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker compose version &>/dev/null; then
|
||||||
|
err "Docker Compose (plugin) n'est pas disponible."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker info &>/dev/null; then
|
||||||
|
err "Docker ne tourne pas ou vous n'avez pas les droits."
|
||||||
|
err "Vérifiez : sudo systemctl start docker && sudo usermod -aG docker $USER"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "Docker $(docker --version | awk '{print $3}' | tr -d ',') — Docker Compose disponible"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_env() {
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
ok "Fichier .env existant"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
warn "Fichier .env absent — création depuis le template..."
|
||||||
|
if [[ ! -f "$ENV_EXAMPLE" ]]; then
|
||||||
|
err "Template introuvable : $ENV_EXAMPLE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||||||
|
warn "╔══════════════════════════════════════════════════════════╗"
|
||||||
|
warn "║ Fichier .env créé. ÉDITEZ-LE avant de continuer : ║"
|
||||||
|
warn "║ nano ${ENV_FILE} ║"
|
||||||
|
warn "║ ║"
|
||||||
|
warn "║ Variables OBLIGATOIRES à renseigner : ║"
|
||||||
|
warn "║ SECRET_KEY — clé secrète aléatoire ║"
|
||||||
|
warn "║ SIGNED_URL_SECRET — clé pour URLs signées ║"
|
||||||
|
warn "║ OPENROUTER_API_KEY — clé API OpenRouter ║"
|
||||||
|
warn "║ ADMIN_API_KEY — (optionnel) master key admin ║"
|
||||||
|
warn "╚══════════════════════════════════════════════════════════╝"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
read -rp "Avez-vous édité le fichier .env ? (o/N) " confirm
|
||||||
|
if [[ ! "$confirm" =~ ^[oO]$ ]]; then
|
||||||
|
err "Déploiement annulé. Éditez .env puis relancez."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_directories() {
|
||||||
|
info "Création des répertoires..."
|
||||||
|
|
||||||
|
# Répertoire de backups
|
||||||
|
local backup_dir="${BACKUP_DIR:-./backups}"
|
||||||
|
if [[ ! -d "$PROJECT_DIR/$backup_dir" ]]; then
|
||||||
|
mkdir -p "$PROJECT_DIR/$backup_dir"
|
||||||
|
ok "Backups : $backup_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Répertoires NFS si configurés
|
||||||
|
local upload_vol="${UPLOAD_VOLUME:-}"
|
||||||
|
if [[ -n "$upload_vol" ]] && [[ ! "$upload_vol" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||||
|
mkdir -p "$upload_vol" 2>/dev/null || warn "Impossible de créer $upload_vol (NFS ?)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pull_images() {
|
||||||
|
info "Récupération des images Docker..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet 2>&1 | tail -3
|
||||||
|
ok "Images à jour"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_services() {
|
||||||
|
info "Démarrage des services..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
||||||
|
|
||||||
|
info "Attente de la santé des services..."
|
||||||
|
local max_wait=60
|
||||||
|
local waited=0
|
||||||
|
while [[ $waited -lt $max_wait ]]; do
|
||||||
|
local backend_ok=$(docker compose -f "$COMPOSE_FILE" ps backend --format json 2>/dev/null | grep -c '"Health":"healthy"' || echo 0)
|
||||||
|
if [[ "$backend_ok" -ge 1 ]]; then
|
||||||
|
ok "Backend healthy après ${waited}s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
waited=$((waited + 3))
|
||||||
|
echo -n "."
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ $waited -ge $max_wait ]]; then
|
||||||
|
warn "Le backend tarde à démarrer (>${max_wait}s). Vérifiez les logs :"
|
||||||
|
warn " docker compose -f $COMPOSE_FILE logs backend"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_bootstrap_key() {
|
||||||
|
echo ""
|
||||||
|
info "Clé API bootstrap (admin) — si c'est un premier déploiement :"
|
||||||
|
docker compose -f "$COMPOSE_FILE" logs backend 2>/dev/null | \
|
||||||
|
grep "API_KEY:" | tail -1 | \
|
||||||
|
sed 's/.*API_KEY: / /' | sed 's/".*//' || true
|
||||||
|
}
|
||||||
|
|
||||||
|
show_status() {
|
||||||
|
echo ""
|
||||||
|
info "État des services :"
|
||||||
|
docker compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_urls() {
|
||||||
|
echo ""
|
||||||
|
ok "═══ Imago est prêt ═══"
|
||||||
|
local admin_port="${ADMIN_PORT:-3001}"
|
||||||
|
local backend_port="${BACKEND_PORT:-8001}"
|
||||||
|
echo " Admin UI : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${admin_port}"
|
||||||
|
echo " API Docs : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${backend_port}/docs"
|
||||||
|
echo " Health : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${backend_port}/health"
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_updated() {
|
||||||
|
info "Mise à jour : pull + recréation des services modifiés..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet
|
||||||
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
||||||
|
ok "Services mis à jour"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Point d'entrée ───────────────────────────────────────────
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--update|-u)
|
||||||
|
check_prerequisites
|
||||||
|
pull_images
|
||||||
|
restart_updated
|
||||||
|
show_status
|
||||||
|
show_urls
|
||||||
|
;;
|
||||||
|
--status|-s)
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
--logs|-l)
|
||||||
|
docker compose -f "$COMPOSE_FILE" logs -f --tail=50
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: $0 [--update|--status|--logs|--help]"
|
||||||
|
echo ""
|
||||||
|
echo " (sans argument) Premier déploiement complet"
|
||||||
|
echo " --update, -u Mise à jour des containers"
|
||||||
|
echo " --status, -s Afficher l'état"
|
||||||
|
echo " --logs, -l Logs en temps réel"
|
||||||
|
echo " --help, -h Cette aide"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
check_prerequisites
|
||||||
|
setup_env
|
||||||
|
create_directories
|
||||||
|
pull_images
|
||||||
|
start_services
|
||||||
|
show_bootstrap_key
|
||||||
|
show_status
|
||||||
|
show_urls
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# Imago — Script de préparation du serveur (one-time setup)
|
||||||
|
# =============================================================================
|
||||||
|
# Usage :
|
||||||
|
# sudo ./scripts/setup.sh
|
||||||
|
#
|
||||||
|
# Ce script :
|
||||||
|
# 1. Installe Docker + Docker Compose si absents
|
||||||
|
# 2. Configure les droits utilisateur (groupe docker)
|
||||||
|
# 3. Vérifie les prérequis système
|
||||||
|
# 4. Crée les répertoires nécessaires
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# ── Vérification root ────────────────────────────────────────
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
err "Ce script doit être lancé en root : sudo ./scripts/setup.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Récupérer l'utilisateur réel (même si lancé via sudo)
|
||||||
|
REAL_USER="${SUDO_USER:-$USER}"
|
||||||
|
REAL_HOME=$(eval echo "~$REAL_USER")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "═══════════════════════════════════════════════════════════"
|
||||||
|
echo " Imago — Préparation du serveur"
|
||||||
|
echo " Utilisateur : $REAL_USER"
|
||||||
|
echo "═══════════════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 1. Installer Docker ─────────────────────────────────────
|
||||||
|
install_docker() {
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
ok "Docker déjà installé : $(docker --version)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installation de Docker..."
|
||||||
|
|
||||||
|
if command -v apt-get &>/dev/null; then
|
||||||
|
# Debian / Ubuntu
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq ca-certificates curl
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
ok "Docker installé via apt"
|
||||||
|
|
||||||
|
elif command -v dnf &>/dev/null; then
|
||||||
|
# Fedora / RHEL
|
||||||
|
dnf -y install dnf-plugins-core
|
||||||
|
dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
||||||
|
dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
ok "Docker installé via dnf"
|
||||||
|
|
||||||
|
else
|
||||||
|
err "Distribution non supportée. Installez Docker manuellement :"
|
||||||
|
err " https://docs.docker.com/engine/install/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl enable docker --now
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 2. Ajouter l'utilisateur au groupe docker ───────────────
|
||||||
|
configure_user() {
|
||||||
|
if groups "$REAL_USER" | grep -q docker; then
|
||||||
|
ok "Utilisateur $REAL_USER déjà dans le groupe docker"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Ajout de $REAL_USER au groupe docker..."
|
||||||
|
usermod -aG docker "$REAL_USER"
|
||||||
|
ok "Utilisateur $REAL_USER ajouté au groupe docker"
|
||||||
|
warn "╔══════════════════════════════════════════════════════════╗"
|
||||||
|
warn "║ Déconnectez-vous puis reconnectez-vous pour que les ║"
|
||||||
|
warn "║ changements de groupe prennent effet. ║"
|
||||||
|
warn "║ Ou lancez : newgrp docker ║"
|
||||||
|
warn "╚══════════════════════════════════════════════════════════╝"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 3. Vérifier le système ──────────────────────────────────
|
||||||
|
check_system() {
|
||||||
|
info "Vérification système..."
|
||||||
|
|
||||||
|
# Espace disque
|
||||||
|
local disk_avail=$(df -BG /var/lib/docker 2>/dev/null | awk 'NR==2 {print $4}' | tr -d 'G' || df -BG / | awk 'NR==2 {print $4}' | tr -d 'G')
|
||||||
|
if [[ ${disk_avail:-0} -lt 10 ]]; then
|
||||||
|
warn "Espace disque faible : ${disk_avail}G disponibles. 10G+ recommandé."
|
||||||
|
else
|
||||||
|
ok "Espace disque : ${disk_avail}G disponibles"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# RAM
|
||||||
|
local mem_total=$(free -m | awk '/^Mem:/{print $2}')
|
||||||
|
if [[ ${mem_total:-0} -lt 2048 ]]; then
|
||||||
|
warn "RAM faible : ${mem_total}MB. 2GB+ recommandé."
|
||||||
|
else
|
||||||
|
ok "RAM : ${mem_total}MB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Docker fonctionnel
|
||||||
|
if docker info &>/dev/null; then
|
||||||
|
ok "Docker opérationnel"
|
||||||
|
else
|
||||||
|
err "Docker ne répond pas. Vérifiez : systemctl start docker"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 4. Créer le répertoire de travail ────────────────────────
|
||||||
|
create_workdir() {
|
||||||
|
local workdir="${1:-/opt/imago}"
|
||||||
|
if [[ -d "$workdir" ]]; then
|
||||||
|
ok "Répertoire de travail existant : $workdir"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Création du répertoire de travail : $workdir"
|
||||||
|
mkdir -p "$workdir"
|
||||||
|
chown "$REAL_USER:$REAL_USER" "$workdir"
|
||||||
|
ok "Répertoire créé : $workdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Exécution ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
install_docker
|
||||||
|
configure_user
|
||||||
|
check_system
|
||||||
|
create_workdir "/opt/imago"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
ok "═══ Serveur prêt pour Imago ═══"
|
||||||
|
echo ""
|
||||||
|
echo " Prochaines étapes :"
|
||||||
|
echo " 1. Déconnectez/reconnectez-vous (groupe docker)"
|
||||||
|
echo " 2. Clonez le dépôt :"
|
||||||
|
echo " git clone <repo-url> /opt/imago"
|
||||||
|
echo " 3. Déployez :"
|
||||||
|
echo " cd /opt/imago && ./scripts/deploy.sh"
|
||||||
|
echo ""
|
||||||
Reference in New Issue
Block a user