Files
sub2api-add/scripts/install-local.sh
T
Qiufeng 292dd329af
Business Plugins CI / check (plugin-admin) (push) Successful in 1m36s
Business Plugins CI / check (subscription-admin) (push) Successful in 1m37s
feat: add one-command plugin deployment and removal
2026-08-27 23:48:18 +08:00

141 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
PREFIX=${PLUGIN_INSTALL_PREFIX:-/opt/sub2api-add}
ETC_DIR=${PLUGIN_ETC_DIR:-/etc/sub2api-add}
VAR_DIR=${PLUGIN_VAR_DIR:-/var/lib/sub2api-add}
RUN_USER=${PLUGIN_SYSTEM_USER:-sub2api-plugin}
SELECTION=all
usage() {
cat <<'EOF'
用法:scripts/install-local.sh [--plugin all|plugin-admin|subscription-admin]
环境变量:
PLUGIN_INSTALL_PREFIX 二进制目录,默认 /opt/sub2api-add
PLUGIN_ETC_DIR 环境文件目录,默认 /etc/sub2api-add
PLUGIN_VAR_DIR 插件数据目录,默认 /var/lib/sub2api-add
PLUGIN_SYSTEM_USER systemd 用户,默认 sub2api-plugin
EOF
}
die() { printf '错误:%s\n' "$*" >&2; exit 1; }
info() { printf '[sub2api-add] %s\n' "$*"; }
while (($#)); do
case "$1" in
--plugin)
(($# >= 2)) || die "--plugin 需要参数"
SELECTION=$2
shift 2
;;
-h|--help)
usage
exit 0
;;
*) die "未知参数:$1" ;;
esac
done
[[ $EUID -eq 0 ]] || die "请使用 root 或 sudo 运行"
command -v go >/dev/null 2>&1 || die "缺少 Go;请安装 Go 1.23 或更高版本"
command -v systemctl >/dev/null 2>&1 || die "缺少 systemd/systemctl"
command -v install >/dev/null 2>&1 || die "缺少 install 命令"
case "$SELECTION" in
all) PLUGINS=(plugin-admin subscription-admin) ;;
plugin-admin|subscription-admin) PLUGINS=("$SELECTION") ;;
*) die "插件必须是 all、plugin-admin 或 subscription-admin" ;;
esac
if ! id -u "$RUN_USER" >/dev/null 2>&1; then
useradd --system --user-group --home-dir "$VAR_DIR" --create-home --shell /usr/sbin/nologin "$RUN_USER"
fi
ensure_env_value() {
local file=$1 key=$2 value=$3
if grep -q "^${key}=" "$file"; then
if command sed --version >/dev/null 2>&1; then
sed -i "s#^${key}=.*#${key}=${value}#" "$file"
else
sed -i '' "s#^${key}=.*#${key}=${value}#" "$file"
fi
else
printf '%s=%s\n' "$key" "$value" >> "$file"
fi
}
random_secret() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 32
else
od -An -N32 -tx1 /dev/urandom | tr -d ' \n'
fi
}
install_one() {
local name=$1 source="$ROOT/plugins/$1" env_file="$ETC_DIR/$1.env"
local binary_dir="$PREFIX/$1/bin" data_dir="$VAR_DIR/$1"
[[ -d "$source" ]] || die "插件目录不存在:$source"
install -d -m 0755 "$binary_dir" "$data_dir" "$ETC_DIR"
if [[ ! -f "$env_file" ]]; then
install -m 0600 "$source/.env.example" "$env_file"
fi
if [[ "$name" == plugin-admin ]]; then
ensure_env_value "$env_file" PLUGIN_REGISTRY_DIR "$data_dir"
if grep -q '^PLUGIN_CONFIG_KEY=generate-and-replace-with-a-random-32-byte-secret$' "$env_file" ||
! grep -q '^PLUGIN_CONFIG_KEY=.' "$env_file"; then
ensure_env_value "$env_file" PLUGIN_CONFIG_KEY "$(random_secret)"
fi
fi
local tmp="$binary_dir/.${name}.tmp"
info "构建 $name"
(cd "$source" && CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o "$tmp" .)
chmod 0755 "$tmp"
mv -f "$tmp" "$binary_dir/$name"
chown -R "$RUN_USER:$RUN_USER" "$data_dir"
chown "$RUN_USER:$RUN_USER" "$binary_dir/$name"
chmod 0600 "$env_file"
cat > "/etc/systemd/system/sub2api-$name.service" <<EOF
[Unit]
Description=Sub2API ${name} business plugin
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${RUN_USER}
Group=${RUN_USER}
WorkingDirectory=${data_dir}
EnvironmentFile=${env_file}
ExecStart=${binary_dir}/${name}
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=${data_dir}
[Install]
WantedBy=multi-user.target
EOF
}
for plugin in "${PLUGINS[@]}"; do
install_one "$plugin"
done
systemctl daemon-reload
for plugin in "${PLUGINS[@]}"; do
systemctl enable --now "sub2api-$plugin.service"
info "$plugin 已启动:$(systemctl is-active "sub2api-$plugin.service")"
done
info "安装完成。配置文件位于 $ETC_DIR;数据位于 $VAR_DIR。"
info "查看日志:journalctl -u sub2api-plugin-admin -u sub2api-subscription-admin -f"