From a9fe6c1fc6c9d74e3ec99b839b1c94fa2d70da67 Mon Sep 17 00:00:00 2001 From: sepehr Date: Sun, 10 May 2026 12:02:44 +0200 Subject: [PATCH] feat: add interactive .env setup wizard Interactive bash script for server configuration: - Generates all secrets automatically (JWT, admin token, DB password) - Hashes admin password with bcrypt - Configurable translation providers, Stripe, Grafana - Validates inputs and confirms before writing - Secures .env with chmod 600 Usage on server: bash scripts/setup-env.sh Co-Authored-By: Claude Opus 4.7 --- scripts/setup-env.sh | 374 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 scripts/setup-env.sh diff --git a/scripts/setup-env.sh b/scripts/setup-env.sh new file mode 100644 index 0000000..e7a1f72 --- /dev/null +++ b/scripts/setup-env.sh @@ -0,0 +1,374 @@ +#!/bin/bash +# ============================================ +# Wordly.art - Configuration Wizard +# ============================================ +# Script interactif pour configurer le .env +# Lancer sur le serveur: bash scripts/setup-env.sh +# ============================================ + +set -e + +# Couleurs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +ENV_FILE=".env" + +echo "" +echo -e "${CYAN}${BOLD}=========================================${NC}" +echo -e "${CYAN}${BOLD} Wordly.art - Configuration Wizard${NC}" +echo -e "${CYAN}${BOLD}=========================================${NC}" +echo "" + +# Verifier qu'on est dans le bon dossier +if [ ! -f "docker-compose.yml" ]; then + echo -e "${RED}Erreur: lance ce script depuis la racine du projet (cd /opt/wordly)${NC}" + echo "Usage: bash scripts/setup-env.sh" + exit 1 +fi + +# Fonction pour demander une valeur avec defaut +ask() { + local prompt="$1" + local default="$2" + local varname="$3" + local required="$4" + + if [ -n "$default" ]; then + echo -ne "${YELLOW}${prompt}${NC} [${default}]: " + else + echo -ne "${YELLOW}${prompt}${NC}: " + fi + + read -r answer + answer="${answer:-$default}" + + if [ "$required" = "true" ] && [ -z "$answer" ]; then + echo -e "${RED}Cette valeur est obligatoire!${NC}" + ask "$prompt" "$default" "$varname" "$required" + return + fi + + eval "$varname=\"\$answer\"" +} + +# Fonction pour demander un mot de passe cache +ask_password() { + local prompt="$1" + local varname="$2" + + echo -ne "${YELLOW}${prompt}${NC}: " + read -rs answer + echo "" + eval "$varname=\"\$answer\"" +} + +# Fonction pour generer un secret +generate_secret() { + python3 -c "import secrets; print(secrets.token_urlsafe(64))" 2>/dev/null || \ + openssl rand -base64 48 | tr -d '\n' +} + +generate_hex() { + python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null || \ + openssl rand -hex 32 +} + +generate_password() { + python3 -c "import secrets; print(secrets.token_urlsafe(32))" 2>/dev/null || \ + openssl rand -base64 24 | tr -d '\n' +} + +# =========================================== +# ETAPE 1 : Domaine +# =========================================== +echo -e "${BOLD}--- Domaine & DNS ---${NC}" +echo "" +ask "Nom de domaine" "wordly.art" DOMAIN true + +# =========================================== +# ETAPE 2 : Base de donnees +# =========================================== +echo "" +echo -e "${BOLD}--- Base de donnees PostgreSQL ---${NC}" +echo "" +ask "Utilisateur PostgreSQL" "translate" POSTGRES_USER true +POSTGRES_PASSWORD=$(generate_password) +echo -e " ${GREEN}Mot de passe genere automatiquement${NC}" + +# =========================================== +# ETAPE 3 : Admin +# =========================================== +echo "" +echo -e "${BOLD}--- Compte Administrateur ---${NC}" +echo "" +ask "Nom d'utilisateur admin" "admin" ADMIN_USERNAME true +ask_password "Mot de passe admin (cache)" ADMIN_PASSWORD +if [ -z "$ADMIN_PASSWORD" ]; then + echo -e "${RED}Le mot de passe admin est obligatoire!${NC}" + ask_password "Mot de passe admin (cache)" ADMIN_PASSWORD +fi +echo "" +ask_password "Confirmer le mot de passe" ADMIN_PASSWORD_CONFIRM +if [ "$ADMIN_PASSWORD" != "$ADMIN_PASSWORD_CONFIRM" ]; then + echo -e "${RED}Les mots de passe ne correspondent pas!${NC}" + exit 1 +fi +echo -e " ${GREEN}Mot de passe confirme${NC}" + +# Generer le hash bcrypt +echo -e " ${CYAN}Generation du hash bcrypt...${NC}" +if command -v docker &> /dev/null; then + ADMIN_PASSWORD_HASH=$(docker run --rm python:3.12-slim bash -c \ + "pip install 'passlib[bcrypt]' bcrypt > /dev/null 2>&1 && \ + python3 -c \"from passlib.context import CryptContext; \ + print(CryptContext(schemes=['bcrypt']).hash('${ADMIN_PASSWORD}'))\"" 2>/dev/null) +fi + +if [ -z "$ADMIN_PASSWORD_HASH" ]; then + echo -e " ${YELLOW}Impossible de generer le hash bcrypt automatiquement.${NC}" + echo -e " ${YELLOW}Tu devras le generer manuellement plus tard.${NC}" + ADMIN_PASSWORD_HASH="CHANGE_WITH_BCRYPT_HASH" +else + echo -e " ${GREEN}Hash bcrypt genere${NC}" +fi + +# =========================================== +# ETAPE 4 : Secrets +# =========================================== +echo "" +echo -e "${BOLD}--- Secrets de securite ---${NC}" +echo -e " ${CYAN}Generation automatique des secrets...${NC}" +JWT_SECRET_KEY=$(generate_secret) +ADMIN_TOKEN_SECRET=$(generate_hex) +echo -e " ${GREEN}JWT_SECRET_KEY genere${NC}" +echo -e " ${GREEN}ADMIN_TOKEN_SECRET genere${NC}" + +# =========================================== +# ETAPE 5 : Services de traduction +# =========================================== +echo "" +echo -e "${BOLD}--- Services de traduction ---${NC}" +echo "" +echo "Quel service de traduction par defaut ?" +echo " 1) ollama (local, gratuit)" +echo " 2) google (API payante)" +echo " 3) deepl (API payante, haute qualite)" +echo " 4) openai (GPT, payant)" +echo " 5) openrouter (multi-modeles, payant)" +ask "Choix (1-5)" "1" TRANSLATION_CHOICE + +case "$TRANSLATION_CHOICE" in + 1) TRANSLATION_SERVICE="ollama" ;; + 2) TRANSLATION_SERVICE="google" ;; + 3) TRANSLATION_SERVICE="deepl" ;; + 4) TRANSLATION_SERVICE="openai" ;; + 5) TRANSLATION_SERVICE="openrouter" ;; + *) TRANSLATION_SERVICE="ollama" ;; +esac + +DEEPL_API_KEY="" +OPENAI_API_KEY="" +OPENROUTER_API_KEY="" + +if [ "$TRANSLATION_SERVICE" = "ollama" ]; then + ask "URL Ollama" "http://ollama:11434" OLLAMA_BASE_URL + ask "Modele Ollama" "llama3" OLLAMA_MODEL +fi + +if [ "$TRANSLATION_SERVICE" = "deepl" ] || [ "$TRANSLATION_SERVICE" = "all" ]; then + ask "Cle API DeepL (laisser vide si pas maintenant)" "" DEEPL_API_KEY +fi + +if [ "$TRANSLATION_SERVICE" = "openai" ] || [ "$TRANSLATION_SERVICE" = "all" ]; then + ask "Cle API OpenAI (laisser vide si pas maintenant)" "" OPENAI_API_KEY +fi + +if [ "$TRANSLATION_SERVICE" = "openrouter" ] || [ "$TRANSLATION_SERVICE" = "all" ]; then + ask "Cle API OpenRouter (laisser vide si pas maintenant)" "" OPENROUTER_API_KEY +fi + +# =========================================== +# ETAPE 6 : Monitoring +# =========================================== +echo "" +echo -e "${BOLD}--- Monitoring Grafana ---${NC}" +echo "" +ask "Utilisateur Grafana" "admin" GRAFANA_USER true +GRAFANA_PASSWORD=$(generate_password) +echo -e " ${GREEN}Mot de passe Grafana genere: ${GRAFANA_PASSWORD}${NC}" + +# =========================================== +# ETAPE 7 : Stripe (optionnel) +# =========================================== +echo "" +echo -e "${BOLD}--- Paiements Stripe (optionnel) ---${NC}" +echo "" +ask "Configurer Stripe maintenant ? (oui/non)" "non" SETUP_STRIPE +STRIPE_SECRET_KEY="" +STRIPE_WEBHOOK_SECRET="" +STRIPE_STARTER_PRICE_ID="" +STRIPE_PRO_PRICE_ID="" +STRIPE_BUSINESS_PRICE_ID="" + +if [ "$SETUP_STRIPE" = "oui" ]; then + ask "Cle secrete Stripe (sk_live_...)" "" STRIPE_SECRET_KEY + ask "Webhook secret (whsec_...)" "" STRIPE_WEBHOOK_SECRET + ask "Price ID Starter" "" STRIPE_STARTER_PRICE_ID + ask "Price ID Pro" "" STRIPE_PRO_PRICE_ID + ask "Price ID Business" "" STRIPE_BUSINESS_PRICE_ID +fi + +# =========================================== +# RESUME +# =========================================== +echo "" +echo -e "${CYAN}${BOLD}=========================================${NC}" +echo -e "${CYAN}${BOLD} Resume de la configuration${NC}" +echo -e "${CYAN}${BOLD}=========================================${NC}" +echo "" +echo -e " Domaine: ${BOLD}${DOMAIN}${NC}" +echo -e " Traduction: ${BOLD}${TRANSLATION_SERVICE}${NC}" +echo -e " DB User: ${BOLD}${POSTGRES_USER}${NC}" +echo -e " Admin: ${BOLD}${ADMIN_USERNAME}${NC}" +echo -e " Admin hash: ${BOLD}${ADMIN_PASSWORD_HASH:0:20}...${NC}" +echo -e " JWT Secret: ${BOLD}${JWT_SECRET_KEY:0:20}...${NC}" +echo -e " Admin Token: ${BOLD}${ADMIN_TOKEN_SECRET:0:20}...${NC}" +echo -e " Grafana: ${BOLD}${GRAFANA_USER} / ${GRAFANA_PASSWORD}${NC}" +echo "" +if [ -n "$STRIPE_SECRET_KEY" ]; then + echo -e " Stripe: ${GREEN}Configure${NC}" +else + echo -e " Stripe: ${YELLOW}Non configure${NC}" +fi +echo "" + +# Confirmation +ask "Cette configuration est correcte ? (oui/non)" "oui" CONFIRM +if [ "$CONFIRM" != "oui" ]; then + echo "Annule. Relance le script pour recommencer." + exit 0 +fi + +# =========================================== +# GENERATION DU FICHIER .env +# =========================================== +echo "" +echo -e "${CYAN}Generation du fichier .env...${NC}" + +cat > "$ENV_FILE" < "$INFO_FILE" <