#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" DEFAULT_ENV_FILE=".env.default" USER_ENV_FILE=".env" log() { printf '%s\n' "$*" >&2 } die() { printf 'Error: %s\n' "$*" >&2 exit 1 } detect_compose() { if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=(docker compose) return fi if command -v docker-compose >/dev/null 2>&1; then COMPOSE_CMD=(docker-compose) return fi die "Docker Compose is not available. Install Docker Compose, then run this command again." } generate_secret_key() { if command -v openssl >/dev/null 2>&1; then openssl rand -base64 42 return fi if command -v dd >/dev/null 2>&1 && command -v base64 >/dev/null 2>&1; then dd if=/dev/urandom bs=42 count=1 2>/dev/null | base64 | tr -d '\n' printf '\n' return fi return 1 } ensure_env_files() { [[ -f "$DEFAULT_ENV_FILE" ]] || die "$DEFAULT_ENV_FILE is missing." if [[ -f "$USER_ENV_FILE" ]]; then return fi : >"$USER_ENV_FILE" if [[ ! -t 0 ]]; then log "Created $USER_ENV_FILE for local overrides." return fi printf 'Created %s for local overrides.\n' "$USER_ENV_FILE" printf 'Do you need a custom deployment now? (Most users can press Enter to skip.) [y/N] ' read -r answer case "${answer:-}" in y | Y | yes | YES | Yes) cat <<'EOF' Edit .env with the settings you want to override, using .env.example as the full reference. Run ./dify-compose up -d again when you are ready. EOF exit 0 ;; esac } user_env_value() { local key="$1" awk -F= -v target="$key" ' /^[[:space:]]*#/ || !/=/{ next } { key = $1 gsub(/^[[:space:]]+|[[:space:]]+$/, "", key) if (key == target) { value = substr($0, index($0, "=") + 1) gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) if ((value ~ /^".*"$/) || (value ~ /^'\''.*'\''$/)) { value = substr(value, 2, length(value) - 2) } result = value } } END { print result } ' "$USER_ENV_FILE" } set_user_env_value() { local key="$1" local value="$2" local temp_file temp_file="$(mktemp "${TMPDIR:-/tmp}/dify-env.XXXXXX")" awk -F= -v target="$key" -v replacement="$key=$value" ' BEGIN { replaced = 0 } /^[[:space:]]*#/ || !/=/{ print; next } { key = $1 gsub(/^[[:space:]]+|[[:space:]]+$/, "", key) if (key == target) { if (!replaced) { print replacement replaced = 1 } next } print } END { if (!replaced) { print replacement } } ' "$USER_ENV_FILE" >"$temp_file" mv "$temp_file" "$USER_ENV_FILE" } ensure_secret_key() { local current_secret_key local secret_key current_secret_key="$(user_env_value SECRET_KEY)" if [[ -n "$current_secret_key" ]]; then return fi secret_key="$(generate_secret_key)" || die "Unable to generate SECRET_KEY. Install openssl or configure SECRET_KEY in .env." set_user_env_value SECRET_KEY "$secret_key" log "Generated SECRET_KEY in $USER_ENV_FILE." } env_value() { local key="$1" awk -F= -v target="$key" ' /^[[:space:]]*#/ || !/=/{ next } { key = $1 gsub(/^[[:space:]]+|[[:space:]]+$/, "", key) if (key == target) { value = substr($0, index($0, "=") + 1) gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) if ((value ~ /^".*"$/) || (value ~ /^'\''.*'\''$/)) { value = substr(value, 2, length(value) - 2) } result = value } } END { print result } ' "$DEFAULT_ENV_FILE" "$USER_ENV_FILE" } user_overrides() { local key="$1" grep -Eq "^[[:space:]]*${key}[[:space:]]*=" "$USER_ENV_FILE" } write_merged_env() { awk ' function trim(s) { sub(/^[[:space:]]+/, "", s) sub(/[[:space:]]+$/, "", s) return s } /^[[:space:]]*#/ || !/=/{ next } { key = $0 sub(/=.*/, "", key) key = trim(key) if (key == "") { next } value = substr($0, index($0, "=") + 1) value = trim(value) if (!(key in seen)) { order[++count] = key seen[key] = 1 } values[key] = value } END { for (i = 1; i <= count; i++) { key = order[i] print key "=" values[key] } } ' "$DEFAULT_ENV_FILE" "$USER_ENV_FILE" >"$MERGED_ENV_FILE" } set_merged_env_value() { local key="$1" local value="$2" local temp_file temp_file="$(mktemp "${TMPDIR:-/tmp}/dify-compose-env.XXXXXX")" awk -F= -v target="$key" -v replacement="$key=$value" ' BEGIN { replaced = 0 } /^[[:space:]]*#/ || !/=/{ print; next } { key = $1 gsub(/^[[:space:]]+|[[:space:]]+$/, "", key) if (key == target) { if (!replaced) { print replacement replaced = 1 } next } print } END { if (!replaced) { print replacement } } ' "$MERGED_ENV_FILE" >"$temp_file" mv "$temp_file" "$MERGED_ENV_FILE" } set_if_not_overridden() { local key="$1" local value="$2" if user_overrides "$key"; then return fi set_merged_env_value "$key" "$value" } metadata_db_host() { case "$1" in mysql) printf 'db_mysql' ;; postgresql | '') printf 'db_postgres' ;; *) printf '%s' "$(env_value DB_HOST)" ;; esac } metadata_db_port() { case "$1" in mysql) printf '3306' ;; postgresql | '') printf '5432' ;; *) printf '%s' "$(env_value DB_PORT)" ;; esac } metadata_db_user() { case "$1" in mysql) printf 'root' ;; postgresql | '') printf 'postgres' ;; *) printf '%s' "$(env_value DB_USERNAME)" ;; esac } build_merged_env() { MERGED_ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/dify-compose.XXXXXX")" trap 'rm -f "$MERGED_ENV_FILE"' EXIT write_merged_env local db_type local redis_host local redis_port local redis_username local redis_password local redis_auth local code_execution_api_key local weaviate_api_key db_type="$(env_value DB_TYPE)" set_if_not_overridden DB_HOST "$(metadata_db_host "$db_type")" set_if_not_overridden DB_PORT "$(metadata_db_port "$db_type")" set_if_not_overridden DB_USERNAME "$(metadata_db_user "$db_type")" if ! user_overrides CELERY_BROKER_URL; then redis_host="$(env_value REDIS_HOST)" redis_port="$(env_value REDIS_PORT)" redis_username="$(env_value REDIS_USERNAME)" redis_password="$(env_value REDIS_PASSWORD)" redis_auth="" if [[ -n "$redis_username" && -n "$redis_password" ]]; then redis_auth="${redis_username}:${redis_password}@" elif [[ -n "$redis_password" ]]; then redis_auth=":${redis_password}@" elif [[ -n "$redis_username" ]]; then redis_auth="${redis_username}@" fi set_merged_env_value CELERY_BROKER_URL "redis://${redis_auth}${redis_host:-redis}:${redis_port:-6379}/1" fi if ! user_overrides SANDBOX_API_KEY; then code_execution_api_key="$(env_value CODE_EXECUTION_API_KEY)" set_if_not_overridden SANDBOX_API_KEY "${code_execution_api_key:-dify-sandbox}" fi if ! user_overrides WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS; then weaviate_api_key="$(env_value WEAVIATE_API_KEY)" set_if_not_overridden WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS \ "${weaviate_api_key:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih}" fi } main() { detect_compose ensure_env_files ensure_secret_key build_merged_env if [[ "$#" -eq 0 ]]; then set -- up -d fi "${COMPOSE_CMD[@]}" --env-file "$MERGED_ENV_FILE" "$@" } main "$@"