#!/bin/sh
# WDCP 4.0.2 online installer. All WDCP assets are served by wdcp.net.
set -eu
umask 077
release_version=4.0.2
asset_base=https://wdcp.net/releases/wdcp/v4.0.2
runtime_url=https://wdcp.net/releases/wdcp/v4.0.2/runtime_v4.sh
runtime_sha=f0d31da03bf3d81a07621ae77bf1c908b9a03bdfb3627f70156e1f38e08f2310
mode=full
check_only=0
panel_port=8080
port_given=0
policy_created=0
work=
allow_eol=0
local_artifacts=
install_stats=${WDCP_INSTALL_STATS:-0}
masked_services=
fail() { printf '%s\n' "$*" >&2; exit 1; }
usage() {
    cat <<'EOF'
WDCP 4.0.2
  sh install_v4.sh              纯净服务器：完整 LNMP + FTP + WDCP
  sh install_v4.sh --panel-only 已有 WDlinux 运行环境：只安装面板
  sh install_v4.sh --upgrade    已有 WDCP：备份并升级面板
  sh install_v4.sh --check      只下载并校验发布文件，不修改系统
  sh install_v4.sh --port 8080  新面板端口，升级时默认保留原端口
完整安装：Ubuntu/Debian、CentOS Stream、Rocky、AlmaLinux、Fedora、openSUSE、Alpine。
  --plan                     只显示发行版及软件包计划
  --allow-eol                显式允许已配置仓库的 CentOS 7/8 旧系统
  --telemetry                自愿上报匿名安装成功统计（默认关闭）
  --no-telemetry             禁止安装统计上报
  --local-artifacts DIR      使用已核对 SHA256 的本地发行文件（含 runtime_v4.sh）
Alpine 使用 OpenRC；其他发行版使用 systemd。amd64 / arm64 静态发布包。
安装不关闭系统防火墙。请在云安全组/防火墙中允许所需端口。
EOF
}
# BEGIN EMBEDDED DISTRIBUTION PLAN
#!/bin/sh
# Pure detection/package planning. No host mutation when sourced.
runtime_detect() {
    release_file=${WDCP_OS_RELEASE:-/etc/os-release}
    test -r "$release_file" || fail "缺少系统标识：$release_file"
    ID= VERSION_ID= NAME= PRETTY_NAME=
    . "$release_file"
    os_major=${VERSION_ID%%.*}
    legacy=0
    init_system=systemd
    case "$ID" in
        ubuntu) case "$VERSION_ID" in 22.04|24.04) ;; *) fail 'Ubuntu 支持 22.04 / 24.04。';; esac; family=deb; package_manager=apt-get ;;
        debian) case "$os_major" in 12|13) ;; *) fail 'Debian 支持 12 / 13。';; esac; family=deb; package_manager=apt-get ;;
        centos)
            family=rpm
            case "$os_major" in
                7|8) legacy=1 ;;
                9|10) case "$NAME" in *Stream*) ;; *) fail 'CentOS 9 / 10 必须为 Stream。';; esac ;;
                *) fail 'CentOS 支持 Linux 7/8（旧系统模式）或 Stream 9/10。';;
            esac
            package_manager=dnf
            test "$os_major" != 7 || package_manager=yum ;;
        rocky|almalinux) case "$os_major" in 8|9|10) ;; *) fail 'Rocky/AlmaLinux 支持 8 / 9 / 10。';; esac; family=rpm; package_manager=dnf ;;
        fedora) case "$os_major" in ''|*[!0-9]*) fail 'Fedora 版本异常。';; esac; test "$os_major" -ge 42 || fail 'Fedora 支持 42 及以上版本。'; family=rpm; package_manager=dnf ;;
        opensuse-leap) case "$VERSION_ID" in 15.6|16.0) ;; *) fail 'openSUSE Leap 支持 15.6 / 16.0。';; esac; family=suse; package_manager=zypper ;;
        opensuse-tumbleweed) family=suse; package_manager=zypper ;;
        alpine) case "$VERSION_ID" in 3.21.*|3.22.*|3.23.*) ;; *) fail 'Alpine 支持 3.21 / 3.22 / 3.23。';; esac; family=alpine; package_manager=apk; init_system=openrc ;;
        *) fail "尚未适配发行版：$ID $VERSION_ID" ;;
    esac
    if test "$legacy" = 1 && test "${allow_eol:-0}" != 1; then
        fail 'CentOS 7/8 已停止维护。仅在已配置可用签名仓库的旧系统使用 --allow-eol；不会自动替换仓库。'
    fi
}
runtime_package_list() {
    case "$family" in
        deb) echo 'nginx mariadb-server mariadb-client php-fpm php-cli php-mysql php-gd php-curl php-mbstring php-xml php-zip php-bcmath phpmyadmin pure-ftpd openssl curl ca-certificates net-tools iproute2 procps psmisc cron logrotate unzip tar gzip sqlite3 bash passwd' ;;
        rpm)
            selinux_pkg=policycoreutils-python-utils
            test "$os_major" != 7 || selinux_pkg=policycoreutils-python
            echo "nginx mariadb-server mariadb php-fpm php-cli php-mysqlnd php-gd php-mbstring php-xml php-bcmath phpMyAdmin pure-ftpd openssl ${rpm_curl:-curl} ca-certificates net-tools iproute procps-ng psmisc cronie logrotate unzip tar gzip sqlite shadow-utils $selinux_pkg php-pecl-zip" ;;
        suse) echo 'nginx mariadb mariadb-client php8 php8-cli php8-fpm php8-mysql php8-gd php8-curl php8-mbstring php8-dom php8-xmlreader php8-xmlwriter php8-zip php8-bcmath phpMyAdmin pure-ftpd openssl curl ca-certificates net-tools iproute2 procps psmisc cron logrotate unzip tar gzip sqlite3 shadow' ;;
        alpine)
            echo "nginx mariadb mariadb-client mariadb-openrc $alpine_php $alpine_php-fpm $alpine_php-mysqli $alpine_php-pdo_mysql $alpine_php-gd $alpine_php-curl $alpine_php-mbstring $alpine_php-xml $alpine_php-dom $alpine_php-session $alpine_php-openssl $alpine_php-ctype $alpine_php-iconv $alpine_php-tokenizer $alpine_php-zip $alpine_php-bcmath phpmyadmin pure-ftpd openssl curl ca-certificates net-tools iproute2 procps psmisc dcron logrotate unzip tar gzip sqlite bash shadow openrc" ;;
    esac
}
runtime_plan() {
    runtime_detect
    alpine_php=php83
    test "$family" != alpine || case "$VERSION_ID" in 3.21.*) alpine_php=php83;; *) alpine_php=php84;; esac
    printf 'distribution=%s\nversion=%s\nfamily=%s\nmanager=%s\ninit=%s\n' "$ID" "$VERSION_ID" "$family" "$package_manager" "$init_system"
    printf 'packages=%s\n' "$(runtime_package_list)"
}
# END EMBEDDED DISTRIBUTION PLAN
while test "$#" -gt 0; do
    case "$1" in
        --plan)
            # Permit legacy systems for read-only planning only.
            allow_eol=1
            runtime_plan
            exit 0 ;;
        --allow-eol) allow_eol=1 ;;
        --telemetry) install_stats=1 ;;
        --no-telemetry) install_stats=0 ;;
        --local-artifacts) test "$#" -ge 2 || fail '--local-artifacts 缺少路径。'; local_artifacts=$2; shift ;;
        --help|-h) usage; exit 0 ;;
        --panel-only) test "$mode" = full || fail '安装模式不能组合。'; mode=panel ;;
        --upgrade) test "$mode" = full || fail '安装模式不能组合。'; mode=upgrade ;;
        --check) check_only=1 ;;
        --port) test "$#" -ge 2 || fail '--port 缺少数值。'; panel_port=$2; port_given=1; shift ;;
        *) fail "未知参数：$1" ;;
    esac
    shift
