#!/bin/bash # Usage: curl -sSL https://vps.mogiz.fr | sudo bash -s # SUR o2SWITCH : # --> MySQL Distant : Ajouter IP du VPS # --> CNAME : Faire matcher non de domaine avec IP du VPS set -e # Forcer la libération des locks dpkg kill -9 $(lsof /var/lib/dpkg/lock-frontend 2>/dev/null | awk 'NR>1 {print $2}') 2>/dev/null || true kill -9 $(lsof /var/lib/dpkg/lock 2>/dev/null | awk 'NR>1 {print $2}') 2>/dev/null || true rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock dpkg --configure -a # Désactiver l'interactivité de needrestart export NEEDRESTART_MODE=a export DEBIAN_FRONTEND=noninteractive # ============================================================================= # COULEURS & HELPERS # ============================================================================= RESET="\033[0m" BOLD="\033[1m" DIM="\033[2m" RED="\033[0;31m" GREEN="\033[0;32m" YELLOW="\033[0;33m" BLUE="\033[0;34m" CYAN="\033[0;36m" WHITE="\033[0;37m" BG_BLUE="\033[44m" BG_GREEN="\033[42m" BG_RED="\033[41m" STEP=0 STEP_TOTAL=7 step() { STEP=$((STEP + 1)) echo "" echo -e "${BG_BLUE}${BOLD}${WHITE} [$STEP/$STEP_TOTAL] $1 ${RESET}" echo -e "${DIM}$(printf '─%.0s' $(seq 1 60))${RESET}" } ok() { echo -e " ${GREEN}✓${RESET} $1"; } skip() { echo -e " ${YELLOW}⊘${RESET} ${DIM}$1 (déjà fait — ignoré)${RESET}"; } info() { echo -e " ${CYAN}→${RESET} $1"; } warn() { echo -e " ${YELLOW}⚠${RESET} $1"; } fail() { echo -e " ${RED}✗${RESET} $1"; exit 1; } header() { echo "" echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════════════╗${RESET}" echo -e "${BOLD}${CYAN}║${RESET} ${BOLD}$1${RESET}" echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════════════╝${RESET}" echo "" } # ============================================================================= # CONFIGURATION # ============================================================================= APP_DIR="/app" APP_USER="chatroulette" BASE_URL="https://vps.mogiz.fr" # MySQL credentials (en dur) MYSQL_HOST="109.234.161.105" MYSQL_USER="demo1633_chatroulette_voice_v2" MYSQL_PASSWORD="safrandu3821" MYSQL_DATABASE="demo1633_chatroulette_voice_v2" FILES_TO_DOWNLOAD=( "chatroulette.js|$APP_DIR/chatroulette.js" "package.json|$APP_DIR/package.json" "plucky-operand-457823-h3-8ce8652e2117.json|$APP_DIR/google-credentials.json" "MP3/attente.mp3|$APP_DIR/MP3/attente.mp3" "MP3/Afida_turner_etienne.mp3|$APP_DIR/MP3/Afida_turner_etienne.mp3" "MP3/skip.mp3|$APP_DIR/MP3/skip.mp3" ) # ============================================================================= # DÉBUT # ============================================================================= clear header "Chatroulette Audio — Installation bare metal (multi-VPS)" # ============================================================================= # [1/7] Mise à jour système # ============================================================================= step "Mise à jour du système" info "apt-get update..." apt-get update -y -qq ok "Système à jour" # ============================================================================= # [2/7] Dépendances système # ============================================================================= step "Dépendances système" PACKAGES=(curl gnupg ca-certificates python3 python3-pip make g++ espeak ffmpeg debian-keyring debian-archive-keyring apt-transport-https mysql-client) TO_INSTALL=() for pkg in "${PACKAGES[@]}"; do if dpkg -s "$pkg" &>/dev/null 2>&1; then skip "$pkg" else TO_INSTALL+=("$pkg") fi done if [ ${#TO_INSTALL[@]} -gt 0 ]; then info "Installation : ${TO_INSTALL[*]}" apt-get install -y --no-install-recommends "${TO_INSTALL[@]}" -qq ok "Paquets installés : ${TO_INSTALL[*]}" else ok "Tous les paquets système sont déjà présents" fi # ============================================================================= # [3/7] Node.js 20 # ============================================================================= step "Node.js 20" if command -v node &>/dev/null && [[ "$(node --version)" == v20* ]]; then skip "Node.js déjà en version $(node --version)" else info "Installation de Node.js 20 via NodeSource..." curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y nodejs -qq ok "Node.js installé : $(node --version)" fi # ============================================================================= # [4/7] Utilisateur, dossiers & fichiers # ============================================================================= step "Utilisateur, dossiers & fichiers" if id "$APP_USER" &>/dev/null; then skip "Utilisateur $APP_USER" else useradd --system --no-create-home --shell /bin/false "$APP_USER" ok "Utilisateur $APP_USER créé" fi mkdir -p "$APP_DIR/MP3" "$APP_DIR/recordings" ok "Dossiers $APP_DIR/MP3 et recordings présents" info "Téléchargement des fichiers applicatifs depuis $BASE_URL" for entry in "${FILES_TO_DOWNLOAD[@]}"; do src="${entry%%|*}" dst="${entry##*|}" url="$BASE_URL/$src" info "↓ $src" curl -fsSL "$url" -o "$dst" || fail "Impossible de télécharger $url" ok "$dst" done if [ -d "./MP3" ]; then cp -r ./MP3/. "$APP_DIR/MP3/" ok "MP3 locaux copiés" fi # ============================================================================= # [5/7] npm install # ============================================================================= step "npm install (dépendances Node.js)" cd "$APP_DIR" HASHFILE="$APP_DIR/.npm_install_hash" CURRENT_HASH=$(md5sum "$APP_DIR/package.json" 2>/dev/null | awk '{print $1}') SAVED_HASH=$(cat "$HASHFILE" 2>/dev/null || echo "") if [ -d "$APP_DIR/node_modules" ] && [ "$CURRENT_HASH" = "$SAVED_HASH" ]; then skip "node_modules déjà installés et package.json inchangé" else info "npm install en cours..." npm install echo "$CURRENT_HASH" > "$HASHFILE" ok "Dépendances Node.js installées" fi cd - > /dev/null chown -R "$APP_USER":"$APP_USER" "$APP_DIR" ok "Permissions $APP_USER appliquées sur $APP_DIR" # ============================================================================= # [6/7] Gestion de la room dans MySQL (IP -> room_id + domaine) # ============================================================================= step "Gestion de la room dans MySQL" # Récupérer l'IP publique IPv4 PUBLIC_IP=$(curl -4 -s ifconfig.me) if [ -z "$PUBLIC_IP" ]; then PUBLIC_IP=$(hostname -I | grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' | head -1) fi if [ -z "$PUBLIC_IP" ]; then fail "Impossible de détecter une IPv4 publique" fi info "IP publique détectée : $PUBLIC_IP" # Vérifier la connexion MySQL info "Vérification de la connexion MySQL..." if ! mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1" "$MYSQL_DATABASE" &>/dev/null; then fail "Impossible de se connecter à MySQL avec les identifiants fournis" fi # Chercher la room correspondant à cette IP ROOM_DATA=$(mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -N -e "SELECT id, server_domain FROM rooms WHERE server_ip = '$PUBLIC_IP' LIMIT 1" "$MYSQL_DATABASE" 2>/dev/null || true) if [ -z "$ROOM_DATA" ]; then fail "Aucune room trouvée avec l'IP $PUBLIC_IP dans la table rooms" fi # Extraire ID et DOMAINE ROOM_ID=$(echo "$ROOM_DATA" | awk '{print $1}') SERVER_DOMAIN=$(echo "$ROOM_DATA" | awk '{print $2}') if [ -z "$ROOM_ID" ]; then fail "L'ID de la room n'a pas pu être extrait" fi if [ -z "$SERVER_DOMAIN" ]; then fail "La room $ROOM_ID n'a pas de server_domain défini (champ NULL ou vide). Impossible de configurer le reverse proxy." fi info "Room trouvée : ID=$ROOM_ID, Domaine=$SERVER_DOMAIN" # ============================================================================= # [7/7] Service systemd # ============================================================================= step "Service systemd" SERVICE_FILE="/etc/systemd/system/chatroulette.service" if [ -f "$SERVICE_FILE" ]; then skip "Service systemd déjà présent (fichier existant conservé)" info "Pour le regénérer, supprimez $SERVICE_FILE puis relancez ce script" else cat > "$SERVICE_FILE" </dev/null; then skip "Service chatroulette déjà activé" else systemctl enable chatroulette ok "Service chatroulette activé au démarrage" fi systemctl restart chatroulette ok "Service chatroulette (re)démarré" # ============================================================================= # Caddy — reverse proxy + SSL automatique (avec le domaine récupéré) # ============================================================================= step "Caddy — reverse proxy + SSL automatique" if command -v caddy &>/dev/null; then skip "Caddy déjà installé ($(caddy version))" else info "Ajout du dépôt Caddy..." curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ | tee /etc/apt/sources.list.d/caddy-stable.list apt-get update -qq apt-get install -y caddy -qq ok "Caddy installé : $(caddy version)" fi CADDYFILE="/etc/caddy/Caddyfile" if [ -f "$CADDYFILE" ] && grep -q "$SERVER_DOMAIN" "$CADDYFILE"; then skip "Caddyfile déjà configuré pour $SERVER_DOMAIN (conservé)" info "Pour le regénérer, supprimez $CADDYFILE puis relancez" else cat > "$CADDYFILE" </dev/null; then skip "Caddy déjà activé au démarrage" else systemctl enable caddy ok "Caddy activé au démarrage" fi systemctl restart caddy ok "Caddy (re)démarré — SSL Let's Encrypt géré automatiquement" # ============================================================================= # FIREWALL # ============================================================================= echo "" echo -e "${DIM}$(printf '─%.0s' $(seq 1 60))${RESET}" info "Vérification du firewall..." if command -v ufw &>/dev/null; then UFW_STATUS=$(ufw status 2>/dev/null) RULES_ADDED=0 for rule in "80/tcp" "443/tcp" "40000:49999/udp"; do if echo "$UFW_STATUS" | grep -qiE "^${rule}[[:space:]]"; then skip "Règle UFW $rule déjà présente" else ufw allow "$rule" > /dev/null 2>&1 ok "Règle UFW ajoutée : $rule" RULES_ADDED=1 fi done if [ "$RULES_ADDED" -eq 1 ]; then ufw reload ok "Firewall rechargé" else skip "Firewall — aucune règle à ajouter" fi else skip "UFW non installé — firewall ignoré" fi # ============================================================================= # SYSCTL & LIMITES # ============================================================================= SYSCTL_FILE="/etc/sysctl.d/99-chatroulette.conf" LIMITS_FILE="/etc/security/limits.d/chatroulette.conf" if [ -f "$SYSCTL_FILE" ]; then skip "sysctl 99-chatroulette.conf déjà présent" else cat > "$SYSCTL_FILE" < "$LIMITS_FILE" <