feat: assistant interactif de configuration dans deploy.sh (stockage, AI, OCR, CORS, backups)
This commit is contained in:
+332
-77
@@ -3,123 +3,349 @@
|
|||||||
# Imago — Script de déploiement automatisé
|
# Imago — Script de déploiement automatisé
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Usage :
|
# Usage :
|
||||||
# ./scripts/deploy.sh # Premier déploiement
|
# ./scripts/deploy.sh # Déploiement interactif
|
||||||
# ./scripts/deploy.sh --update # Mise à jour (pull + recréation)
|
# ./scripts/deploy.sh --update # Mise à jour
|
||||||
# ./scripts/deploy.sh --status # Afficher l'état des conteneurs
|
# ./scripts/deploy.sh --reconfigure # Reconfigurer le .env
|
||||||
# ./scripts/deploy.sh --logs # Logs en temps réel
|
# ./scripts/deploy.sh --status # État des conteneurs
|
||||||
|
# ./scripts/deploy.sh --logs # Logs en temps réel
|
||||||
#
|
#
|
||||||
# Prérequis : Docker + Docker Compose installés (voir scripts/setup.sh)
|
# Prérequis : Docker + Docker Compose (voir scripts/setup.sh)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ── Couleurs ──────────────────────────────────────────────────
|
# ── Couleurs ──────────────────────────────────────────────────
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||||
BLUE='\033[0;34m'; NC='\033[0m'
|
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
ask() { echo -ne "${CYAN}[?]${NC} $* "; }
|
||||||
|
|
||||||
# ── Configuration ─────────────────────────────────────────────
|
# ── Configuration ─────────────────────────────────────────────
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
COMPOSE_FILE="${PROJECT_DIR}/docker-compose/docker-compose.prod.yml"
|
COMPOSE_FILE="${PROJECT_DIR}/docker-compose/docker-compose.prod.yml"
|
||||||
ENV_FILE="${PROJECT_DIR}/.env"
|
ENV_FILE="${PROJECT_DIR}/.env"
|
||||||
ENV_EXAMPLE="${PROJECT_DIR}/docker-compose/.env.example"
|
|
||||||
SERVICE_NAME="imago"
|
SERVICE_NAME="imago"
|
||||||
|
|
||||||
# ── Fonctions ─────────────────────────────────────────────────
|
# ── Fonctions utilitaires ────────────────────────────────────
|
||||||
|
|
||||||
|
generate_secret() { python3 -c "import secrets; print(secrets.token_urlsafe(32))" 2>/dev/null || openssl rand -base64 32 2>/dev/null || date +%s | sha256sum | head -c 44; }
|
||||||
|
|
||||||
|
press_enter() { echo ""; read -rp "Appuyez sur Entrée pour continuer..."; }
|
||||||
|
|
||||||
|
# ── 1. Prérequis ─────────────────────────────────────────────
|
||||||
|
|
||||||
check_prerequisites() {
|
check_prerequisites() {
|
||||||
info "Vérification des prérequis..."
|
info "Vérification des prérequis..."
|
||||||
|
|
||||||
if ! command -v docker &>/dev/null; then
|
if ! command -v docker &>/dev/null; then
|
||||||
err "Docker n'est pas installé. Lancez : scripts/setup.sh"
|
err "Docker n'est pas installé. Lancez : sudo ./scripts/setup.sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! docker compose version &>/dev/null; then
|
if ! docker compose version &>/dev/null; then
|
||||||
err "Docker Compose (plugin) n'est pas disponible."
|
err "Docker Compose (plugin) requis."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! docker info &>/dev/null; then
|
if ! docker info &>/dev/null; then
|
||||||
err "Docker ne tourne pas ou vous n'avez pas les droits."
|
err "Docker ne tourne pas. Vérifiez : sudo systemctl start docker"
|
||||||
err "Vérifiez : sudo systemctl start docker && sudo usermod -aG docker $USER"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ok "Docker $(docker --version | awk '{print $3}' | tr -d ',') — Docker Compose disponible"
|
ok "Docker $(docker --version | awk '{print $3}' | tr -d ',')"
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_env() {
|
# ── 2. Assistant de configuration interactif ─────────────────
|
||||||
if [[ -f "$ENV_FILE" ]]; then
|
|
||||||
ok "Fichier .env existant"
|
configure_env() {
|
||||||
|
local reconfigure="${1:-false}"
|
||||||
|
|
||||||
|
if [[ -f "$ENV_FILE" ]] && [[ "$reconfigure" != "true" ]]; then
|
||||||
|
ok "Fichier .env existant — utilisez --reconfigure pour le régénérer"
|
||||||
return
|
return
|
||||||
fi
|
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 ""
|
echo ""
|
||||||
read -rp "Avez-vous édité le fichier .env ? (o/N) " confirm
|
echo -e "${BOLD}${BLUE}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
if [[ ! "$confirm" =~ ^[oO]$ ]]; then
|
echo -e "${BOLD}${BLUE}║ Imago — Assistant de configuration ║${NC}"
|
||||||
err "Déploiement annulé. Éditez .env puis relancez."
|
echo -e "${BOLD}${BLUE}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "Cet assistant va créer le fichier ${BOLD}.env${NC} pour votre déploiement."
|
||||||
|
echo -e "Les valeurs entre [crochets] sont les défauts (appuyez sur Entrée)."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Génération des secrets ─────────────────────────────
|
||||||
|
local SECRET_KEY=$(generate_secret)
|
||||||
|
local SIGNED_URL_SECRET=$(generate_secret)
|
||||||
|
|
||||||
|
# ── Section 1 : Application ────────────────────────────
|
||||||
|
echo -e "${BOLD}── Application ──${NC}"
|
||||||
|
ask "Nom de l'application [Imago]:"; read -r APP_NAME; APP_NAME="${APP_NAME:-Imago}"
|
||||||
|
ask "Port admin UI [3001]:"; read -r ADMIN_PORT; ADMIN_PORT="${ADMIN_PORT:-3001}"
|
||||||
|
ask "Port API backend [8001]:"; read -r BACKEND_PORT; BACKEND_PORT="${BACKEND_PORT:-8001}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Section 2 : Stockage ───────────────────────────────
|
||||||
|
echo -e "${BOLD}── Stockage ──${NC}"
|
||||||
|
echo " Type de stockage :"
|
||||||
|
echo " [1] Local — fichiers sur le disque du serveur (simple, recommandé)"
|
||||||
|
echo " [2] S3 — MinIO / AWS S3 / Cloudflare R2 (multi-serveur)"
|
||||||
|
ask "Choix [1]:"; read -r STORAGE_CHOICE
|
||||||
|
|
||||||
|
local STORAGE_BACKEND="local"
|
||||||
|
local UPLOAD_DIR="/app/data/uploads"
|
||||||
|
local THUMBNAILS_DIR="/app/data/thumbnails"
|
||||||
|
local UPLOAD_VOLUME=""
|
||||||
|
local THUMBNAIL_VOLUME=""
|
||||||
|
|
||||||
|
if [[ "$STORAGE_CHOICE" == "2" ]]; then
|
||||||
|
STORAGE_BACKEND="s3"
|
||||||
|
ask "Bucket S3 [imago]:"; read -r S3_BUCKET; S3_BUCKET="${S3_BUCKET:-imago}"
|
||||||
|
ask "URL endpoint S3 [http://minio:9000]:"; read -r S3_ENDPOINT_URL; S3_ENDPOINT_URL="${S3_ENDPOINT_URL:-http://minio:9000}"
|
||||||
|
ask "URL publique S3 [identique]:"; read -r S3_ENDPOINT_PUBLIC; S3_ENDPOINT_PUBLIC="${S3_ENDPOINT_PUBLIC:-$S3_ENDPOINT_URL}"
|
||||||
|
ask "Access Key [minioadmin]:"; read -r S3_ACCESS_KEY; S3_ACCESS_KEY="${S3_ACCESS_KEY:-minioadmin}"
|
||||||
|
ask "Secret Key [minioadmin]:"; read -r S3_SECRET_KEY; S3_SECRET_KEY="${S3_SECRET_KEY:-minioadmin}"
|
||||||
|
ask "Région [us-east-1]:"; read -r S3_REGION; S3_REGION="${S3_REGION:-us-east-1}"
|
||||||
|
else
|
||||||
|
ask "Répertoire uploads dans le conteneur [/app/data/uploads]:"; read -r UPLOAD_DIR; UPLOAD_DIR="${UPLOAD_DIR:-/app/data/uploads}"
|
||||||
|
ask "Répertoire thumbnails dans le conteneur [/app/data/thumbnails]:"; read -r THUMBNAILS_DIR; THUMBNAILS_DIR="${THUMBNAILS_DIR:-/app/data/thumbnails}"
|
||||||
|
echo ""
|
||||||
|
echo " Volume de stockage (laisser vide = volume Docker nommé) :"
|
||||||
|
echo " Exemples : /NFS/IMAGO/uploads ou /data/imago/uploads"
|
||||||
|
ask "Volume uploads [volume Docker]:"; read -r UPLOAD_VOLUME
|
||||||
|
ask "Volume thumbnails [volume Docker]:"; read -r THUMBNAIL_VOLUME
|
||||||
|
fi
|
||||||
|
|
||||||
|
ask "Taille max upload en MB [50]:"; read -r MAX_UPLOAD_SIZE_MB; MAX_UPLOAD_SIZE_MB="${MAX_UPLOAD_SIZE_MB:-50}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Section 3 : AI ─────────────────────────────────────
|
||||||
|
echo -e "${BOLD}── Intelligence Artificielle ──${NC}"
|
||||||
|
echo " Fournisseur AI :"
|
||||||
|
echo " [1] OpenRouter (recommandé, multi-modèles)"
|
||||||
|
echo " [2] Google Gemini"
|
||||||
|
echo " [3] Désactiver l'AI"
|
||||||
|
ask "Choix [1]:"; read -r AI_CHOICE; AI_CHOICE="${AI_CHOICE:-1}"
|
||||||
|
|
||||||
|
local AI_ENABLED="true"
|
||||||
|
local AI_PROVIDER="openrouter"
|
||||||
|
local OPENROUTER_API_KEY=""
|
||||||
|
local OPENROUTER_MODEL="qwen/qwen2.5-vl-72b-instruct"
|
||||||
|
local GEMINI_API_KEY=""
|
||||||
|
local GEMINI_MODEL="gemini-3.1-pro-preview"
|
||||||
|
|
||||||
|
case "$AI_CHOICE" in
|
||||||
|
2)
|
||||||
|
AI_PROVIDER="gemini"
|
||||||
|
ask "Clé API Gemini:"; read -r GEMINI_API_KEY
|
||||||
|
ask "Modèle [$GEMINI_MODEL]:"; read -r GEMINI_MODEL; GEMINI_MODEL="${GEMINI_MODEL:-$GEMINI_MODEL}"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
AI_ENABLED="false"
|
||||||
|
ok "AI désactivée"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ask "Clé API OpenRouter:"; read -r OPENROUTER_API_KEY
|
||||||
|
ask "Modèle [$OPENROUTER_MODEL]:"; read -r OPENROUTER_MODEL; OPENROUTER_MODEL="${OPENROUTER_MODEL:-$OPENROUTER_MODEL}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ask "Langue des descriptions AI [francais]:"; read -r AI_DESCRIPTION_LANGUAGE; AI_DESCRIPTION_LANGUAGE="${AI_DESCRIPTION_LANGUAGE:-francais}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Section 4 : OCR ────────────────────────────────────
|
||||||
|
echo -e "${BOLD}── OCR ──${NC}"
|
||||||
|
ask "Activer l'OCR ? (O/n):"; read -r OCR_ENABLED
|
||||||
|
OCR_ENABLED=$( [[ ! "$OCR_ENABLED" =~ ^[nN]$ ]] && echo "true" || echo "false" )
|
||||||
|
if [[ "$OCR_ENABLED" == "true" ]]; then
|
||||||
|
ask "Commande Tesseract [/usr/bin/tesseract]:"; read -r TESSERACT_CMD; TESSERACT_CMD="${TESSERACT_CMD:-/usr/bin/tesseract}"
|
||||||
|
ask "Langues OCR [fra+eng]:"; read -r OCR_LANGUAGES; OCR_LANGUAGES="${OCR_LANGUAGES:-fra+eng}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Section 5 : CORS ───────────────────────────────────
|
||||||
|
echo -e "${BOLD}── CORS (origines autorisées) ──${NC}"
|
||||||
|
ask "Origines supplémentaires (ex: https://mon-domaine.com) [aucune]:"; read -r EXTRA_CORS
|
||||||
|
local CORS_ORIGINS="[\"http://localhost:3000\", \"http://localhost:3001\""
|
||||||
|
if [[ -n "$EXTRA_CORS" ]]; then
|
||||||
|
for origin in $EXTRA_CORS; do
|
||||||
|
CORS_ORIGINS+=", \"$origin\""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
CORS_ORIGINS+="]"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Section 6 : Backup ─────────────────────────────────
|
||||||
|
echo -e "${BOLD}── Sauvegardes ──${NC}"
|
||||||
|
ask "Répertoire des backups DB [./backups]:"; read -r BACKUP_DIR; BACKUP_DIR="${BACKUP_DIR:-./backups}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Récapitulatif ──────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}║ Récapitulatif ║${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo -e " Application : ${BOLD}$APP_NAME${NC} (admin:$ADMIN_PORT, api:$BACKEND_PORT)"
|
||||||
|
echo -e " Stockage : ${BOLD}$STORAGE_BACKEND${NC}"
|
||||||
|
if [[ "$STORAGE_BACKEND" == "s3" ]]; then
|
||||||
|
echo -e " Bucket : $S3_BUCKET @ $S3_ENDPOINT_URL"
|
||||||
|
else
|
||||||
|
echo -e " Uploads : $UPLOAD_DIR${UPLOAD_VOLUME:+ → $UPLOAD_VOLUME}"
|
||||||
|
echo -e " Thumbnails: $THUMBNAILS_DIR${THUMBNAIL_VOLUME:+ → $THUMBNAIL_VOLUME}"
|
||||||
|
fi
|
||||||
|
echo -e " AI : ${BOLD}${AI_ENABLED}${NC} (${AI_PROVIDER})"
|
||||||
|
echo -e " OCR : ${BOLD}${OCR_ENABLED}${NC}"
|
||||||
|
echo -e " Backups : $BACKUP_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
ask "Écrire cette configuration ? (O/n):"; read -r confirm
|
||||||
|
if [[ "$confirm" =~ ^[nN]$ ]]; then
|
||||||
|
err "Configuration annulée."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Écriture du .env ───────────────────────────────────
|
||||||
|
cat > "$ENV_FILE" << EOF
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# Imago — Configuration générée le $(date '+%Y-%m-%d %H:%M')
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# ── Application ──────────────────────────────────────────────
|
||||||
|
APP_NAME=$APP_NAME
|
||||||
|
DEBUG=false
|
||||||
|
SECRET_KEY=$SECRET_KEY
|
||||||
|
ADMIN_PORT=$ADMIN_PORT
|
||||||
|
BACKEND_PORT=$BACKEND_PORT
|
||||||
|
|
||||||
|
# ── Stockage ─────────────────────────────────────────────────
|
||||||
|
STORAGE_BACKEND=$STORAGE_BACKEND
|
||||||
|
UPLOAD_DIR=$UPLOAD_DIR
|
||||||
|
THUMBNAILS_DIR=$THUMBNAILS_DIR
|
||||||
|
MAX_UPLOAD_SIZE_MB=$MAX_UPLOAD_SIZE_MB
|
||||||
|
SIGNED_URL_SECRET=$SIGNED_URL_SECRET
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ -n "$UPLOAD_VOLUME" ]]; then
|
||||||
|
echo "UPLOAD_VOLUME=$UPLOAD_VOLUME" >> "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
if [[ -n "$THUMBNAIL_VOLUME" ]]; then
|
||||||
|
echo "THUMBNAIL_VOLUME=$THUMBNAIL_VOLUME" >> "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$STORAGE_BACKEND" == "s3" ]]; then
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
S3_BUCKET=$S3_BUCKET
|
||||||
|
S3_REGION=$S3_REGION
|
||||||
|
S3_ENDPOINT_URL=$S3_ENDPOINT_URL
|
||||||
|
S3_ENDPOINT_PUBLIC=$S3_ENDPOINT_PUBLIC
|
||||||
|
S3_ACCESS_KEY=$S3_ACCESS_KEY
|
||||||
|
S3_SECRET_KEY=$S3_SECRET_KEY
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
|
||||||
|
# ── AI ───────────────────────────────────────────────────────
|
||||||
|
AI_ENABLED=$AI_ENABLED
|
||||||
|
AI_PROVIDER=$AI_PROVIDER
|
||||||
|
AI_DESCRIPTION_LANGUAGE=$AI_DESCRIPTION_LANGUAGE
|
||||||
|
AI_TAGS_MIN=5
|
||||||
|
AI_TAGS_MAX=10
|
||||||
|
AI_CACHE_DAYS=30
|
||||||
|
AI_REQUEST_TIMEOUT=60
|
||||||
|
AI_MAX_RETRIES=2
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ "$AI_PROVIDER" == "openrouter" ]]; then
|
||||||
|
echo "OPENROUTER_API_KEY=$OPENROUTER_API_KEY" >> "$ENV_FILE"
|
||||||
|
echo "OPENROUTER_MODEL=$OPENROUTER_MODEL" >> "$ENV_FILE"
|
||||||
|
elif [[ "$AI_PROVIDER" == "gemini" ]]; then
|
||||||
|
echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> "$ENV_FILE"
|
||||||
|
echo "GEMINI_MODEL=$GEMINI_MODEL" >> "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
|
||||||
|
# ── OCR ──────────────────────────────────────────────────────
|
||||||
|
OCR_ENABLED=$OCR_ENABLED
|
||||||
|
TESSERACT_CMD=$TESSERACT_CMD
|
||||||
|
OCR_LANGUAGES=$OCR_LANGUAGES
|
||||||
|
|
||||||
|
# ── CORS ─────────────────────────────────────────────────────
|
||||||
|
CORS_ORIGINS=$CORS_ORIGINS
|
||||||
|
|
||||||
|
# ── Authentification ─────────────────────────────────────────
|
||||||
|
ADMIN_API_KEY=
|
||||||
|
JWT_SECRET_KEY=$(generate_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
|
||||||
|
|
||||||
|
# ── Sauvegardes ─────────────────────────────────────────────
|
||||||
|
BACKUP_DIR=$BACKUP_DIR
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ok "Fichier .env créé : $ENV_FILE"
|
||||||
|
press_enter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── 3. Création des répertoires ──────────────────────────────
|
||||||
|
|
||||||
create_directories() {
|
create_directories() {
|
||||||
info "Création des répertoires..."
|
info "Préparation des répertoires..."
|
||||||
|
|
||||||
# Répertoire de backups
|
# Charger .env s'il existe
|
||||||
local backup_dir="${BACKUP_DIR:-./backups}"
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
if [[ ! -d "$PROJECT_DIR/$backup_dir" ]]; then
|
set -a; source "$ENV_FILE"; set +a
|
||||||
mkdir -p "$PROJECT_DIR/$backup_dir"
|
|
||||||
ok "Backups : $backup_dir"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Répertoires NFS si configurés
|
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 de stockage si chemins locaux (pas volumes Docker)
|
||||||
local upload_vol="${UPLOAD_VOLUME:-}"
|
local upload_vol="${UPLOAD_VOLUME:-}"
|
||||||
if [[ -n "$upload_vol" ]] && [[ ! "$upload_vol" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
if [[ -n "$upload_vol" ]] && [[ "$upload_vol" == /* ]]; then
|
||||||
mkdir -p "$upload_vol" 2>/dev/null || warn "Impossible de créer $upload_vol (NFS ?)"
|
mkdir -p "$upload_vol" 2>/dev/null || warn "Impossible de créer $upload_vol"
|
||||||
|
fi
|
||||||
|
local thumb_vol="${THUMBNAIL_VOLUME:-}"
|
||||||
|
if [[ -n "$thumb_vol" ]] && [[ "$thumb_vol" == /* ]]; then
|
||||||
|
mkdir -p "$thumb_vol" 2>/dev/null || warn "Impossible de créer $thumb_vol"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── 4. Pull images ───────────────────────────────────────────
|
||||||
|
|
||||||
pull_images() {
|
pull_images() {
|
||||||
info "Récupération des images Docker..."
|
info "Récupération des images Docker..."
|
||||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet 2>&1 | tail -3
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull 2>&1 | grep -E "Pull|Image" || true
|
||||||
ok "Images à jour"
|
ok "Images à jour"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── 5. Démarrage ─────────────────────────────────────────────
|
||||||
|
|
||||||
start_services() {
|
start_services() {
|
||||||
info "Démarrage des services..."
|
info "Démarrage des services..."
|
||||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
||||||
|
|
||||||
info "Attente de la santé des services..."
|
info "Attente de la santé du backend..."
|
||||||
local max_wait=60
|
local max_wait=90
|
||||||
local waited=0
|
local waited=0
|
||||||
while [[ $waited -lt $max_wait ]]; do
|
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)
|
local health=$(docker compose -f "$COMPOSE_FILE" ps backend --format '{{.Health}}' 2>/dev/null || echo "")
|
||||||
if [[ "$backend_ok" -ge 1 ]]; then
|
if [[ "$health" == "healthy" ]]; then
|
||||||
ok "Backend healthy après ${waited}s"
|
ok "Backend healthy après ${waited}s"
|
||||||
break
|
return
|
||||||
fi
|
fi
|
||||||
sleep 3
|
sleep 3
|
||||||
waited=$((waited + 3))
|
waited=$((waited + 3))
|
||||||
@@ -128,38 +354,58 @@ start_services() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ $waited -ge $max_wait ]]; then
|
if [[ $waited -ge $max_wait ]]; then
|
||||||
warn "Le backend tarde à démarrer (>${max_wait}s). Vérifiez les logs :"
|
warn "Le backend tarde à démarrer (>${max_wait}s)."
|
||||||
warn " docker compose -f $COMPOSE_FILE logs backend"
|
warn "Logs : docker compose -f $COMPOSE_FILE logs backend"
|
||||||
|
warn "Si c'est un premier déploiement, cela peut être normal (init BDD)."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── 6. Affichage final ───────────────────────────────────────
|
||||||
|
|
||||||
show_bootstrap_key() {
|
show_bootstrap_key() {
|
||||||
echo ""
|
echo ""
|
||||||
info "Clé API bootstrap (admin) — si c'est un premier déploiement :"
|
info "Clé API bootstrap (admin) — conservez-la précieusement :"
|
||||||
docker compose -f "$COMPOSE_FILE" logs backend 2>/dev/null | \
|
local key=$(docker compose -f "$COMPOSE_FILE" logs backend 2>/dev/null | grep "API_KEY:" | tail -1 | sed 's/.*API_KEY: //;s/".*//' || echo "")
|
||||||
grep "API_KEY:" | tail -1 | \
|
if [[ -n "$key" ]]; then
|
||||||
sed 's/.*API_KEY: / /' | sed 's/".*//' || true
|
echo -e " ${BOLD}${GREEN}${key}${NC}"
|
||||||
|
else
|
||||||
|
echo " (aucune clé trouvée — vérifiez les logs si premier déploiement)"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
show_status() {
|
show_status() {
|
||||||
echo ""
|
echo ""
|
||||||
info "État des services :"
|
info "État des services :"
|
||||||
docker compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
docker compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
show_urls() {
|
show_urls() {
|
||||||
echo ""
|
local ip=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
|
||||||
ok "═══ Imago est prêt ═══"
|
|
||||||
|
# Charger les ports depuis .env
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
set -a; source "$ENV_FILE"; set +a
|
||||||
|
fi
|
||||||
local admin_port="${ADMIN_PORT:-3001}"
|
local admin_port="${ADMIN_PORT:-3001}"
|
||||||
local backend_port="${BACKEND_PORT:-8001}"
|
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 ""
|
||||||
echo " Health : http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):${backend_port}/health"
|
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}║ Imago est prêt ! ║${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Admin UI :${NC} http://${ip}:${admin_port}"
|
||||||
|
echo -e " ${BOLD}API Docs :${NC} http://${ip}:${backend_port}/docs"
|
||||||
|
echo -e " ${BOLD}Health :${NC} http://${ip}:${backend_port}/health"
|
||||||
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Mise à jour ──────────────────────────────────────────────
|
||||||
|
|
||||||
restart_updated() {
|
restart_updated() {
|
||||||
info "Mise à jour : pull + recréation des services modifiés..."
|
source "$ENV_FILE" 2>/dev/null || true
|
||||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull --quiet
|
info "Mise à jour : pull + recréation..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" pull 2>&1 | grep -E "Pull|Image|up to date" || true
|
||||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --remove-orphans
|
||||||
ok "Services mis à jour"
|
ok "Services mis à jour"
|
||||||
}
|
}
|
||||||
@@ -171,29 +417,38 @@ cd "$PROJECT_DIR"
|
|||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
--update|-u)
|
--update|-u)
|
||||||
check_prerequisites
|
check_prerequisites
|
||||||
pull_images
|
|
||||||
restart_updated
|
restart_updated
|
||||||
show_status
|
show_status
|
||||||
show_urls
|
show_urls
|
||||||
;;
|
;;
|
||||||
|
--reconfigure|--config)
|
||||||
|
check_prerequisites
|
||||||
|
configure_env "true"
|
||||||
|
create_directories
|
||||||
|
restart_updated
|
||||||
|
show_status
|
||||||
|
show_bootstrap_key
|
||||||
|
show_urls
|
||||||
|
;;
|
||||||
--status|-s)
|
--status|-s)
|
||||||
show_status
|
show_status
|
||||||
;;
|
;;
|
||||||
--logs|-l)
|
--logs|-l)
|
||||||
docker compose -f "$COMPOSE_FILE" logs -f --tail=50
|
docker compose -f "$COMPOSE_FILE" logs -f --tail=100
|
||||||
;;
|
;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
echo "Usage: $0 [--update|--status|--logs|--help]"
|
echo "Usage: $0 [OPTION]"
|
||||||
echo ""
|
echo ""
|
||||||
echo " (sans argument) Premier déploiement complet"
|
echo " (sans argument) Premier déploiement (assistant interactif)"
|
||||||
echo " --update, -u Mise à jour des containers"
|
echo " --update, -u Mise à jour des conteneurs"
|
||||||
echo " --status, -s Afficher l'état"
|
echo " --reconfigure Reconfigurer le fichier .env"
|
||||||
echo " --logs, -l Logs en temps réel"
|
echo " --status, -s Afficher l'état des services"
|
||||||
echo " --help, -h Cette aide"
|
echo " --logs, -l Logs en temps réel"
|
||||||
|
echo " --help, -h Cette aide"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
check_prerequisites
|
check_prerequisites
|
||||||
setup_env
|
configure_env "false"
|
||||||
create_directories
|
create_directories
|
||||||
pull_images
|
pull_images
|
||||||
start_services
|
start_services
|
||||||
|
|||||||
Reference in New Issue
Block a user