done
case "$install_stats" in 0|1) ;; *) fail 'WDCP_INSTALL_STATS 只能为 0 或 1。';; esac
case "$panel_port" in ''|*[!0-9]*) fail '端口应为 1024 到 65535 的整数。' ;; esac
test "$panel_port" -ge 1024 && test "$panel_port" -le 65535 || fail '端口应为 1024 到 65535 的整数。'
test "$(uname -s)" = Linux || fail '安装脚本需要 Linux。'
case "$(uname -m)" in
    x86_64|amd64) arch=amd64; package_sha=70e456480ca6089125d3faf574ca640d9f31fea8b2aced087b217d1e793b2a75; helper_sha=e52ca1348664cc6a04f37b476d9df58e4935eccaf11310ac1863b1521701f0de ;;
    aarch64|arm64) arch=arm64; package_sha=03e9e190b425abd55cfe0d7de814c36e0ebfea7c4e611d0bfa534921879db8a6; helper_sha=d60fdfd0ae4248ece118a1006139ff80e708bb05d7b889d004a798c9b5154dcb ;;
    *) fail '仅支持 amd64 / arm64。' ;;
esac
init_system=systemd
if test -f /etc/alpine-release; then init_system=openrc; fi
command -v sha256sum >/dev/null || fail '缺少 sha256sum。'
test -n "$local_artifacts" || command -v curl >/dev/null || command -v wget >/dev/null || fail '请先安装 curl 或 wget 及 ca-certificates。'
if test "$check_only" = 0; then
    test "$(id -u)" = 0 || fail '安装和升级需要 root。'
    if test "$init_system" = openrc; then
        command -v rc-service >/dev/null && test -d /run/openrc || fail '在线安装需要运行中的 OpenRC。'
    else test -d /run/systemd/system || fail '在线安装需要正在运行的 systemd。'; fi
    if test "$mode" = upgrade; then
        test -x /www/wdlinux/wdcp/wdcp || fail '未检测到已安装的 WDCP。'
        if test "$init_system" = systemd; then
            systemctl cat wdcp.service >/dev/null || fail '未找到 wdcp.service。'
        else test -x /etc/init.d/wdcp || fail '未找到 wdcp OpenRC 服务。'; fi
        if test "$port_given" = 0 && test -f /www/wdlinux/wdcp/conf/port.conf; then
            panel_port=$(cut -d '|' -f 1 /www/wdlinux/wdcp/conf/port.conf | tr -d '\r\n')
            case "$panel_port" in ''|*[!0-9]*) fail '原面板端口配置异常。' ;; esac
            test "$panel_port" -ge 1 && test "$panel_port" -le 65535 || fail '原面板端口配置异常。'
        fi
    else
        test ! -e /www/wdlinux/wdcp || fail '面板目录已存在，请使用更新脚本。'
        if test "$init_system" = systemd; then
            ! systemctl cat wdcp.service >/dev/null 2>&1 || fail '已有 wdcp.service，请先检查。'
        else test ! -e /etc/init.d/wdcp || fail '已有 wdcp OpenRC 服务。'; fi
        if test "$mode" = panel; then
            test -d /www/wdlinux || fail '未找到 WDlinux 环境，请使用完整安装。'
            test -x /www/wdlinux/mysql/bin/mysql || fail '未找到 WDlinux MySQL。'
            test -x /www/wdlinux/php/bin/php || fail '未找到 WDlinux PHP。'
            test -x /www/wdlinux/nginx/sbin/nginx || test -x /www/wdlinux/apache/bin/httpd || fail '未找到 WDlinux 网站服务。'
            command -v ss >/dev/null || fail '请先安装 iproute2。'
            test -z "$(ss -H -ltn "sport = :$panel_port")" || fail '面板端口已被占用。'
        fi
    fi
