mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 14:23:08 +00:00
adds appsmith as an optional service with caddy reverse proxy, auto-generated encryption secrets, wizard selection, welcome page integration, update preview support, and final report output. bumps version to 1.3.0.
604 lines
25 KiB
Bash
Executable File
604 lines
25 KiB
Bash
Executable File
#!/bin/bash
|
|
# =============================================================================
|
|
# 03_generate_secrets.sh - Secret and configuration generator
|
|
# =============================================================================
|
|
# Generates secure passwords, JWT secrets, API keys, and encryption keys for
|
|
# all services. Creates the .env file from .env.example template.
|
|
#
|
|
# Features:
|
|
# - Generates cryptographically secure random values (passwords, secrets, keys)
|
|
# - Creates bcrypt hashes for Caddy basic auth using `caddy hash-password`
|
|
# - Preserves existing user-provided values in .env on re-run
|
|
# - Supports --update flag to add new variables without regenerating existing
|
|
# - Prompts for domain name and Let's Encrypt email
|
|
#
|
|
# Secret types: password (alphanum), secret (base64), hex, api_key, jwt
|
|
#
|
|
# Usage: bash scripts/03_generate_secrets.sh [--update]
|
|
# =============================================================================
|
|
|
|
set -e
|
|
|
|
# Source the utilities file and initialize paths
|
|
source "$(dirname "$0")/utils.sh"
|
|
init_paths
|
|
|
|
# Source telemetry functions
|
|
source "$SCRIPT_DIR/telemetry.sh"
|
|
|
|
# Setup cleanup for temporary files
|
|
TEMP_FILES=()
|
|
cleanup_temp_files() {
|
|
for f in "${TEMP_FILES[@]}"; do
|
|
rm -f "$f" 2>/dev/null
|
|
done
|
|
}
|
|
trap cleanup_temp_files EXIT
|
|
|
|
# Check for openssl
|
|
require_command "openssl" "Please ensure openssl is installed and available in your PATH."
|
|
|
|
# --- Configuration ---
|
|
TEMPLATE_FILE="$PROJECT_ROOT/.env.example"
|
|
OUTPUT_FILE="$PROJECT_ROOT/.env"
|
|
|
|
# Variables that get assigned the user's email address
|
|
EMAIL_VARS=(
|
|
"COMFYUI_USERNAME"
|
|
"DASHBOARD_USERNAME"
|
|
"DOCLING_USERNAME"
|
|
"LANGFUSE_INIT_USER_EMAIL"
|
|
"LETSENCRYPT_EMAIL"
|
|
"LIGHTRAG_USERNAME"
|
|
"LT_USERNAME"
|
|
"PADDLEOCR_USERNAME"
|
|
"PROMETHEUS_USERNAME"
|
|
"RAGAPP_USERNAME"
|
|
"SEARXNG_USERNAME"
|
|
"TEMPORAL_UI_USERNAME"
|
|
"WAHA_DASHBOARD_USERNAME"
|
|
"WEAVIATE_USERNAME"
|
|
"WELCOME_USERNAME"
|
|
"WHATSAPP_SWAGGER_USERNAME"
|
|
)
|
|
|
|
# All user input variables (EMAIL_VARS plus non-email vars)
|
|
USER_INPUT_VARS=(
|
|
"${EMAIL_VARS[@]}"
|
|
"N8N_WORKER_COUNT"
|
|
"NEO4J_AUTH_USERNAME"
|
|
"OPENAI_API_KEY"
|
|
"RUN_N8N_IMPORT"
|
|
)
|
|
|
|
# Variables to generate: varName="type:length"
|
|
# Types: password (alphanum), secret (base64), hex, base64, alphanum
|
|
declare -A VARS_TO_GENERATE=(
|
|
["APPSMITH_ENCRYPTION_PASSWORD"]="password:32"
|
|
["APPSMITH_ENCRYPTION_SALT"]="password:32"
|
|
["CLICKHOUSE_PASSWORD"]="password:32"
|
|
["COMFYUI_PASSWORD"]="password:32" # Added ComfyUI basic auth password
|
|
["DASHBOARD_PASSWORD"]="password:32" # Supabase Dashboard
|
|
["DIFY_SECRET_KEY"]="secret:64" # Dify application secret key (maps to SECRET_KEY in Dify)
|
|
["DOCLING_PASSWORD"]="password:32"
|
|
["ENCRYPTION_KEY"]="hex:64" # Langfuse Encryption Key (32 bytes -> 64 hex chars)
|
|
["GOST_PASSWORD"]="password:32"
|
|
["GOST_USERNAME"]="fixed:gost"
|
|
["GRAFANA_ADMIN_PASSWORD"]="password:32"
|
|
["JWT_SECRET"]="base64:64" # 48 bytes -> 64 chars
|
|
["LANGFUSE_INIT_PROJECT_PUBLIC_KEY"]="langfuse_pk:32"
|
|
["LANGFUSE_INIT_PROJECT_SECRET_KEY"]="langfuse_sk:32"
|
|
["LANGFUSE_INIT_USER_PASSWORD"]="password:32"
|
|
["LANGFUSE_SALT"]="secret:64" # base64 encoded, 48 bytes -> 64 chars
|
|
["LETTA_SERVER_PASSWORD"]="password:32" # Added Letta server password
|
|
["LIGHTRAG_API_KEY"]="secret:48"
|
|
["LIGHTRAG_PASSWORD"]="password:32"
|
|
["LOGFLARE_PRIVATE_ACCESS_TOKEN"]="fixed:not-in-use" # For supabase-vector, can't be empty
|
|
["LOGFLARE_PUBLIC_ACCESS_TOKEN"]="fixed:not-in-use" # For supabase-vector, can't be empty
|
|
["LT_PASSWORD"]="password:32" # Added LibreTranslate basic auth password
|
|
["MINIO_ROOT_PASSWORD"]="password:32"
|
|
["N8N_ENCRYPTION_KEY"]="secret:64" # base64 encoded, 48 bytes -> 64 chars
|
|
["N8N_RUNNERS_AUTH_TOKEN"]="secret:64" # Task runner auth token for n8n v2.0
|
|
["N8N_USER_MANAGEMENT_JWT_SECRET"]="secret:64" # base64 encoded, 48 bytes -> 64 chars
|
|
["NEO4J_AUTH_PASSWORD"]="password:32" # Added Neo4j password
|
|
["NEO4J_AUTH_USERNAME"]="fixed:neo4j" # Added Neo4j username
|
|
["NEXTAUTH_SECRET"]="secret:64" # base64 encoded, 48 bytes -> 64 chars
|
|
["NOCODB_JWT_SECRET"]="secret:64" # NocoDB authentication JWT secret
|
|
["PADDLEOCR_PASSWORD"]="password:32" # Added PaddleOCR basic auth password
|
|
["PG_META_CRYPTO_KEY"]="alphanum:32"
|
|
["POSTGRES_NON_ROOT_PASSWORD"]="password:32"
|
|
["POSTGRES_PASSWORD"]="password:32"
|
|
["PROMETHEUS_PASSWORD"]="password:32" # Added Prometheus password
|
|
["QDRANT_API_KEY"]="secret:48" # API Key for Qdrant service
|
|
["RAGAPP_PASSWORD"]="password:32" # Added RAGApp basic auth password
|
|
["RAGFLOW_ELASTICSEARCH_PASSWORD"]="password:32"
|
|
["RAGFLOW_MINIO_ROOT_PASSWORD"]="password:32"
|
|
["RAGFLOW_MYSQL_ROOT_PASSWORD"]="password:32"
|
|
["RAGFLOW_REDIS_PASSWORD"]="password:32"
|
|
["SEARXNG_PASSWORD"]="password:32" # Added SearXNG admin password
|
|
["SECRET_KEY_BASE"]="base64:64" # 48 bytes -> 64 chars
|
|
["TEMPORAL_UI_PASSWORD"]="password:32" # Temporal UI basic auth password
|
|
["VAULT_ENC_KEY"]="alphanum:32"
|
|
["WAHA_DASHBOARD_PASSWORD"]="password:32"
|
|
["WEAVIATE_API_KEY"]="secret:48" # API Key for Weaviate service (36 bytes -> 48 chars base64)
|
|
["WELCOME_PASSWORD"]="password:32" # Welcome page basic auth password
|
|
["WHATSAPP_SWAGGER_PASSWORD"]="password:32"
|
|
)
|
|
|
|
# Initialize existing_env_vars and attempt to read .env if it exists
|
|
log_info "Initializing environment configuration..."
|
|
declare -A existing_env_vars
|
|
declare -A generated_values
|
|
|
|
if [ -f "$OUTPUT_FILE" ]; then
|
|
log_info "Found existing $OUTPUT_FILE. Reading its values to use as defaults and preserve current settings."
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
if [[ -n "$line" && ! "$line" =~ ^\s*# && "$line" == *"="* ]]; then
|
|
varName=$(echo "$line" | cut -d'=' -f1 | xargs)
|
|
varValue=$(echo "$line" | cut -d'=' -f2-)
|
|
# Repeatedly unquote "value" or 'value' to get the bare value
|
|
_tempVal="$varValue"
|
|
while true; do
|
|
if [[ "$_tempVal" =~ ^\"(.*)\"$ ]]; then # Check double quotes
|
|
_tempVal="${BASH_REMATCH[1]}"
|
|
continue
|
|
fi
|
|
if [[ "$_tempVal" =~ ^\'(.*)\'$ ]]; then # Check single quotes
|
|
_tempVal="${BASH_REMATCH[1]}"
|
|
continue
|
|
fi
|
|
break # No more surrounding quotes of these types
|
|
done
|
|
varValue="$_tempVal"
|
|
existing_env_vars["$varName"]="$varValue"
|
|
fi
|
|
done < "$OUTPUT_FILE"
|
|
fi
|
|
|
|
# Install Caddy
|
|
log_subheader "Installing Caddy"
|
|
log_info "Adding Caddy repository and installing..."
|
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --yes --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 install -y caddy
|
|
|
|
# Check for caddy
|
|
require_command "caddy" "Caddy installation failed. Please check the installation logs above."
|
|
|
|
require_whiptail
|
|
|
|
# Prompt for the domain name
|
|
log_subheader "Domain Configuration"
|
|
DOMAIN="" # Initialize DOMAIN variable
|
|
|
|
# Try to get domain from existing .env file first
|
|
# Check if USER_DOMAIN_NAME is set in existing_env_vars and is not empty
|
|
if [[ ${existing_env_vars[USER_DOMAIN_NAME]+_} && -n "${existing_env_vars[USER_DOMAIN_NAME]}" ]]; then
|
|
DOMAIN="${existing_env_vars[USER_DOMAIN_NAME]}"
|
|
# Ensure this value is carried over to generated_values for writing and template processing
|
|
# If it came from existing_env_vars, it might already be there, but this ensures it.
|
|
generated_values["USER_DOMAIN_NAME"]="$DOMAIN"
|
|
else
|
|
while true; do
|
|
DOMAIN_INPUT=$(wt_input "Primary Domain" "Enter the primary domain name for your services (e.g., example.com)." "") || true
|
|
|
|
DOMAIN_TO_USE="$DOMAIN_INPUT" # Direct assignment, no default fallback
|
|
|
|
# Validate domain input
|
|
if [[ -z "$DOMAIN_TO_USE" ]]; then
|
|
wt_msg "Validation" "Domain name cannot be empty."
|
|
continue
|
|
fi
|
|
|
|
# Basic check for likely invalid domain characters (very permissive)
|
|
if [[ "$DOMAIN_TO_USE" =~ [^a-zA-Z0-9.-] ]]; then
|
|
wt_msg "Validation" "Warning: Domain contains potentially invalid characters: '$DOMAIN_TO_USE'"
|
|
fi
|
|
if wt_yesno "Confirm Domain" "Use '$DOMAIN_TO_USE' as the primary domain?" "yes"; then
|
|
DOMAIN="$DOMAIN_TO_USE" # Set the final DOMAIN variable
|
|
generated_values["USER_DOMAIN_NAME"]="$DOMAIN" # Using USER_DOMAIN_NAME
|
|
log_info "Domain set to '$DOMAIN'. It will be saved in .env."
|
|
break # Confirmed, exit loop
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Prompt for user email
|
|
log_subheader "Email Configuration"
|
|
if [[ -z "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
|
wt_msg "Email Required" "Please enter your email address. It will be used for logins and Let's Encrypt SSL."
|
|
fi
|
|
|
|
if [[ -n "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
|
USER_EMAIL="${existing_env_vars[LETSENCRYPT_EMAIL]}"
|
|
else
|
|
while true; do
|
|
USER_EMAIL=$(wt_input "Email" "Enter your email address." "") || true
|
|
|
|
# Validate email input
|
|
if [[ -z "$USER_EMAIL" ]]; then
|
|
wt_msg "Validation" "Email cannot be empty."
|
|
continue
|
|
fi
|
|
|
|
# Basic email format validation
|
|
if [[ ! "$USER_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
|
wt_msg "Validation" "Warning: Email format appears to be invalid: '$USER_EMAIL'"
|
|
fi
|
|
if wt_yesno "Confirm Email" "Use '$USER_EMAIL' as your email?" "yes"; then
|
|
break # Confirmed, exit loop
|
|
fi
|
|
done
|
|
fi
|
|
|
|
|
|
|
|
log_subheader "Secret Generation"
|
|
log_info "Generating secrets and creating .env file..."
|
|
|
|
# --- Helper Functions ---
|
|
# Note: gen_random, gen_password, gen_hex, gen_base64 are now in utils.sh
|
|
|
|
# Function to update or add a variable to the .env file
|
|
# Usage: _update_or_add_env_var "VAR_NAME" "var_value"
|
|
_update_or_add_env_var() {
|
|
local var_name="$1"
|
|
local var_value="$2"
|
|
local tmp_env_file
|
|
|
|
tmp_env_file=$(mktemp)
|
|
# Ensure temp file is cleaned up if this function exits unexpectedly (though trap in main script should also cover)
|
|
# trap 'rm -f "$tmp_env_file"' EXIT
|
|
|
|
if [[ -f "$OUTPUT_FILE" ]]; then
|
|
grep -v -E "^${var_name}=" "$OUTPUT_FILE" > "$tmp_env_file" || true # Allow grep to not find anything
|
|
else
|
|
touch "$tmp_env_file" # Create empty temp if output file doesn't exist yet
|
|
fi
|
|
|
|
if [[ -n "$var_value" ]]; then
|
|
# Use single quotes for values containing $ (like bcrypt hashes) to prevent variable expansion
|
|
# Use double quotes for everything else
|
|
if [[ "$var_value" == *'$'* ]]; then
|
|
echo "${var_name}='$var_value'" >> "$tmp_env_file"
|
|
else
|
|
echo "${var_name}=\"$var_value\"" >> "$tmp_env_file"
|
|
fi
|
|
fi
|
|
mv "$tmp_env_file" "$OUTPUT_FILE"
|
|
# trap - EXIT # Remove specific trap for this temp file if desired, or let main script's trap handle it.
|
|
}
|
|
|
|
# Note: generate_bcrypt_hash() is now in utils.sh
|
|
|
|
# --- Main Logic ---
|
|
|
|
if [ ! -f "$TEMPLATE_FILE" ]; then
|
|
log_error "Template file not found at $TEMPLATE_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Pre-populate generated_values with non-empty values from existing_env_vars
|
|
for key_from_existing in "${!existing_env_vars[@]}"; do
|
|
if [[ -n "${existing_env_vars[$key_from_existing]}" ]]; then
|
|
generated_values["$key_from_existing"]="${existing_env_vars[$key_from_existing]}"
|
|
fi
|
|
done
|
|
|
|
# Store user input values (potentially overwriting if user was re-prompted and gave new input)
|
|
# Assign user email to all EMAIL_VARS
|
|
for var in "${EMAIL_VARS[@]}"; do
|
|
generated_values["$var"]="$USER_EMAIL"
|
|
done
|
|
|
|
# Database names for backward compatibility
|
|
# New installations: use service-specific databases (postiz, waha, lightrag)
|
|
# Upgrades: use 'postgres' to preserve existing data
|
|
DB_MIGRATION_VARS=("POSTIZ_DB_NAME" "WAHA_DB_NAME" "LIGHTRAG_DB_NAME")
|
|
|
|
for var in "${DB_MIGRATION_VARS[@]}"; do
|
|
if [[ -z "${existing_env_vars[$var]}" ]]; then
|
|
# Variable not in existing .env
|
|
if [[ ${#existing_env_vars[@]} -gt 0 ]]; then
|
|
# This is an upgrade - .env exists but var is missing
|
|
# Use 'postgres' for backward compatibility
|
|
generated_values["$var"]="postgres"
|
|
else
|
|
# New installation - use service name
|
|
case "$var" in
|
|
"POSTIZ_DB_NAME") generated_values["$var"]="postiz" ;;
|
|
"WAHA_DB_NAME") generated_values["$var"]="waha" ;;
|
|
"LIGHTRAG_DB_NAME") generated_values["$var"]="lightrag" ;;
|
|
esac
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Create a temporary file for processing
|
|
TMP_ENV_FILE=$(mktemp)
|
|
TEMP_FILES+=("$TMP_ENV_FILE")
|
|
|
|
# Track whether our custom variables were found in the template
|
|
declare -A found_vars
|
|
for var in "${USER_INPUT_VARS[@]}"; do
|
|
found_vars["$var"]=0
|
|
done
|
|
|
|
# Read template, substitute domain, generate initial values
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
# Substitute domain placeholder
|
|
processed_line=$(echo "$line" | sed "s/$DOMAIN_PLACEHOLDER/$DOMAIN/g")
|
|
|
|
# Check if it's a variable assignment line (non-empty, not comment, contains '=')
|
|
if [[ -n "$processed_line" && ! "$processed_line" =~ ^\s*# && "$processed_line" == *"="* ]]; then
|
|
varName=$(echo "$processed_line" | cut -d'=' -f1 | xargs) # Trim whitespace
|
|
currentValue=$(echo "$processed_line" | cut -d'=' -f2-)
|
|
|
|
# If already have a non-empty value from existing .env or prior generation/user input, use it
|
|
if [[ -n "${generated_values[$varName]}" ]]; then
|
|
processed_line="${varName}=\"${generated_values[$varName]}\""
|
|
# Check if this is one of our user-input derived variables that might not have a value yet
|
|
# (e.g. OPENAI_API_KEY if user left it blank). These are handled by `found_vars` later if needed.
|
|
# Or, if variable needs generation AND is not already populated (or is empty) in generated_values
|
|
elif [[ ${VARS_TO_GENERATE[$varName]+_} && -z "${generated_values[$varName]}" ]]; then
|
|
IFS=':' read -r type length <<< "${VARS_TO_GENERATE[$varName]}"
|
|
newValue=""
|
|
case "$type" in
|
|
password|alphanum) newValue=$(gen_password "$length") ;;
|
|
secret|base64) newValue=$(gen_base64 "$length") ;;
|
|
hex) newValue=$(gen_hex "$length") ;;
|
|
langfuse_pk) newValue="pk-lf-$(gen_hex "$length")" ;;
|
|
langfuse_sk) newValue="sk-lf-$(gen_hex "$length")" ;;
|
|
fixed) newValue="$length" ;; # Handle fixed type
|
|
*) log_warning "Unknown generation type '$type' for $varName" ;;
|
|
esac
|
|
|
|
if [[ -n "$newValue" ]]; then
|
|
processed_line="${varName}=\"${newValue}\"" # Quote generated values
|
|
generated_values["$varName"]="$newValue" # Store newly generated
|
|
else
|
|
# Keep original line structure but ensure value is empty if generation failed
|
|
# but it was in VARS_TO_GENERATE
|
|
processed_line="${varName}=\""
|
|
generated_values["$varName"]="" # Explicitly mark as empty in generated_values
|
|
fi
|
|
# For variables from the template that are not in VARS_TO_GENERATE and not already in generated_values
|
|
# store their template value if it's a direct assignment (not a ${...} substitution)
|
|
# This allows them to be used in later ${VAR} substitutions if they are referenced.
|
|
else
|
|
# This 'else' block is for lines from template not covered by existing values or VARS_TO_GENERATE.
|
|
# Check if it is one of the user input vars - these are handled by found_vars later if not in template.
|
|
is_user_input_var=0 # Reset for each line
|
|
for uivar in "${USER_INPUT_VARS[@]}"; do
|
|
if [[ "$varName" == "$uivar" ]]; then
|
|
is_user_input_var=1
|
|
# Mark as found if it's in template, value taken from generated_values if already set or blank
|
|
found_vars["$varName"]=1
|
|
if [[ ${generated_values[$varName]+_} ]]; then # if it was set (even to empty by user)
|
|
processed_line="${varName}=\"${generated_values[$varName]}\""
|
|
else # Not set in generated_values, keep template's default if any, or make it empty
|
|
if [[ "$currentValue" =~ ^\$\{.*\} || -z "$currentValue" ]]; then # if template is ${VAR} or empty
|
|
processed_line="${varName}=\"\""
|
|
else # template has a default simple value
|
|
processed_line="${varName}=\"$currentValue\"" # Use template's default, and quote it
|
|
fi
|
|
fi
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ $is_user_input_var -eq 0 ]]; then # Not a user input var, not in VARS_TO_GENERATE, not in existing
|
|
trimmed_value=$(echo "$currentValue" | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'//")
|
|
if [[ -n "$varName" && -n "$trimmed_value" && "$trimmed_value" != "\${INSTANCE_DOMAIN}" && "$trimmed_value" != "\${SUBDOMAIN_WILDCARD_CERT}" && ! "$trimmed_value" =~ ^\\$\\{ ]]; then # Check for other placeholders
|
|
# Only store if not already in generated_values and not a placeholder reference
|
|
if [[ -z "${generated_values[$varName]}" ]]; then
|
|
generated_values["$varName"]="$trimmed_value"
|
|
fi
|
|
fi
|
|
# processed_line remains as is (from template, after domain sub) for these cases
|
|
fi
|
|
fi
|
|
fi
|
|
echo "$processed_line" >> "$TMP_ENV_FILE"
|
|
done < "$TEMPLATE_FILE"
|
|
|
|
# Generate placeholder Supabase keys (always generate these)
|
|
|
|
# Function to create a JWT token
|
|
create_jwt() {
|
|
local role=$1
|
|
local jwt_secret=$2
|
|
local now=$(date +%s)
|
|
local exp=$((now + 315360000)) # 10 years from now (seconds)
|
|
|
|
# Create header (alg=HS256, typ=JWT)
|
|
local header='{"alg":"HS256","typ":"JWT"}'
|
|
# Create payload with role, issued at time, and expiry
|
|
local payload="{\"role\":\"$role\",\"iss\":\"supabase\",\"iat\":$now,\"exp\":$exp}"
|
|
|
|
# Base64url encode header and payload
|
|
local b64_header=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
|
|
local b64_payload=$(echo -n "$payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
|
|
|
|
# Create signature
|
|
local signature_input="$b64_header.$b64_payload"
|
|
local signature=$(echo -n "$signature_input" | openssl dgst -sha256 -hmac "$jwt_secret" -binary | base64 -w 0 | tr '/+' '_-' | tr -d '=')
|
|
|
|
# Combine to form JWT
|
|
echo -n "$b64_header.$b64_payload.$signature" # Use echo -n to avoid trailing newline
|
|
}
|
|
|
|
# Get JWT secret from previously generated values
|
|
JWT_SECRET_TO_USE="${generated_values["JWT_SECRET"]}"
|
|
|
|
if [[ -z "$JWT_SECRET_TO_USE" ]]; then
|
|
# This should ideally have been generated by VARS_TO_GENERATE if it was missing
|
|
# and JWT_SECRET is in VARS_TO_GENERATE. For safety, generate if truly empty.
|
|
log_warning "JWT_SECRET was empty, attempting to generate it now."
|
|
# Assuming JWT_SECRET definition is 'base64:64'
|
|
JWT_SECRET_TO_USE=$(gen_base64 64)
|
|
generated_values["JWT_SECRET"]="$JWT_SECRET_TO_USE"
|
|
fi
|
|
|
|
# Generate the actual JWT tokens using the JWT_SECRET_TO_USE, if not already set
|
|
if [[ -z "${generated_values[ANON_KEY]}" ]]; then
|
|
generated_values["ANON_KEY"]=$(create_jwt "anon" "$JWT_SECRET_TO_USE")
|
|
fi
|
|
|
|
if [[ -z "${generated_values[SERVICE_ROLE_KEY]}" ]]; then
|
|
generated_values["SERVICE_ROLE_KEY"]=$(create_jwt "service_role" "$JWT_SECRET_TO_USE")
|
|
fi
|
|
|
|
# Add any custom variables that weren't found in the template
|
|
for var in "${USER_INPUT_VARS[@]}"; do
|
|
if [[ ${found_vars["$var"]} -eq 0 && ${generated_values[$var]+_} ]]; then
|
|
# Before appending, check if it's already in TMP_ENV_FILE to avoid duplicates
|
|
if ! grep -q -E "^${var}=" "$TMP_ENV_FILE"; then
|
|
echo "${var}=\"${generated_values[$var]}\"" >> "$TMP_ENV_FILE" # Ensure quoting
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# --- WAHA API KEY (sha512) --- (moved after .env write to avoid overwrite)
|
|
|
|
# Second pass: Substitute generated values referenced like ${VAR}
|
|
# We'll process the substitutions line by line to avoid escaping issues
|
|
|
|
# Copy the temporary file to the output
|
|
cp "$TMP_ENV_FILE" "$OUTPUT_FILE"
|
|
|
|
log_info "Applying variable substitutions..."
|
|
|
|
# Process each generated value
|
|
for key in "${!generated_values[@]}"; do
|
|
value="${generated_values[$key]}"
|
|
|
|
# Create a temporary file for this value to avoid escaping issues
|
|
value_file=$(mktemp)
|
|
echo -n "$value" > "$value_file"
|
|
|
|
# Create a new temporary file for the output
|
|
new_output=$(mktemp)
|
|
|
|
# Process each line in the file
|
|
while IFS= read -r line; do
|
|
# Replace ${KEY} format
|
|
if [[ "$line" == *"\${$key}"* ]]; then
|
|
placeholder="\${$key}"
|
|
replacement=$(cat "$value_file")
|
|
line="${line//$placeholder/$replacement}"
|
|
fi
|
|
|
|
# Replace $KEY format
|
|
if [[ "$line" == *"$"$key* ]]; then
|
|
placeholder="$"$key
|
|
replacement=$(cat "$value_file")
|
|
line="${line//$placeholder/$replacement}"
|
|
fi
|
|
|
|
# Handle specific cases
|
|
if [[ "$key" == "ANON_KEY" && "$line" == "ANON_KEY="* ]]; then
|
|
line="ANON_KEY=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
if [[ "$key" == "SERVICE_ROLE_KEY" && "$line" == "SERVICE_ROLE_KEY="* ]]; then
|
|
line="SERVICE_ROLE_KEY=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
if [[ "$key" == "ANON_KEY" && "$line" == "SUPABASE_ANON_KEY="* ]]; then
|
|
line="SUPABASE_ANON_KEY=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
if [[ "$key" == "SERVICE_ROLE_KEY" && "$line" == "SUPABASE_SERVICE_ROLE_KEY="* ]]; then
|
|
line="SUPABASE_SERVICE_ROLE_KEY=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
if [[ "$key" == "JWT_SECRET" && "$line" == "SUPABASE_JWT_SECRET="* ]]; then
|
|
line="SUPABASE_JWT_SECRET=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
if [[ "$key" == "POSTGRES_PASSWORD" && "$line" == "SUPABASE_POSTGRES_PASSWORD="* ]]; then
|
|
line="SUPABASE_POSTGRES_PASSWORD=\"$(cat "$value_file")\""
|
|
fi
|
|
|
|
# Write the processed line to the new file
|
|
echo "$line" >> "$new_output"
|
|
done < "$OUTPUT_FILE"
|
|
|
|
# Replace the output file with the new version
|
|
mv "$new_output" "$OUTPUT_FILE"
|
|
|
|
# Clean up
|
|
rm -f "$value_file"
|
|
done
|
|
|
|
# --- WAHA API KEY (sha512) --- ensure after .env write/substitutions ---
|
|
# Generate plaintext API key if missing, then compute sha512:HEX and store in WAHA_API_KEY
|
|
if [[ -z "${generated_values[WAHA_API_KEY_PLAIN]}" ]]; then
|
|
generated_values[WAHA_API_KEY_PLAIN]="$(gen_base64 48 | tr -d '\n' | tr '/+' 'AZ')"
|
|
fi
|
|
|
|
PLAINTEXT_KEY="${generated_values[WAHA_API_KEY_PLAIN]}"
|
|
if [[ -n "$PLAINTEXT_KEY" ]]; then
|
|
SHA_HEX="$(printf "%s" "$PLAINTEXT_KEY" | openssl dgst -sha512 | awk '{print $2}')"
|
|
if [[ -n "$SHA_HEX" ]]; then
|
|
generated_values[WAHA_API_KEY]="sha512:${SHA_HEX}"
|
|
fi
|
|
fi
|
|
|
|
_update_or_add_env_var "WAHA_API_KEY_PLAIN" "${generated_values[WAHA_API_KEY_PLAIN]}"
|
|
_update_or_add_env_var "WAHA_API_KEY" "${generated_values[WAHA_API_KEY]}"
|
|
|
|
# Generate GOST_PROXY_URL if gost profile is active
|
|
if is_profile_active "gost"; then
|
|
if [[ -n "${generated_values[GOST_PASSWORD]}" && -n "${generated_values[GOST_USERNAME]}" ]]; then
|
|
generated_values["GOST_PROXY_URL"]="http://${generated_values[GOST_USERNAME]}:${generated_values[GOST_PASSWORD]}@gost:8080"
|
|
_update_or_add_env_var "GOST_PROXY_URL" "${generated_values[GOST_PROXY_URL]}"
|
|
fi
|
|
else
|
|
# Clear proxy URL if gost is not active
|
|
_update_or_add_env_var "GOST_PROXY_URL" ""
|
|
fi
|
|
|
|
# Update GOST_NO_PROXY from template to ensure all internal services are included
|
|
# This overwrites user's value to guarantee new services added in updates are included
|
|
template_no_proxy=$(grep -E "^GOST_NO_PROXY=" "$TEMPLATE_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d '"' || echo "")
|
|
if [[ -n "$template_no_proxy" ]]; then
|
|
_update_or_add_env_var "GOST_NO_PROXY" "$template_no_proxy"
|
|
fi
|
|
|
|
# Hash passwords using caddy with bcrypt (consolidated loop)
|
|
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "TEMPORAL_UI" "WELCOME")
|
|
|
|
for service in "${SERVICES_NEEDING_HASH[@]}"; do
|
|
password_var="${service}_PASSWORD"
|
|
hash_var="${service}_PASSWORD_HASH"
|
|
|
|
plain_pass="${generated_values[$password_var]}"
|
|
existing_hash="${generated_values[$hash_var]}"
|
|
|
|
# If no hash exists but we have a plain password, generate new hash
|
|
if [[ -z "$existing_hash" && -n "$plain_pass" ]]; then
|
|
new_hash=$(generate_bcrypt_hash "$plain_pass")
|
|
if [[ -n "$new_hash" ]]; then
|
|
existing_hash="$new_hash"
|
|
generated_values["$hash_var"]="$new_hash"
|
|
fi
|
|
fi
|
|
|
|
_update_or_add_env_var "$hash_var" "$existing_hash"
|
|
done
|
|
|
|
log_success ".env file generated successfully in the project root ($OUTPUT_FILE)."
|
|
|
|
# Save installation ID for telemetry correlation
|
|
save_installation_id "$OUTPUT_FILE"
|
|
|
|
# Uninstall caddy
|
|
apt remove -y caddy
|
|
|
|
# Cleanup any .bak files
|
|
cleanup_bak_files "$PROJECT_ROOT"
|
|
|
|
exit 0
|