#!/bin/bash cat << "EOF" ██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ███████╗ ██╔════╝██║ ██║██╔══██╗████╗ ██║██╔════╝ ██╔════╝ ██║ ███████║███████║██╔██╗ ██║██║ ███╗█████╗ ██║ ██╔══██║██╔══██║██║╚██╗██║██║ ██║██╔══╝ ╚██████╗██║ ██║██║ ██║██║ ╚████║╚██████╔╝███████╗ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ███╗ ███╗ █████╗ ██╗ ██╗███████╗██████╗ ████╗ ████║██╔══██╗██║ ██╔╝██╔════╝██╔══██╗ ██╔████╔██║███████║█████╔╝ █████╗ ██████╔╝ ██║╚██╔╝██║██╔══██║██╔═██╗ ██╔══╝ ██╔══██╗ ██║ ╚═╝ ██║██║ ██║██║ ██╗███████╗██║ ██║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ Configuration Wizard EOF # Get the absolute path of the script directory SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ENV_FILE="$SCRIPT_DIR/.env" MKDOCS_YML="$SCRIPT_DIR/mkdocs/mkdocs.yml" # Fixed path - removed extra 'docs' directory TUNNEL_CONFIG_DIR="$SCRIPT_DIR/configs/cloudflare" TUNNEL_CONFIG_FILE="$TUNNEL_CONFIG_DIR/tunnel-config.yml" SERVICES_YAML="$SCRIPT_DIR/configs/homepage/services.yaml" MAIN_HTML="$SCRIPT_DIR/mkdocs/docs/overrides/main.html" echo "Looking for .env file at: $ENV_FILE" # Function to generate a random secure password generate_password() { local length=${1:-16} openssl rand -base64 48 | tr -dc 'a-zA-Z0-9!@#$%^&*()-_=+' | head -c "$length" } # Function to safely update environment variables in .env file update_env_var() { local key=$1 local value=$2 # More robust method to handle special characters in passwords if grep -q "^$key=" "$ENV_FILE"; then # Create a temporary file local tmpfile=$(mktemp) # Process the .env file line by line while IFS= read -r line; do if [[ "$line" =~ ^$key= ]]; then echo "$key=$value" >> "$tmpfile" else echo "$line" >> "$tmpfile" fi done < "$ENV_FILE" # Replace the original file with the temporary file mv "$tmpfile" "$ENV_FILE" echo "Updated $key in .env file" else # Add new key-value pair if it doesn't exist echo "$key=$value" >> "$ENV_FILE" echo "Added $key to .env file" fi } # Function to create a timestamped backup of the .env file backup_env_file() { if [ -f "$ENV_FILE" ]; then local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="$ENV_FILE.backup_$timestamp" echo "Creating backup of current .env file to: $backup_file" if cp "$ENV_FILE" "$backup_file"; then echo "Backup created successfully!" return 0 else echo "Failed to create backup file. Proceeding with caution..." return 1 fi fi } # Function to get all used ports on the system get_used_ports() { local used_ports=() # Try different methods to get used ports, in order of preference if command -v ss >/dev/null 2>&1; then # Use ss (preferred, modern replacement for netstat) while IFS= read -r line; do if [[ "$line" =~ :([0-9]+)[[:space:]] ]]; then used_ports+=("${BASH_REMATCH[1]}") fi done < <(ss -tuln 2>/dev/null | grep -E ':[0-9]+[[:space:]]') elif command -v netstat >/dev/null 2>&1; then # Use netstat (fallback) while IFS= read -r line; do if [[ "$line" =~ :([0-9]+)[[:space:]] ]]; then used_ports+=("${BASH_REMATCH[1]}") fi done < <(netstat -tuln 2>/dev/null | grep -E ':[0-9]+[[:space:]]') elif command -v lsof >/dev/null 2>&1; then # Use lsof (another fallback) while IFS= read -r line; do if [[ "$line" =~ :([0-9]+)[[:space:]] ]]; then used_ports+=("${BASH_REMATCH[1]}") fi done < <(lsof -i -P -n 2>/dev/null | grep LISTEN | grep -E ':[0-9]+[[:space:]]') else echo "Warning: No port scanning tools available (ss, netstat, or lsof)" return 1 fi # Remove duplicates and sort printf '%s\n' "${used_ports[@]}" | sort -nu } # Function to check if a specific port is available is_port_available() { local port=$1 local used_ports_list=$2 # Check if port is in the used ports list if echo "$used_ports_list" | grep -q "^$port$"; then return 1 # Port is in use else return 0 # Port is available fi } # Function to find next available port starting from a given port find_next_available_port() { local start_port=$1 local used_ports_list=$2 local max_attempts=${3:-100} # Maximum ports to check for ((i=0; i&2 return 1 } # Function to initialize available ports for services initialize_available_ports() { echo "Scanning system for used ports to avoid conflicts..." # Get list of all used ports local used_ports_list used_ports_list=$(get_used_ports) if [ $? -ne 0 ] || [ -z "$used_ports_list" ]; then echo "Warning: Could not scan system ports. Using default ports." echo "You may need to manually resolve conflicts later." return 1 fi echo "Found $(echo "$used_ports_list" | wc -l) ports currently in use" # Define desired starting ports for each service local -A service_ports=( ["CODE_SERVER_PORT"]=8888 ["LISTMONK_PORT"]=9000 ["LISTMONK_DB_PORT"]=5432 ["MKDOCS_PORT"]=4000 ["MKDOCS_SITE_SERVER_PORT"]=4001 ["N8N_PORT"]=5678 ["NOCODB_PORT"]=8090 ["HOMEPAGE_PORT"]=3010 ["GITEA_WEB_PORT"]=3030 ["GITEA_SSH_PORT"]=2222 ) # Find available ports for each service local -A available_ports for service in "${!service_ports[@]}"; do local preferred_port=${service_ports[$service]} local available_port if is_port_available "$preferred_port" "$used_ports_list"; then available_port=$preferred_port echo "✅ $service: Using preferred port $preferred_port" else available_port=$(find_next_available_port "$preferred_port" "$used_ports_list") if [ $? -eq 0 ]; then echo "⚠️ $service: Port $preferred_port in use, using $available_port instead" else echo "❌ $service: Could not find available port, using $preferred_port (may conflict)" available_port=$preferred_port fi fi available_ports[$service]=$available_port # Add the assigned port to used_ports_list to avoid double-assignment used_ports_list=$(printf '%s\n%s' "$used_ports_list" "$available_port" | sort -nu) done # Export the available ports as global variables for service in "${!available_ports[@]}"; do export "$service=${available_ports[$service]}" done echo "Port assignment completed successfully!" return 0 } # Function to initialize the .env file with default values initialize_env_file() { echo "Initializing new .env file with available ports..." # Initialize available ports before creating the .env file initialize_available_ports cat > "$ENV_FILE" << EOL # Never share this file publicly. It contains sensitive information. # This file is used to configure various applications and services. # Generated by Changemaker Config Wizard on $(date) # User and Group Configuration USER_NAME=coder USER_ID=1000 GROUP_ID=1000 # Port Configuration (automatically assigned to avoid conflicts) CODE_SERVER_PORT=${CODE_SERVER_PORT:-8888} LISTMONK_PORT=${LISTMONK_PORT:-9000} LISTMONK_DB_PORT=${LISTMONK_DB_PORT:-5432} MKDOCS_PORT=${MKDOCS_PORT:-4000} MKDOCS_SITE_SERVER_PORT=${MKDOCS_SITE_SERVER_PORT:-4001} N8N_PORT=${N8N_PORT:-5678} NOCODB_PORT=${NOCODB_PORT:-8090} HOMEPAGE_PORT=${HOMEPAGE_PORT:-3010} GITEA_WEB_PORT=${GITEA_WEB_PORT:-3030} GITEA_SSH_PORT=${GITEA_SSH_PORT:-2222} # Domain Configuration BASE_DOMAIN=https://changeme.org DOMAIN=changeme.org LISTMONK_HOSTNAME=listmonk.changeme.org N8N_HOST=n8n.changeme.org GITEA_DOMAIN=git.changeme.org GITEA_ROOT_URL=https://git.changeme.org # Cloudflare Configuration CF_API_TOKEN=your_cloudflare_api_token CF_ZONE_ID=your_cloudflare_zone_id CF_TUNNEL_ID=your_cloudflared_tunnel_id CF_DOMAIN=changeme.org # Database Configuration (PostgreSQL for Listmonk) POSTGRES_USER=listmonk POSTGRES_PASSWORD=changeMe POSTGRES_DB=listmonk # Listmonk Database Performance Settings LISTMONK_DB_MAX_OPEN=25 LISTMONK_DB_MAX_IDLE=25 LISTMONK_DB_MAX_LIFETIME=300s # Listmonk Admin Configuration LISTMONK_ADMIN_USER=admin LISTMONK_ADMIN_PASSWORD=changeMe # N8N Configuration N8N_USER_EMAIL=admin@example.com N8N_USER_PASSWORD=changeMe N8N_ENCRYPTION_KEY=changeMe GENERIC_TIMEZONE=UTC # Nocodb Configuration NOCODB_PORT=${NOCODB_PORT:-8090} NOCODB_JWT_SECRET=changeMe NOCODB_DB_NAME=nocodb NOCODB_DB_USER=noco NOCODB_DB_PASSWORD=changeMe HOMEPAGE_VAR_BASE_URL=https://changeme.org EOL echo "New .env file created with conflict-free port assignments." # Show port assignments summary echo "" echo "=== Port Assignments Summary ===" echo "Code Server: ${CODE_SERVER_PORT:-8888}" echo "Listmonk: ${LISTMONK_PORT:-9000}" echo "Listmonk DB: ${LISTMONK_DB_PORT:-5432}" echo "MkDocs: ${MKDOCS_PORT:-4000}" echo "MkDocs Site: ${MKDOCS_SITE_SERVER_PORT:-4001}" echo "N8N: ${N8N_PORT:-5678}" echo "NocoDB: ${NOCODB_PORT:-8090}" echo "Homepage: ${HOMEPAGE_PORT:-3010}" echo "Gitea Web: ${GITEA_WEB_PORT:-3030}" echo "Gitea SSH: ${GITEA_SSH_PORT:-2222}" echo "================================" } # Function to update the site_url in mkdocs.yml update_mkdocs_yml() { local new_domain=$1 if [ ! -f "$MKDOCS_YML" ]; then echo "Warning: mkdocs.yml not found at $MKDOCS_YML" return 1 fi echo "Updating site_url in mkdocs.yml..." # Create a backup of the mkdocs.yml file local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="${MKDOCS_YML}.backup_${timestamp}" cp "$MKDOCS_YML" "$backup_file" echo "Created backup of mkdocs.yml at $backup_file" # Update the site_url value - handle both http and https sed -i "s|^site_url:.*|site_url: https://$new_domain|" "$MKDOCS_YML" if grep -q "site_url: https://$new_domain" "$MKDOCS_YML"; then echo "✅ Updated site_url in mkdocs.yml to: https://$new_domain" return 0 else echo "Warning: Failed to update site_url in mkdocs.yml" return 1 fi } # Function to update service URLs in services.yaml update_services_yaml() { local new_domain=$1 if [ ! -f "$SERVICES_YAML" ]; then echo "Warning: services.yaml not found at $SERVICES_YAML" return 1 fi echo "Updating service URLs in services.yaml..." # Create a backup of the services.yaml file local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="${SERVICES_YAML}.backup_${timestamp}" cp "$SERVICES_YAML" "$backup_file" echo "Created backup of services.yaml at $backup_file" # Update all domain references - handle the current domain (albertademocracytaskforce.org) # First, update any existing domain to the new domain sed -i "s|albertademocracytaskforce\.org|$new_domain|g" "$SERVICES_YAML" # Also update any changeme.org references that might exist sed -i "s|changeme\.org|$new_domain|g" "$SERVICES_YAML" # Update specific service URLs with proper formatting sed -i "s|# href: \"https://code\.[^\"]*\"|# href: \"https://code.$new_domain\"|g" "$SERVICES_YAML" sed -i "s|# href: \"https://listmonk\.[^\"]*\"|# href: \"https://listmonk.$new_domain\"|g" "$SERVICES_YAML" sed -i "s|# href: \"https://db\.[^\"]*\"|# href: \"https://db.$new_domain\"|g" "$SERVICES_YAML" sed -i "s|# href: \"https://git\.[^\"]*\"|# href: \"https://git.$new_domain\"|g" "$SERVICES_YAML" sed -i "s|# href: \"https://docs\.[^\"]*\"|# href: \"https://docs.$new_domain\"|g" "$SERVICES_YAML" sed -i "s|# href: \"https://n8n\.[^\"]*\"|# href: \"https://n8n.$new_domain\"|g" "$SERVICES_YAML" # Update root domain reference sed -i "s|# href: \"https://[^/\"]*\"|# href: \"https://$new_domain\"|g" "$SERVICES_YAML" | tail -1 echo "✅ Updated service URLs in services.yaml to use domain: $new_domain" return 0 } # Function to update the login URL in main.html update_main_html() { local new_domain=$1 if [ ! -f "$MAIN_HTML" ]; then echo "Warning: main.html not found at $MAIN_HTML" return 1 fi echo "Updating login URL in main.html..." # Create a backup of the main.html file local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="${MAIN_HTML}.backup_${timestamp}" cp "$MAIN_HTML" "$backup_file" echo "Created backup of main.html at $backup_file" # Update the login button href - handle current domain sed -i "s|href=\"https://homepage\.[^\"]*\"|href=\"https://homepage.$new_domain\"|g" "$MAIN_HTML" # Also update any albertademocracytaskforce.org references sed -i "s|albertademocracytaskforce\.org|$new_domain|g" "$MAIN_HTML" # Update any test.com or changeme.org references sed -i "s|test\.com|$new_domain|g" "$MAIN_HTML" sed -i "s|changeme\.org|$new_domain|g" "$MAIN_HTML" echo "✅ Updated login URL in main.html to: https://homepage.$new_domain" return 0 } # Function to update the tunnel configuration file with new domain update_tunnel_config() { local new_domain=$1 if [ ! -f "$TUNNEL_CONFIG_FILE" ]; then echo "Warning: tunnel-config.yml not found at $TUNNEL_CONFIG_FILE" echo "Creating new tunnel configuration file..." # Create the file if it doesn't exist create_tunnel_config "$new_domain" "${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}}" return $? fi echo "Updating tunnel configuration with new domain..." # Create a backup of the tunnel-config.yml file local timestamp=$(date +"%Y%m%d_%H%M%S") local backup_file="${TUNNEL_CONFIG_FILE}.backup_${timestamp}" cp "$TUNNEL_CONFIG_FILE" "$backup_file" echo "Created backup of tunnel-config.yml at $backup_file" # Since the current file has all entries as cmlite.org pointing to port 3010, # we need to recreate it with the proper configuration echo "Regenerating tunnel configuration with correct service mappings..." # Load current environment variables to get port numbers load_env_vars # Recreate the tunnel configuration with proper mappings cat > "$TUNNEL_CONFIG_FILE" << EOL # filepath: /home/bunker-admin/changemaker.lite/configs/cloudflare/tunnel-config.yml # Cloudflare Tunnel Configuration # Auto-generated by Changemaker Configuration Wizard tunnel: ${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}} # e.g. 1234567890abcdef credentials-file: /home/coder/.cloudflared/${CF_TUNNEL_ID:-\${CF_TUNNEL_ID}}.json # e.g. /home/coder/.cloudflared/[insert tunnel number].json ingress: - hostname: homepage.$new_domain service: http://localhost:${HOMEPAGE_PORT:-3010} - hostname: code.$new_domain service: http://localhost:${CODE_SERVER_PORT:-8888} - hostname: listmonk.$new_domain service: http://localhost:${LISTMONK_PORT:-9000} - hostname: docs.$new_domain service: http://localhost:${MKDOCS_PORT:-4000} - hostname: $new_domain service: http://localhost:${MKDOCS_SITE_SERVER_PORT:-4001} - hostname: n8n.$new_domain service: http://localhost:${N8N_PORT:-5678} - hostname: db.$new_domain service: http://localhost:${NOCODB_PORT:-8090} - hostname: git.$new_domain service: http://localhost:${GITEA_WEB_PORT:-3030} # Catch-all rule (required) - service: http_status:404 EOL echo "✅ Regenerated tunnel configuration with correct service mappings for domain: $new_domain" return 0 } # Function to get Cloudflare API Token and Zone ID get_cloudflare_credentials() { echo "" echo "To use the DNS setup script, you'll need Cloudflare credentials." echo "You can find these values in your Cloudflare dashboard:" echo " - API Token: https://dash.cloudflare.com/profile/api-tokens" echo " (Create a token with Zone:DNS:Edit and Access:Apps:Edit permissions)" echo " - Zone ID: On your domain's overview page" echo "" } # Function to show tunnel setup instructions show_tunnel_instructions() { local domain=$1 echo "" echo "=== Cloudflare Tunnel Setup Instructions ===" echo "" echo "To complete the tunnel setup:" echo "" echo "1. Install cloudflared on your server:" echo " https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation" echo "" echo "2. Create a tunnel using the Cloudflare dashboard or run:" echo " cloudflared tunnel create changemaker-tunnel" echo "" echo "3. Copy the credentials file to the correct location:" echo " mkdir -p /home/coder/.cloudflared" echo " cp ~/.cloudflared/[TUNNEL-ID].json /home/coder/.cloudflared/" echo "" echo "4. Update your .env with the correct CF_TUNNEL_ID if not already done" echo "" echo "5. Start the tunnel with your configuration file:" echo " cloudflared tunnel --config $TUNNEL_CONFIG_FILE run" echo "" echo "6. After verifying it works, you can create a systemd service for automatic startup:" echo " sudo cloudflared service install" echo "" echo "7. Your services will be available at the following URLs:" echo " - Documentation: https://$domain" echo " - Homepage: https://homepage.$domain" echo " - Code Server: https://code.$domain" echo " - Listmonk: https://listmonk.$domain" echo " - N8N: https://n8n.$domain" echo " - NocoDB: https://db.$domain" echo " - MkDocs Dev: https://docs.$domain" echo " - Gitea: https://git.$domain" echo "" } # Function to load environment variables from .env file load_env_vars() { if [ -f "$ENV_FILE" ]; then # Load variables from .env file, ignoring comments and empty lines while IFS= read -r line; do if [[ "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then export "$line" fi done < <(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE") fi } # Initialize a new .env file if it doesn't exist if [ ! -f "$ENV_FILE" ]; then echo "No .env file found. Creating a new one from scratch." touch "$ENV_FILE" initialize_env_file else echo "Found existing .env file. Will update values." backup_env_file # For existing .env files, also scan ports and suggest alternatives if conflicts exist echo "" echo "Checking existing port assignments for conflicts..." load_env_vars check_port_conflicts fi # Load existing environment variables load_env_vars echo -e "\n\nWelcome to Changemaker Config!\n" echo "This script will help you configure your .env file for Changemaker." echo "Please provide the following information:" # Domain configuration read -p "Enter your domain name (without protocol, e.g., example.com): " domain_name if [ -z "$domain_name" ]; then echo "Domain name cannot be empty. Using default: changeme.org" domain_name="changeme.org" fi echo -e "\nUpdating domain settings in .env file..." # Update main domain settings update_env_var "DOMAIN" "$domain_name" update_env_var "BASE_DOMAIN" "https://$domain_name" update_env_var "HOMEPAGE_VAR_BASE_URL" "https://$domain_name" update_env_var "LISTMONK_HOSTNAME" "listmonk.$domain_name" update_env_var "N8N_HOST" "n8n.$domain_name" update_env_var "CF_DOMAIN" "$domain_name" update_env_var "GITEA_DOMAIN" "git.$domain_name" update_env_var "GITEA_ROOT_URL" "https://git.$domain_name" echo "Domain settings updated successfully!" # Cloudflare Configuration echo -e "\n---- Cloudflare Configuration ----" get_cloudflare_credentials read -p "Do you want to configure Cloudflare settings now? [Y/n]: " configure_cf if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then # Get Cloudflare API Token read -p "Enter your Cloudflare API Token: " cf_api_token while [ -z "$cf_api_token" ] || [ "$cf_api_token" == "your_cloudflare_api_token" ]; do echo "API Token is required. Please enter a valid token." read -p "Enter your Cloudflare API Token: " cf_api_token done # Get Cloudflare Zone ID read -p "Enter your Cloudflare Zone ID: " cf_zone_id while [ -z "$cf_zone_id" ] || [ "$cf_zone_id" == "your_cloudflare_zone_id" ]; do echo "Zone ID is required. Please enter a valid Zone ID." read -p "Enter your Cloudflare Zone ID: " cf_zone_id done # Optional: Get Account ID now read -p "Enter your Cloudflare Account ID (optional, can be added later): " cf_account_id # Update .env file with Cloudflare credentials update_env_var "CF_API_TOKEN" "$cf_api_token" update_env_var "CF_ZONE_ID" "$cf_zone_id" update_env_var "CF_DOMAIN" "$domain_name" if [ ! -z "$cf_account_id" ]; then update_env_var "CF_ACCOUNT_ID" "$cf_account_id" fi echo "Cloudflare credentials saved successfully!" else echo "Skipping Cloudflare configuration. You can configure it later by editing the .env file." fi # Update the site_url in mkdocs.yml echo -e "\nUpdating site_url in mkdocs.yml..." update_mkdocs_yml "$domain_name" # Update service URLs in services.yaml echo -e "\nUpdating service URLs in services.yaml..." update_services_yaml "$domain_name" # Update the login URL in main.html echo -e "\nUpdating login URL in main.html..." update_main_html "$domain_name" # Update the tunnel configuration file echo -e "\nUpdating tunnel configuration..." update_tunnel_config "$domain_name" # Listmonk Admin Credentials configuration echo -e "\n---- Listmonk Admin Credentials ----" read -p "Enter Listmonk admin email/username [default: admin@example.com]: " listmonk_user read -sp "Enter Listmonk admin password [default: changeMe]: " listmonk_password echo # Add new line after password input if [ -z "$listmonk_user" ]; then listmonk_user="admin@example.com" fi if [ -z "$listmonk_password" ]; then listmonk_password="changeMe" fi update_env_var "LISTMONK_ADMIN_USER" "$listmonk_user" update_env_var "LISTMONK_ADMIN_PASSWORD" "$listmonk_password" echo "Listmonk admin credentials updated." # N8N User Credentials configuration echo -e "\n---- N8N Admin Credentials ----" read -p "Enter N8N admin email [default: admin@example.com]: " n8n_email read -sp "Enter N8N admin password [default: changeMe]: " n8n_password echo # Add new line after password input if [ -z "$n8n_email" ]; then n8n_email="admin@example.com" fi if [ -z "$n8n_password" ]; then n8n_password="changeMe" fi update_env_var "N8N_USER_EMAIL" "$n8n_email" update_env_var "N8N_USER_PASSWORD" "$n8n_password" echo "N8N admin credentials updated." # Generate secure passwords for database and encryption echo -e "\n---- Generating Secure Passwords ----" echo "Generating secure passwords for database and encryption keys..." # Generate and update database password postgres_password=$(generate_password 20) update_env_var "POSTGRES_PASSWORD" "$postgres_password" # Generate and update N8N encryption key n8n_encryption_key=$(generate_password 32) update_env_var "N8N_ENCRYPTION_KEY" "$n8n_encryption_key" # Generate and update NocoDB passwords nocodb_jwt_secret=$(generate_password 32) update_env_var "NOCODB_JWT_SECRET" "$nocodb_jwt_secret" nocodb_db_password=$(generate_password 20) update_env_var "NOCODB_DB_PASSWORD" "$nocodb_db_password" # Generate and update Gitea passwords gitea_db_password=$(generate_password 20) update_env_var "GITEA_DB_PASSWD" "$gitea_db_password" gitea_db_root_password=$(generate_password 20) update_env_var "GITEA_DB_ROOT_PASSWORD" "$gitea_db_root_password" echo "Secure passwords generated and updated." echo -e "\n✅ Configuration completed successfully!" echo "Your .env file has been configured with:" echo "- Domain: $domain_name" echo "- Listmonk Admin: $listmonk_user" echo "- N8N Admin Email: $n8n_email" echo "- Secure random passwords for database, encryption, and NocoDB" if [[ ! "$configure_cf" =~ ^[Nn]$ ]]; then echo "- Cloudflare credentials for DNS management" fi echo -e "\nYour .env file is located at: $ENV_FILE" echo "A backup of your original .env file was created before modifications." echo "" echo "======================================" echo "Next Steps:" echo "======================================" echo "" echo "1. Start services locally:" echo " docker compose up -d" echo "" echo "2. Test all services at:" echo " - Homepage: http://localhost:${HOMEPAGE_PORT:-3010}" echo " - Code Server: http://localhost:${CODE_SERVER_PORT:-8888}" echo " - Listmonk: http://localhost:${LISTMONK_PORT:-9000}" echo " - Documentation: http://localhost:${MKDOCS_PORT:-4000}" echo " - n8n: http://localhost:${N8N_PORT:-5678}" echo " - NocoDB: http://localhost:${NOCODB_PORT:-8090}" echo " - Gitea: http://localhost:${GITEA_WEB_PORT:-3030}" echo "" echo "3. When ready for production:" echo " ./start-production.sh" echo "" echo "======================================"