#!/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; } # curl | bash 时 stdin 是脚本管道,交互必须从终端读 _tty_read() { if [[ -r /dev/tty ]]; then read -r "$@" /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 _tty_read -s -p "$prompt: " value echo "" else _tty_read -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" { echo "# 由 bootstrap.sh 自动生成" printf 'VPS_IP=%s\n' "$VPS_IP" printf 'DOMAIN=%s\n' "$DOMAIN" printf 'ACME_EMAIL=%s\n' "$ACME_EMAIL" printf 'PANEL_USERNAME=%s\n' "$PANEL_USERNAME" printf 'PANEL_PASSWORD=%s\n' "$PANEL_PASSWORD" } > "$env_file" chmod 600 "$env_file" } print_banner() { echo "" echo -e "${CYAN}${BOLD}╔══════════════════════════════════════╗${NC}" echo -e "${CYAN}${BOLD}║ jiedian 一键部署 ║${NC}" echo -e "${CYAN}${BOLD}║ Hysteria2 + Web 管理面板 ║${NC}" echo -e "${CYAN}${BOLD}╚══════════════════════════════════════╝${NC}" echo "" echo "部署前请确认:" echo " □ 域名 A 记录已指向本机公网 IP" echo " □ 安全组已放行 22/TCP 80/TCP 443/TCP 8443-8499/UDP" echo "" } confirm_or_abort() { local answer="" echo "" echo -e "${BOLD}即将写入配置:${NC}" echo " 公网 IP: $VPS_IP" echo " 域名: $DOMAIN" echo " 证书邮箱: $ACME_EMAIL" echo " 用户名: $PANEL_USERNAME" echo " 密码: ${PANEL_PASSWORD:0:2}****** (${#PANEL_PASSWORD} 位)" echo "" _tty_read -p "确认开始部署?(Y/n): " answer case "${answer:-Y}" in Y|y|yes|YES) ;; *) err "已取消" ;; esac } check_existing_install() { local env_file="$INSTALL_DIR/.env" [[ -f "$env_file" ]] || return 0 warn "检测到已有部署($env_file)" local answer="" _tty_read -p "是否重新部署?将覆盖 .env 并重新安装 (y/N): " answer case "${answer:-N}" in y|Y|yes|YES) ;; *) err "已取消。若要更新代码请: cd $INSTALL_DIR && git pull" ;; esac } save_credentials() { # shellcheck disable=SC1091 source "$INSTALL_DIR/.env" local panel_path="${PANEL_PATH:-}" if [[ -z "$panel_path" ]]; then panel_path="$(grep -E '^PANEL_PATH=' "$INSTALL_DIR/.env" | cut -d= -f2- || true)" fi [[ -n "$panel_path" ]] || panel_path="jiedian-unknown" cat > "$CREDS_FILE" <