#!/usr/bin/env bash # jiedian 一键部署入口:自动 clone、交互配置、调用 install.sh # 用法: # curl -fsSL https://git.bz121.com/dekun/jiedian/raw/main/scripts/bootstrap.sh | bash # bash scripts/bootstrap.sh # 非交互: # bash scripts/bootstrap.sh --domain example.com --email you@example.com --yes set -euo pipefail INSTALL_DIR="/opt/jiedian" REPO_URL="https://git.bz121.com/dekun/jiedian.git" REPO_BRANCH="main" CREDS_FILE="/root/jiedian-credentials.txt" RED='\033[0;31m' GREEN='\033[0;32m' CYAN='\033[0;36m' YELLOW='\033[1;33m' BOLD='\033[1m' NC='\033[0m' log() { echo -e "${GREEN}[+]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } err() { echo -e "${RED}[!]${NC} $*" >&2; exit 1; } usage() { cat <<'EOF' 用法: curl -fsSL https://git.bz121.com/dekun/jiedian/raw/main/scripts/bootstrap.sh | bash bash scripts/bootstrap.sh [选项] 选项: --domain DOMAIN 域名(Hy2 + 面板 HTTPS) --email EMAIL Let's Encrypt 证书邮箱 --username USER 面板登录用户名(默认 admin) --password PASS 面板登录密码(留空则随机生成) --yes, -y 跳过确认 -h, --help 显示帮助 EOF } ensure_repo() { if [[ -d "$INSTALL_DIR/.git" ]] && [[ -f "$INSTALL_DIR/scripts/install.sh" ]]; then return 0 fi log "安装 git ..." export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq git ca-certificates curl if [[ -d "$INSTALL_DIR" ]] && [[ ! -d "$INSTALL_DIR/.git" ]]; then warn "目录 $INSTALL_DIR 已存在但不是 git 仓库,将备份为 ${INSTALL_DIR}.bak" mv "$INSTALL_DIR" "${INSTALL_DIR}.bak.$(date +%s)" fi log "克隆仓库到 $INSTALL_DIR ..." git clone --depth 1 -b "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR" } reexec_from_repo() { [[ "${JIEDIAN_BOOTSTRAP_FROM_REPO:-}" == "1" ]] && return 0 local repo_bootstrap="$INSTALL_DIR/scripts/bootstrap.sh" [[ -f "$repo_bootstrap" ]] || return 0 local src="${BASH_SOURCE[0]:-}" case "$src" in "$repo_bootstrap"|*/jiedian/scripts/bootstrap.sh) return 0 ;; esac export JIEDIAN_BOOTSTRAP_FROM_REPO=1 log "使用仓库内 bootstrap 继续 ..." exec bash "$repo_bootstrap" "$@" } check_root() { [[ $EUID -eq 0 ]] || err "请使用 root 运行(或 sudo bash bootstrap.sh)" } check_os() { [[ -f /etc/os-release ]] || err "无法识别操作系统" # shellcheck disable=SC1091 source /etc/os-release [[ "${ID:-}" == "ubuntu" ]] || err "仅支持 Ubuntu,当前: ${ID:-unknown}" case "${VERSION_ID:-}" in 22.04|24.04) ;; *) err "仅支持 Ubuntu 22.04 / 24.04,当前: ${VERSION_ID:-unknown}" ;; esac } detect_public_ip() { curl -4 -fsSL --connect-timeout 5 ifconfig.me 2>/dev/null \ || curl -4 -fsSL --connect-timeout 5 ip.sb 2>/dev/null \ || curl -4 -fsSL --connect-timeout 5 icanhazip.com 2>/dev/null \ || err "无法检测公网 IP,请检查网络" } resolve_domain() { local domain="$1" if command -v dig >/dev/null 2>&1; then dig +short A "$domain" @8.8.8.8 2>/dev/null | grep -E '^[0-9.]+$' | head -1 return 0 fi getent ahostsv4 "$domain" 2>/dev/null | awk '{print $1; exit}' } valid_domain() { [[ "$1" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ ]] } valid_email() { [[ "$1" == *"@"* ]] && [[ "$1" == *"."* ]] } prompt_if_empty() { local var_name="$1" local prompt="$2" local default="${3:-}" local secret="${4:-0}" local value="" if [[ -n "${!var_name:-}" ]]; then return 0 fi if [[ -n "$default" ]]; then prompt="$prompt [$default]" fi if [[ "$secret" == "1" ]]; then read -r -s -p "$prompt: " value echo "" else read -r -p "$prompt: " value fi if [[ -z "$value" && -n "$default" ]]; then value="$default" fi printf -v "$var_name" '%s' "$value" } generate_password() { openssl rand -base64 32 | tr -d '/+=' | head -c 20 } write_env_file() { local env_file="$INSTALL_DIR/.env" cat > "$env_file" < "$CREDS_FILE" <