@@ -0,0 +1,326 @@
#!/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 " <<EOF
# 由 bootstrap.sh 自动生成
VPS_IP=${VPS_IP}
DOMAIN=${DOMAIN}
ACME_EMAIL=${ACME_EMAIL}
PANEL_USERNAME=${PANEL_USERNAME}
PANEL_PASSWORD=${PANEL_PASSWORD}
EOF
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 ""
read -r -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 = ""
read -r -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 " <<EOF
jiedian 部署凭证
生成时间: $(date -Iseconds)
面板地址: https://${DOMAIN}/${panel_path}/
用户名: ${PANEL_USERNAME}
密码: ${PANEL_PASSWORD}
面板路径: ${panel_path}
VPS IP: ${VPS_IP}
EOF
chmod 600 " $CREDS_FILE "
}
print_success( ) {
# shellcheck disable=SC1091
source " $INSTALL_DIR /.env "
local panel_path = " ${ PANEL_PATH :- jiedian -unknown } "
echo ""
echo -e " ${ GREEN } ${ BOLD } ╔══════════════════════════════════════════════════════╗ ${ NC } "
echo -e " ${ GREEN } ${ BOLD } ║ 部署完成 ║ ${ NC } "
echo -e " ${ GREEN } ${ BOLD } ╠══════════════════════════════════════════════════════╣ ${ NC } "
echo -e " ${ GREEN } ${ BOLD } ║ ${ NC } 面板地址: ${ CYAN } https:// ${ DOMAIN } / ${ panel_path } / ${ NC } "
echo -e " ${ GREEN } ${ BOLD } ║ ${ NC } 用户名: ${ PANEL_USERNAME } "
echo -e " ${ GREEN } ${ BOLD } ║ ${ NC } 密码: ${ PANEL_PASSWORD } "
echo -e " ${ GREEN } ${ BOLD } ╠══════════════════════════════════════════════════════╣ ${ NC } "
echo -e " ${ GREEN } ${ BOLD } ║ ${ NC } 凭证已保存: ${ CREDS_FILE } "
echo -e " ${ GREEN } ${ BOLD } ╚══════════════════════════════════════════════════════╝ ${ NC } "
echo ""
echo "下一步:浏览器打开面板 → 登录 → 添加节点 → 复制 Hy2 链接到客户端"
echo ""
}
# ── 参数解析 ──
DOMAIN = ""
ACME_EMAIL = ""
PANEL_USERNAME = ""
PANEL_PASSWORD = ""
ASSUME_YES = 0
while [ [ $# -gt 0 ] ] ; do
case " $1 " in
--domain) DOMAIN = " $2 " ; shift 2 ; ;
--email) ACME_EMAIL = " $2 " ; shift 2 ; ;
--username) PANEL_USERNAME = " $2 " ; shift 2 ; ;
--password) PANEL_PASSWORD = " $2 " ; shift 2 ; ;
--yes| -y) ASSUME_YES = 1; shift ; ;
-h| --help) usage; exit 0 ; ;
*) err " 未知参数: $1 (使用 --help 查看) " ; ;
esac
done
check_root
check_os
# curl | bash 时先 clone,再用仓库内脚本继续
ensure_repo
reexec_from_repo " $@ "
print_banner
ROOT_DIR = " $INSTALL_DIR "
ENV_FILE = " $ROOT_DIR /.env "
check_existing_install
VPS_IP = " $( detect_public_ip) "
log " 检测到公网 IP: $VPS_IP "
prompt_if_empty DOMAIN "域名 (Hy2 + 面板 HTTPS)"
prompt_if_empty ACME_EMAIL "证书邮箱 (Let's Encrypt)"
prompt_if_empty PANEL_USERNAME "面板用户名" "admin"
valid_domain " $DOMAIN " || err " 域名格式无效: $DOMAIN "
valid_email " $ACME_EMAIL " || err " 邮箱格式无效: $ACME_EMAIL "
[ [ -n " $PANEL_USERNAME " ] ] || err "用户名不能为空"
if [ [ -z " $PANEL_PASSWORD " ] ] ; then
read -r -s -p "面板密码 (留空自动生成): " PANEL_PASSWORD
echo ""
fi
if [ [ -z " $PANEL_PASSWORD " ] ] ; then
PANEL_PASSWORD = " $( generate_password) "
log "已自动生成面板密码"
fi
RESOLVED_IP = " $( resolve_domain " $DOMAIN " || true ) "
if [ [ -z " $RESOLVED_IP " ] ] ; then
err " 域名 $DOMAIN 无法解析,请先将 A 记录指向 $VPS_IP "
fi
if [ [ " $RESOLVED_IP " != " $VPS_IP " ] ] ; then
warn " 域名 $DOMAIN 解析为 $RESOLVED_IP ,本机公网 IP 为 $VPS_IP "
if [ [ " $ASSUME_YES " -eq 0 ] ] ; then
read -r -p "DNS 可能未生效,继续?(y/N): " dns_ok
case " ${ dns_ok :- N } " in
y| Y| yes| YES) ; ;
*) err "请先修正 DNS 后重试" ; ;
esac
fi
fi
if [ [ " $ASSUME_YES " -eq 0 ] ] ; then
confirm_or_abort
fi
write_env_file
log " 已写入 $ENV_FILE "
log "开始安装 ..."
bash " $ROOT_DIR /scripts/install.sh "
save_credentials
print_success