fi
cleanup() {
    if test "$policy_created" = 1; then rm -f /usr/sbin/policy-rc.d; fi
    if command -v runtime_unmask_distribution_services >/dev/null; then runtime_unmask_distribution_services || true; fi
    if test -n "$work"; then rm -rf "$work"; fi
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
work=$(mktemp -d "${TMPDIR:-/tmp}/wdcp-install.XXXXXXXX")
fetch_verified() {
    url=$1; dest=$2; expected=$3
    case "$url" in https://wdcp.net/*) ;; *) fail '下载地址必须为 WDCP 官网 HTTPS。' ;; esac
    if test -n "$local_artifacts"; then
        case "$local_artifacts" in /*) ;; *) fail '本地发行目录必须为绝对路径。';; esac
        cp "$local_artifacts/${url##*/}" "$dest" || fail "缺少本地文件：${url##*/}"
    elif command -v curl >/dev/null; then
        code=$(curl --fail --silent --show-error --proto '=https' --tlsv1.2 --retry 3 --connect-timeout 15 --max-time 600 --output "$dest" --write-out '%{http_code}' "$url") || fail "下载失败：$url"
        test "$code" = 200 || fail '下载必须直接返回 HTTP 200，拒绝重定向。'
    else
        wget --quiet --https-only --max-redirect=0 --timeout=30 --tries=3 -O "$dest" "$url" || fail "下载失败：$url"
    fi
    printf '%s  %s\n' "$expected" "$dest" | sha256sum -c - >/dev/null || fail "SHA256 校验失败：$url"
}
if test "$mode" = full; then
    fetch_verified "$runtime_url" "$work/runtime.sh" "$runtime_sha"
    . "$work/runtime.sh"
    if test "$check_only" = 0; then runtime_preflight; fi
fi
echo "下载并校验 WDCP $release_version ($arch)…"
fetch_verified "$asset_base/wdcp-updater_linux_$arch" "$work/wdcp-updater" "$helper_sha"
fetch_verified "$asset_base/wdcp_v${release_version}_linux_$arch.tar.gz" "$work/panel.tar.gz" "$package_sha"
chmod 700 "$work/wdcp-updater"
"$work/wdcp-updater" verify --archive "$work/panel.tar.gz" > "$work/verification.json"
if test "$check_only" = 1; then echo '校验通过。系统未修改。'; exit 0; fi
root=/www/wdlinux/wdcp
# Only explicit consent enables a one-time, non-blocking success report.
wdcp_report_installation() {
    test "$install_stats" = 1 || return 0
    (
        stats_file="$root/data/installation-stats-id"
        if test ! -f "$stats_file"; then
            instance=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')
            test "${#instance}" = 32 || exit 0
            # noclobber prevents concurrent installs from replacing a stable instance ID.
            (set -C; printf '%s\n' "$instance" > "$stats_file") 2>/dev/null || true
            chmod 600 "$stats_file" 2>/dev/null || true
        fi
        instance=$(cat "$stats_file" 2>/dev/null) || exit 0
        case "$instance" in ''|*[!a-f0-9]*) exit 0;; esac
        test "${#instance}" -ge 32 && test "${#instance}" -le 64 || exit 0
        stats_os=$( ( . /etc/os-release; printf '%s' "${ID:-unknown}" ) 2>/dev/null) || stats_os=unknown
        stats_os_version=$( ( . /etc/os-release; printf '%s' "${VERSION_ID:-0}" ) 2>/dev/null) || stats_os_version=0
        if command -v curl >/dev/null 2>&1; then
            curl --fail --silent --show-error --proto '=https' --tlsv1.2 --connect-timeout 2 --max-time 5 \
                --data-urlencode "instance=$instance" --data-urlencode "version=$release_version" \
                --data-urlencode "arch=$arch" --data-urlencode "os=$stats_os" --data-urlencode "os_version=$stats_os_version" \
                --data-urlencode "mode=$mode" --data 'consent=1' --data 'status=success' \
                https://wdcp.net/user/telemetry/install >/dev/null 2>&1 || true
        elif command -v wget >/dev/null 2>&1; then
            # Values originate only from bounded system identifiers and a random hex ID.
            case "$stats_os$stats_os_version" in *[!a-zA-Z0-9._-]*) exit 0;; esac
            wget --quiet --https-only --max-redirect=0 --timeout=5 --tries=1 -O /dev/null \
                --post-data="instance=$instance&version=$release_version&arch=$arch&os=$stats_os&os_version=$stats_os_version&mode=$mode&consent=1&status=success" \
                https://wdcp.net/user/telemetry/install >/dev/null 2>&1 || true
        fi
    ) || true
    return 0
}
if test "$mode" = upgrade; then
    token=$("$work/wdcp-updater" prepare --root "$root" --archive "$work/panel.tar.gz" --token-only)
    "$work/wdcp-updater" apply --root "$root" --token "$token" --health-url "http://127.0.0.1:$panel_port/healthz"
    wdcp_report_installation
    echo '面板升级成功；备份位于 /www/wdlinux/wdcp/update-backups。'
    exit 0
