#!/usr/bin/env bash set -euo pipefail log() { printf '%s\n' "$*"; } warn() { printf 'WARN: %s\n' "$*" >&2; } ensure_network() { local name=$1 if docker network inspect "$name" >/dev/null 2>&1; then log "network exists: $name" else log "creating overlay network: $name" docker network create --driver overlay --attachable "$name" fi } secret_exists() { docker secret inspect "$1" >/dev/null 2>&1; } create_secret_from_value() { local name=$1; local value=$2 printf '%s' "$value" | docker secret create "$name" - >/dev/null 2>&1 && log "secret created: $name" || log "secret exists: $name" } create_secret_from_file() { local name=$1; local path=$2 docker secret create "$name" "$path" >/dev/null 2>&1 && log "secret created: $name (from $path)" || log "secret exists: $name" } rand_b64() { openssl rand -base64 32 2>/dev/null || head -c 24 /dev/urandom | base64; } get_from_env_or_file_or_default() { local name=$1 envvar=$2 default_gen=$3 local file="stacks/secrets/${name}.txt" local val="" if [[ -n "${!envvar-}" ]]; then val="${!envvar}" elif [[ -f "$file" ]]; then val=$(cat "$file") elif [[ "$default_gen" == "gen" ]]; then val=$(rand_b64) warn "no env or file for $name; generating random" else val="" fi printf '%s' "$val" } main() { # Networks ensure_network traefik-public ensure_network database-network ensure_network monitoring-network # Secrets map: name envvar gen-policy declare -a items=( "pg_root_password PG_ROOT_PASSWORD gen" "mariadb_root_password MARIADB_ROOT_PASSWORD gen" "gitea_db_password GITEA_DB_PASSWORD gen" "nextcloud_db_password NEXTCLOUD_DB_PASSWORD gen" "smtp_user SMTP_USER plain" "smtp_pass SMTP_PASS gen" "appflowy_db_url APPFLOWY_DB_URL plain" "minio_access_key MINIO_ACCESS_KEY gen" "minio_secret_key MINIO_SECRET_KEY gen" ) for entry in "${items[@]}"; do set -- $entry name=$1; envvar=$2; policy=$3 if secret_exists "$name"; then log "secret exists: $name" continue fi val=$(get_from_env_or_file_or_default "$name" "$envvar" "$policy") if [[ -z "$val" ]]; then warn "secret $name missing (env $envvar or stacks/secrets/${name}.txt). Creating placeholder; update ASAP." case "$name" in smtp_user) val="noreply@example.local" ;; appflowy_db_url) val="postgres://user:pass@postgresql_primary:5432/appflowy" ;; *) val=$(rand_b64) ;; esac fi create_secret_from_value "$name" "$val" done log "bootstrap complete" } main "$@"