#!/usr/bin/env bash # ============================================================================= # Actelyo — Installateur self-hosted # Usage : curl -sSL https://install.actelyo.fr | bash # # Systèmes supportés : Ubuntu 22.04+, Debian 11+, RHEL/Rocky 8+ # Prérequis : accès root ou sudo, port 8080 ouvert # ============================================================================= set -euo pipefail # ── Couleurs ────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' info() { echo -e "${CYAN}▶${RESET} $*"; } success() { echo -e "${GREEN}✓${RESET} $*"; } warn() { echo -e "${YELLOW}⚠${RESET} $*"; } error() { echo -e "${RED}✗${RESET} $*" >&2; exit 1; } ask() { echo -en "${BOLD} $1${RESET}"; } # ── Banner ──────────────────────────────────────────────────────────────────── clear echo -e "${CYAN}" cat << 'BANNER' ╔══════════════════════════════════════════════════════════╗ ║ ACTELYO — Installation Self-Hosted ║ ║ 100% local · Zéro dépendance cloud ║ ╚══════════════════════════════════════════════════════════╝ BANNER echo -e "${RESET}" # ── Variables globales ──────────────────────────────────────────────────────── REPO_URL="https://github.com/jeanneretsamy-ux/ACTELYO-ERP.git" DEPLOY_DIR="/opt/actelyo" LOG_FILE="/tmp/actelyo-install.log" ACTELYO_VERSION="main" exec 2>>"$LOG_FILE" # ── 1. Vérifications préalables ─────────────────────────────────────────────── echo -e "\n${BOLD}Étape 1/7 — Vérification du système${RESET}" if [[ $EUID -ne 0 ]]; then if ! sudo -n true 2>/dev/null; then error "Ce script nécessite les droits sudo. Relancez avec : sudo bash" fi SUDO="sudo" else SUDO="" fi if [[ -f /etc/os-release ]]; then . /etc/os-release OS_ID="${ID}" OS_VERSION="${VERSION_ID%%.*}" else error "Impossible de détecter l'OS. Ubuntu 22.04+ ou Debian 11+ requis." fi case "$OS_ID" in ubuntu|debian|linuxmint) PKG_MGR="apt" ;; rhel|centos|rocky|almalinux|fedora) PKG_MGR="dnf" ;; *) error "OS '$OS_ID' non supporté. Ubuntu/Debian/RHEL requis." ;; esac success "OS détecté : $PRETTY_NAME" TOTAL_RAM_MB=$(awk '/MemTotal/ {printf "%d", $2/1024}' /proc/meminfo) if [[ $TOTAL_RAM_MB -lt 3800 ]]; then warn "RAM disponible : ${TOTAL_RAM_MB}Mo — 4Go minimum recommandé" else success "RAM : ${TOTAL_RAM_MB}Mo" fi FREE_DISK_GB=$(df /opt 2>/dev/null | awk 'NR==2 {printf "%d", $4/1024/1024}' || echo 0) if [[ $FREE_DISK_GB -lt 15 ]]; then warn "Espace libre sur /opt : ${FREE_DISK_GB}Go — 20Go recommandé" else success "Espace disque : ${FREE_DISK_GB}Go disponibles" fi # ── 2. Installation des dépendances ─────────────────────────────────────────── echo -e "\n${BOLD}Étape 2/7 — Installation des dépendances${RESET}" install_pkg() { if [[ $PKG_MGR == "apt" ]]; then $SUDO apt-get install -y -qq "$@" >> "$LOG_FILE" 2>&1 else $SUDO dnf install -y -q "$@" >> "$LOG_FILE" 2>&1 fi } if ! command -v git &>/dev/null; then info "Installation de git..." install_pkg git fi success "git $(git --version | awk '{print $3}')" if ! command -v node &>/dev/null; then info "Installation de Node.js 20..." if [[ $PKG_MGR == "apt" ]]; then curl -fsSL https://deb.nodesource.com/setup_20.x | $SUDO bash - >> "$LOG_FILE" 2>&1 install_pkg nodejs else $SUDO dnf module install -y nodejs:20 >> "$LOG_FILE" 2>&1 fi fi success "Node.js $(node --version)" if ! command -v docker &>/dev/null; then info "Installation de Docker CE..." if [[ $PKG_MGR == "apt" ]]; then install_pkg ca-certificates curl gnupg lsb-release $SUDO install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/${OS_ID}/gpg \ | $SUDO gpg --dearmor -o /etc/apt/keyrings/docker.gpg >> "$LOG_FILE" 2>&1 echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/${OS_ID} $(lsb_release -cs) stable" \ | $SUDO tee /etc/apt/sources.list.d/docker.list >> "$LOG_FILE" 2>&1 $SUDO apt-get update -qq >> "$LOG_FILE" 2>&1 install_pkg docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin else $SUDO dnf install -y -q dnf-plugins-core >> "$LOG_FILE" 2>&1 $SUDO dnf config-manager --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo >> "$LOG_FILE" 2>&1 install_pkg docker-ce docker-ce-cli containerd.io docker-compose-plugin fi $SUDO systemctl enable --now docker >> "$LOG_FILE" 2>&1 [[ -n "${SUDO_USER:-}" ]] && $SUDO usermod -aG docker "$SUDO_USER" fi success "Docker $(docker --version | awk '{print $3}' | tr -d ',')" if ! docker compose version &>/dev/null; then error "Docker Compose v2 non trouvé. Mettez à jour Docker Desktop." fi success "Docker Compose $(docker compose version --short)" # ── 3. Configuration interactive ────────────────────────────────────────────── echo -e "\n${BOLD}Étape 3/7 — Configuration de votre installation${RESET}\n" LOCAL_IP=$(hostname -I | awk '{print $1}') ask "Nom du cabinet [Mon Cabinet] : " read -r CLIENT_NAME; CLIENT_NAME="${CLIENT_NAME:-Mon Cabinet}" ask "URL publique [http://${LOCAL_IP}:8080] : " read -r SITE_URL; SITE_URL="${SITE_URL:-http://${LOCAL_IP}:8080}" ask "IP du serveur GPU / vLLM [http://${LOCAL_IP}:8000] : " read -r VLLM_URL; VLLM_URL="${VLLM_URL:-http://${LOCAL_IP}:8000}" ask "IP du serveur CPU llama.cpp [http://${LOCAL_IP}:8080] : " read -r LLAMACPP_URL; LLAMACPP_URL="${LLAMACPP_URL:-http://${LOCAL_IP}:8080}" echo "" ask "Configurer l'email SMTP maintenant ? (o/N) : " read -r CONFIGURE_SMTP SMTP_HOST="" SMTP_PORT="587" SMTP_USER="" SMTP_PASS="" ENABLE_AUTOCONFIRM="true" if [[ "${CONFIGURE_SMTP,,}" == "o" ]]; then ask " SMTP host : "; read -r SMTP_HOST ask " SMTP port [587] : "; read -r p; SMTP_PORT="${p:-587}" ask " SMTP email : "; read -r SMTP_USER ask " SMTP mot de passe : "; read -rs SMTP_PASS; echo "" ENABLE_AUTOCONFIRM="false" fi echo "" success "Configuration enregistrée" # ── 4. Génération des secrets ────────────────────────────────────────────────── echo -e "\n${BOLD}Étape 4/7 — Génération des secrets${RESET}" gen_secret() { openssl rand -base64 "${1:-48}" | tr -d '=/+' | head -c "${2:-64}"; } gen_hex() { openssl rand -hex "${1:-32}"; } POSTGRES_PASSWORD=$(gen_secret 32 44) LITELLM_KEY=$(gen_secret 32 44) ODYSSEUS_SECRET=$(gen_secret 48 64) POWERSYNC_SECRET=$(gen_secret 32 44) KEYCLOAK_PASSWORD=$(gen_secret 20 28) N8N_PASSWORD=$(gen_secret 20 28) JWT_SECRET=$(gen_hex 32) success "Secrets générés (POSTGRES, JWT, LiteLLM, Keycloak, n8n, PowerSync)" # ── 5. Clone du repo ────────────────────────────────────────────────────────── echo -e "\n${BOLD}Étape 5/7 — Téléchargement d'Actelyo${RESET}" if [[ -d "$DEPLOY_DIR/.git" ]]; then info "Mise à jour du repo existant..." git -C "$DEPLOY_DIR" pull --ff-only >> "$LOG_FILE" 2>&1 else info "Clone du repo Actelyo..." $SUDO git clone --depth 1 --branch "$ACTELYO_VERSION" \ "$REPO_URL" "$DEPLOY_DIR" >> "$LOG_FILE" 2>&1 $SUDO chown -R "${SUDO_USER:-root}:${SUDO_USER:-root}" "$DEPLOY_DIR" fi success "Actelyo téléchargé dans $DEPLOY_DIR" info "Génération des JWT Supabase..." SUPABASE_KEYS=$(node "$DEPLOY_DIR/scripts/generate-supabase-keys.js" "$JWT_SECRET") ANON_KEY=$(echo "$SUPABASE_KEYS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['anon_key'])") SERVICE_ROLE_KEY=$(echo "$SUPABASE_KEYS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['service_role_key'])") SECRET_KEY_BASE=$(echo "$SUPABASE_KEYS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['secret_key_base'])") success "JWT Supabase générés" # ── 6. Écriture des fichiers de configuration ───────────────────────────────── echo -e "\n${BOLD}Étape 6/7 — Configuration des services${RESET}" cat > "$DEPLOY_DIR/.env.selfhosted" << ENV # Actelyo Self-Hosted — ${CLIENT_NAME} # Généré le $(date '+%Y-%m-%d %H:%M') — NE PAS MODIFIER MANUELLEMENT SITE_URL=${SITE_URL} ACTELYO_PORT=8080 LEGAL_API_PORT=8000 STUDIO_PORT=3000 KEYCLOAK_PORT=8180 N8N_PORT=5678 POWERSYNC_PORT=8083 ACAGENT_PORT=8010 POSTGRES_PASSWORD=${POSTGRES_PASSWORD} JWT_SECRET=${JWT_SECRET} ANON_KEY=${ANON_KEY} SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY} SECRET_KEY_BASE=${SECRET_KEY_BASE} STUDIO_DEFAULT_ORGANIZATION=${CLIENT_NAME} STUDIO_DEFAULT_PROJECT=Actelyo ERP ENABLE_EMAIL_AUTOCONFIRM=${ENABLE_AUTOCONFIRM} SMTP_HOST=${SMTP_HOST} SMTP_PORT=${SMTP_PORT} SMTP_USER=${SMTP_USER} SMTP_PASS=${SMTP_PASS} SMTP_SENDER_NAME=Actelyo KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_PASSWORD} POWERSYNC_JWT_SECRET=${POWERSYNC_SECRET} LITELLM_MASTER_KEY=${LITELLM_KEY} LITELLM_MODEL=actelyo/gemma VLLM_BASE_URL=${VLLM_URL} LLAMACPP_BASE_URL=${LLAMACPP_URL} QDRANT_API_KEY= ODYSSEUS_JWT_SECRET=${ODYSSEUS_SECRET} N8N_BASIC_AUTH_USER=admin N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD} N8N_HOST=${LOCAL_IP} N8N_PROTOCOL=http N8N_WEBHOOK_URL=http://${LOCAL_IP}:5678/ VITE_N8N_ENABLED=false VITE_AI_PROVIDER=litellm STRIPE_SECRET_KEY= PAPPERS_API_KEY= DOCUSEAL_API_KEY= PENNYLANE_API_KEY= OPENAI_API_KEY= ENV cat > "$DEPLOY_DIR/acagent/.env" << ACAGENT LLM_PROVIDER=litellm LITELLM_BASE_URL=http://litellm:4000 LITELLM_MASTER_KEY=${LITELLM_KEY} LITELLM_MODEL=actelyo/gemma QDRANT_URL=http://qdrant:6333 QDRANT_API_KEY= SUPABASE_URL=http://actelyo-web:80 SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY} ACAGENT $SUDO bash -c "cat > /root/actelyo-secrets-$(date +%Y%m%d).txt" << SECRETS ACTELYO SECRETS — ${CLIENT_NAME^^} Généré le $(date '+%Y-%m-%d %H:%M') — CONSERVER EN LIEU SÛR POSTGRES_PASSWORD = ${POSTGRES_PASSWORD} JWT_SECRET = ${JWT_SECRET} ANON_KEY = ${ANON_KEY} SERVICE_ROLE_KEY = ${SERVICE_ROLE_KEY} SECRET_KEY_BASE = ${SECRET_KEY_BASE} LITELLM_MASTER_KEY = ${LITELLM_KEY} ODYSSEUS_JWT_SECRET = ${ODYSSEUS_SECRET} POWERSYNC_JWT_SECRET = ${POWERSYNC_SECRET} KEYCLOAK_ADMIN_PASS = ${KEYCLOAK_PASSWORD} N8N_ADMIN_PASSWORD = ${N8N_PASSWORD} APPLICATION = ${SITE_URL} SUPABASE STUDIO = ${SITE_URL%:*}:3000 KEYCLOAK ADMIN = ${SITE_URL%:*}:8180/admin SECRETS $SUDO chmod 400 /root/actelyo-secrets-$(date +%Y%m%d).txt success "Configuration créée — Secrets dans /root/actelyo-secrets-$(date +%Y%m%d).txt" # ── 7. Démarrage ────────────────────────────────────────────────────────────── echo -e "\n${BOLD}Étape 7/7 — Démarrage d'Actelyo${RESET}" echo -e " ${YELLOW}10-20 minutes (téléchargement des images Docker)...${RESET}\n" cd "$DEPLOY_DIR" docker compose \ -f docker-compose.selfhosted.yml \ -f infra/hermes-odysseus/docker-compose.hermes.yml \ --env-file .env.selfhosted \ up -d --build --pull always >> "$LOG_FILE" 2>&1 success "Conteneurs démarrés" info "Attente de la base de données..." RETRIES=30 until docker inspect --format='{{.State.Health.Status}}' actelyo-supabase-db 2>/dev/null | grep -q "healthy"; do RETRIES=$((RETRIES-1)) [[ $RETRIES -eq 0 ]] && error "La base de données ne démarre pas. Voir : $LOG_FILE" sleep 10 done success "Base de données prête" info "Application des migrations SQL..." MIGRATION_COUNT=0 for SQL_FILE in "$DEPLOY_DIR"/supabase/migrations/*.sql; do FILENAME=$(basename "$SQL_FILE") docker exec actelyo-supabase-db psql -U postgres -d postgres -q \ -c "CREATE TABLE IF NOT EXISTS _migrations_applied (filename TEXT PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT now());" \ >> "$LOG_FILE" 2>&1 APPLIED=$(docker exec actelyo-supabase-db psql -U postgres -d postgres -tAq \ -c "SELECT COUNT(*) FROM _migrations_applied WHERE filename='${FILENAME}';" 2>/dev/null || echo "0") if [[ "$APPLIED" == "0" ]]; then docker exec -i actelyo-supabase-db psql -U postgres -d postgres -q \ < "$SQL_FILE" >> "$LOG_FILE" 2>&1 && \ docker exec actelyo-supabase-db psql -U postgres -d postgres -q \ -c "INSERT INTO _migrations_applied(filename) VALUES('${FILENAME}');" >> "$LOG_FILE" 2>&1 MIGRATION_COUNT=$((MIGRATION_COUNT+1)) fi done success "$MIGRATION_COUNT migrations appliquées" info "Attente du moteur IA..." RETRIES=20 until docker inspect --format='{{.State.Health.Status}}' actelyo-acagent 2>/dev/null | grep -q "healthy"; do RETRIES=$((RETRIES-1)) [[ $RETRIES -eq 0 ]] && { warn "AcAgent lent — ingestion ignorée"; break; } sleep 10 done if [[ $RETRIES -gt 0 ]]; then info "Ingestion de la base de connaissances..." docker compose -f docker-compose.selfhosted.yml --env-file .env.selfhosted \ exec -T acagent python scripts/ingest_knowledge.py >> "$LOG_FILE" 2>&1 \ && success "Base de connaissances indexée" fi echo "" echo -e "${GREEN}" cat << SUMMARY ╔══════════════════════════════════════════════════════════╗ ║ Actelyo installé avec succès ! ║ ╠══════════════════════════════════════════════════════════╣ ║ ║ ║ Application → ${SITE_URL} ║ Supabase DB → ${SITE_URL%:*}:3000 ║ Keycloak SSO → ${SITE_URL%:*}:8180/admin ║ n8n → ${SITE_URL%:*}:5678 ║ ║ ║ Secrets → /root/actelyo-secrets-$(date +%Y%m%d).txt ║ Logs install → ${LOG_FILE} ║ ║ ╚══════════════════════════════════════════════════════════╝ SUMMARY echo -e "${RESET}"