#!/usr/bin/env bash set -Eeuo pipefail usage() { echo "Usage: $0 [image-tag] [business-name]" >&2 echo "Example: $0 blake-heritage blakeheritage.com nextchair:2026-09-04 \"Blake Heritage\"" >&2 exit 2 } [[ $# -ge 2 && $# -le 4 ]] || usage business_slug="$1" domain="$2" image_tag="${3:-nextchair:latest}" business_name="${4:-$business_slug}" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" stack_root="${NEXTCHAIR_STACK_ROOT:-${repo_root}/stacks}" infra_env="${NEXTCHAIR_INFRA_ENV:-${repo_root}/.env.infra}" stack_dir="${stack_root}/${business_slug}" compose_template="${repo_root}/deploy/business-compose.yaml.template" [[ "$business_slug" =~ ^[a-z0-9]+([a-z0-9-]*[a-z0-9])?$ ]] || { echo "Invalid slug: use lowercase letters, numbers, and hyphens." >&2; exit 2; } [[ ${#business_slug} -le 22 ]] || { echo "Invalid slug: maximum length is 22 characters." >&2; exit 2; } [[ "$domain" =~ ^[A-Za-z0-9.-]+$ ]] || { echo "Invalid domain." >&2; exit 2; } [[ "$image_tag" =~ ^[A-Za-z0-9._/@:-]+$ ]] || { echo "Invalid image tag." >&2; exit 2; } [[ -f "$infra_env" ]] || { echo "Missing $infra_env. Copy .env.infra.example first." >&2; exit 1; } [[ -f "$compose_template" ]] || { echo "Missing $compose_template." >&2; exit 1; } [[ ! -e "$stack_dir" ]] || { echo "Stack already exists: $stack_dir" >&2; exit 1; } # shellcheck disable=SC1090 source "$infra_env" [[ -n "${MYSQL_ROOT_PASSWORD:-}" ]] || { echo "MYSQL_ROOT_PASSWORD is required in $infra_env." >&2; exit 1; } database_name="nextchair_${business_slug//-/_}" database_user="$database_name" database_password="$(openssl rand -hex 24)" auth_secret="$(openssl rand -base64 48 | tr -d '\n')" installation_token="$(openssl rand -hex 32)" cron_secret="$(openssl rand -hex 32)" docker network inspect nextchair-db >/dev/null 2>&1 || docker network create nextchair-db >/dev/null docker network inspect nextchair-proxy >/dev/null 2>&1 || docker network create nextchair-proxy >/dev/null docker inspect nextchair-mysql >/dev/null 2>&1 || { echo "nextchair-mysql is not running. Start stacks/infra/compose.yaml first." >&2; exit 1; } printf 'Creating database %s...\n' "$database_name" sql="CREATE DATABASE IF NOT EXISTS \`$database_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; CREATE USER IF NOT EXISTS '$database_user'@'%' IDENTIFIED BY '$database_password'; ALTER USER '$database_user'@'%' IDENTIFIED BY '$database_password'; GRANT ALL PRIVILEGES ON \`$database_name\`.* TO '$database_user'@'%'; FLUSH PRIVILEGES;" printf '%s\n' "$sql" | docker exec -i nextchair-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" mkdir -p "$stack_dir" sed \ -e "s|\${BUSINESS_SLUG}|$business_slug|g" \ -e "s|\${NEXTCHAIR_IMAGE:-nextchair:latest}|$image_tag|g" \ "$compose_template" > "$stack_dir/compose.yaml" cat > "$stack_dir/.env" < "$caddy_sites_dir/$business_slug.caddy" <