fi
if test "$mode" = full; then runtime_packages; fi
"$work/wdcp-updater" install --root "$root" --archive "$work/panel.tar.gz"
if test "$mode" = full; then
    runtime_configure
else
    printf '%s\n' "$panel_port" > "$root/conf/port.conf"
    if test -x /www/wdlinux/nginx/sbin/nginx; then
        if test -x /www/wdlinux/apache/bin/httpd; then engine=3; else engine=2; fi
    else engine=1; fi
    printf '%s\n' "$engine" > "$root/conf/eng.conf"
fi
if test "$init_system" = systemd; then
    ln -s "$root/wdcp.service" /etc/systemd/system/wdcp.service
    systemctl daemon-reload
    systemctl enable --now wdcp.service
else
    install -m 755 "$root/wdcp.openrc" /etc/init.d/wdcp
    rc-update add wdcp default
    rc-service wdcp start
fi
ready=0
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
    if command -v curl >/dev/null; then
        health=$(curl -fsS --max-time 3 "http://127.0.0.1:$panel_port/healthz" 2>/dev/null || true)
    else health=$(wget -qO- --timeout=3 "http://127.0.0.1:$panel_port/healthz" 2>/dev/null || true); fi
    health=$(printf '%s' "$health" | tr -d '[:space:]')
    if printf '%s' "$health" | grep -q '"status":"ok"' && printf '%s' "$health" | grep -Eq '"version":"v?4\.0\.2"'; then ready=1; break; fi
    sleep 1
done
test "$ready" = 1 || fail '服务未通过健康检查，请查看系统服务日志和面板 logs/service.log；数据和日志保留供排查。'
wdcp_report_installation
printf 'WDCP 4.0.2 安装完成。面板：http://服务器IP:%s\n' "$panel_port"
echo '管理员：admin；随机初始密码：/www/wdlinux/wdcp/data/initial-admin-password（仅 root 可读）。'
if test "$install_stats" = 1; then echo '已同意匿名安装统计：仅版本、架构、系统、安装模式和随机编号；上报失败不会影响安装。'; else echo '匿名安装统计未启用。可使用 --telemetry 自愿开启。'; fi
echo '登录后请修改密码并删除初始密码文件。请按需配置安全组/防火墙，面板端口仅允许管理 IP。'
if test "$mode" = full; then echo '网站目录：/www/web；数据库仅监听本机；FTPS：21、30000-30100，初始证书为自签名。'; fi
