#!/usr/bin/env bash
# 鹏佳云WAF installer — CentOS/RHEL / Debian/Ubuntu
# OpenResty (native) + SQLite control plane + OPS UI console on random port
set -euo pipefail

APP_NAME="鹏佳云WAF"
PORTAL="https://waf.pjyun.cc"
PREFIX="/opt/tutu-waf"
VERSION="1.0.0"
FORCE_OS=""
REINSTALL=0
TOTAL_STEPS=10
STEP=0
STARTED_AT="$(date +%s)"
INSTALL_LOG="/tmp/pjyun-waf-install.log"

C_G='\033[1;32m'
C_C='\033[1;36m'
C_Y='\033[1;33m'
C_R='\033[1;31m'
C_D='\033[90m'
C_B='\033[1m'
C_N='\033[0m'

elapsed() { echo "$(( $(date +%s) - STARTED_AT ))"; }

draw_bar() {
  local pct="$1" width=36
  [[ "$pct" -lt 0 ]] && pct=0
  [[ "$pct" -gt 100 ]] && pct=100
  local filled=$(( pct * width / 100 ))
  local empty=$(( width - filled ))
  local bar
  bar="$(printf '%*s' "$filled" '' | tr ' ' '#')"
  bar+="$(printf '%*s' "$empty" '' | tr ' ' '-')"
  printf "${C_C}[%s]${C_N} ${C_G}%3d%%${C_N}" "$bar" "$pct"
}

banner() {
  echo
  echo "============================================================"
  echo -e "  ${C_B}${APP_NAME}${C_N}  v${VERSION}"
  echo "  一键安装 · 每一步都有进度，请勿关闭窗口"
  echo "============================================================"
  echo
}

step_begin() {
  STEP=$((STEP + 1))
  local title="$1"
  local pct=$(( STEP * 100 / TOTAL_STEPS ))
  if [[ "$STEP" -lt "$TOTAL_STEPS" && "$pct" -gt 96 ]]; then pct=96; fi
  echo
  echo -e "${C_Y}[$STEP/$TOTAL_STEPS]${C_N} ${C_B}${title}${C_N}"
  printf "  "
  draw_bar "$pct"
  echo -e "  ${C_D}已用 $(elapsed)s${C_N}"
}

step_ok() {
  echo -e "  ${C_G}✓${C_N} $*"
}

note() { echo -e "  ${C_D}$*${C_N}"; }
log() { echo -e "${C_G}[+]${C_N} $*"; }
warn() { echo -e "${C_Y}[!]${C_N} $*"; }
die() { echo -e "${C_R}[x]${C_N} $*" >&2; exit 1; }

spin_cmd() {
  local title="$1"; shift
  note "$title"
  : >"$INSTALL_LOG"
  "$@" >"$INSTALL_LOG" 2>&1 &
  local pid=$!
  local frames='|/-\\'
  local i=0
  while kill -0 "$pid" 2>/dev/null; do
    i=$(( (i + 1) % 4 ))
    local last=""
    last="$(tail -n 1 "$INSTALL_LOG" 2>/dev/null | tr -d '\r' | cut -c1-68 || true)"
    printf "\r  ${C_C}%s${C_N} 进行中 ${C_D}%ds${C_N}  %-68s" "${frames:i:1}" "$(elapsed)" "${last:-请稍候，正在工作...}"
    sleep 0.25
  done
  local st=0
  wait "$pid" || st=$?
  printf "\r\033[K"
  if [[ "$st" -ne 0 ]]; then
    echo -e "  ${C_R}失败，最近日志：${C_N}"
    tail -n 40 "$INSTALL_LOG" || true
    return "$st"
  fi
  return 0
}

fetch() {
  local url="$1" dest="$2" name="${3:-$(basename "$2")}"
  mkdir -p "$(dirname "$dest")"
  printf "  ↓ %s  " "$name"
  if ! curl -fL --retry 3 --retry-delay 1 --connect-timeout 20 --progress-bar -o "$dest" "$url"; then
    echo
    die "下载失败: $name"
  fi
}

[[ ${EUID:-$(id -u)} -eq 0 ]] || die "请使用 root 运行"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --os) FORCE_OS="${2:-}"; shift 2 ;;
    --prefix) PREFIX="${2:-}"; shift 2 ;;
    --reinstall) REINSTALL=1; shift ;;
    -h|--help)
      cat <<EOF
$APP_NAME 安装脚本
用法: curl -fsSL $PORTAL/install.sh | bash
可选: --os el9|debian   --prefix /opt/tutu-waf
      --reinstall       已安装时允许先卸载再重装（仍会确认）
已安装请勿重复执行。升级请用控制台「更新」；卸载：tutuwaf uninstall
EOF
      exit 0 ;;
    *) die "未知参数: $1" ;;
  esac
done

banner

already_installed() {
  [[ -f "$PREFIX/data/waf.db" ]] && return 0
  [[ -x "$PREFIX/bin/console_api.py" ]] && return 0
  [[ -f /etc/systemd/system/tutu-waf.service ]] && return 0
  [[ -f /etc/systemd/system/tutu-waf-api.service ]] && return 0
  command -v tutuwaf >/dev/null 2>&1 && return 0
  return 1
}

show_existing() {
  local ver="" svc="" api=""
  if [[ -f "$PREFIX/data/waf.db" ]] && command -v python3 >/dev/null 2>&1; then
    ver="$(python3 - <<PY
import sqlite3,os
db=os.path.join("$PREFIX","data","waf.db")
try:
    con=sqlite3.connect(db)
    row=con.execute("select v from meta where k='version'").fetchone()
    print(row[0] if row else "")
except Exception:
    print("")
PY
)"
  fi
  svc="$(systemctl is-active tutu-waf 2>/dev/null || true)"
  api="$(systemctl is-active tutu-waf-api 2>/dev/null || true)"
  echo -e "  目录:   ${C_B}${PREFIX}${C_N}"
  [[ -n "$ver" ]] && echo "  版本:   $ver"
  echo "  服务:   tutu-waf=${svc:-未知}  api=${api:-未知}"
  [[ -f /root/tutu-waf-console.txt ]] && echo "  凭据:   /root/tutu-waf-console.txt"
}

uninstall_inline() {
  echo -e "  ${C_Y}正在卸载现有安装…${C_N}"
  systemctl stop tutu-waf tutu-waf-api 2>/dev/null || true
  systemctl disable tutu-waf tutu-waf-api 2>/dev/null || true
  rm -f /etc/systemd/system/tutu-waf.service /etc/systemd/system/tutu-waf-api.service
  rm -rf /etc/systemd/system/tutu-waf.service.d /etc/systemd/system/tutu-waf-api.service.d
  systemctl daemon-reload 2>/dev/null || true
  rm -f /usr/local/bin/tutuwaf /usr/local/bin/tw /usr/local/bin/tutu-waf
  rm -f /root/tutu-waf-console.txt
  rm -rf "$PREFIX"
  echo -e "  ${C_G}✓${C_N} 旧版本已卸载，开始全新安装"
}

guard_existing() {
  already_installed || return 0
  echo
  echo -e "${C_Y}============================================================${C_N}"
  echo -e "${C_Y}  检测到本机已经安装 $APP_NAME${C_N}"
  echo -e "${C_Y}============================================================${C_N}"
  show_existing
  echo
  echo "  再次安装会覆盖账号、端口和站点配置，导致无法登录。"
  echo "  日常升级请用控制台顶栏「更新」，不要重跑安装脚本。"
  echo
  echo "  1) 退出（推荐）"
  echo "  2) 先卸载再重新安装（会删除现有数据）"
  echo
  local ans=""
  if [[ -e /dev/tty ]]; then
    echo -n "  请选择 [1/2]，直接回车=退出: "
    read -r ans < /dev/tty || true
  elif [[ "$REINSTALL" == "1" ]]; then
    ans=2
  else
    echo
    die "已安装。请先执行: tutuwaf uninstall    或控制台点「更新」"
  fi
  ans="${ans:-1}"
  case "$ans" in
    2)
      local yes=""
      if [[ -e /dev/tty ]]; then
        echo -n "  确认删除 $PREFIX 及全部站点数据，请输入 yes: "
        read -r yes < /dev/tty || true
      elif [[ "$REINSTALL" == "1" ]]; then
        yes=yes
      fi
      [[ "$yes" == "yes" ]] || die "已取消，现有安装未改动"
      uninstall_inline
      ;;
    1|"")
      echo
      echo "  已退出，现有安装未改动。"
      echo "  升级: 打开控制台 → 更新"
      echo "  卸载: tutuwaf uninstall"
      echo "  登录: tutuwaf login"
      echo
      exit 0
      ;;
    *)
      die "已取消，现有安装未改动"
      ;;
  esac
}

guard_existing

detect_os() {
  if [[ -n "$FORCE_OS" ]]; then echo "$FORCE_OS"; return; fi
  if [[ -f /etc/os-release ]]; then
    # shellcheck disable=SC1091
    . /etc/os-release
    case "${ID_LIKE:-}${ID:-}" in
      *rhel*|*centos*|*fedora*|*rocky*|*alma*) echo "el"; return ;;
      *debian*|*ubuntu*) echo "debian"; return ;;
    esac
    case "${ID:-}" in
      centos|rhel|rocky|almalinux|fedora) echo "el"; return ;;
      debian|ubuntu) echo "debian"; return ;;
    esac
  fi
  die "无法识别系统，请用 --os el9 或 --os debian"
}

rand_port() {
  local p
  for _ in $(seq 1 40); do
    p=$((20000 + RANDOM % 40000))
    if ! ss -lntu 2>/dev/null | awk '{print $5}' | grep -Eq "[:.]${p}$"; then
      echo "$p"; return
    fi
  done
  echo "24680"
}

rand_user() {
  python3 - <<'PY'
import secrets, string
print("waf" + "".join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(6)))
PY
}

rand_pass() {
  python3 - <<'PY'
import secrets, string
alphabet = string.ascii_letters + string.digits
print("".join(secrets.choice(alphabet) for _ in range(16)))
PY
}

step_begin "检测系统"
OS_FAMILY="$(detect_os)"
step_ok "系统族: $OS_FAMILY"

install_openresty_el() {
  if command -v dnf >/dev/null 2>&1; then PM=dnf; else PM=yum; fi
  step_begin "安装系统依赖"
  note "安装 curl / python3 / openssl / sqlite"
  spin_cmd "yum/dnf 正在解析软件源，可能要 1–3 分钟" \
    "$PM" -y install curl ca-certificates python3 openssl sqlite || die "依赖安装失败"
  step_ok "依赖已就绪"

  step_begin "安装 OpenResty"
  if command -v openresty >/dev/null 2>&1 || [[ -x /usr/local/openresty/nginx/sbin/nginx ]]; then
    step_ok "OpenResty 已存在，跳过下载"
    return
  fi
  note "此步最久，CPU 升高是正常的，请耐心等待"
  if [[ ! -f /etc/yum.repos.d/openresty.repo ]]; then
    cat >/etc/yum.repos.d/openresty.repo <<'REPO'
[openresty]
name=Official OpenResty Repository
baseurl=https://openresty.org/package/centos/$releasever/$basearch
skip_if_unavailable=True
gpgcheck=0
enabled=1
REPO
  fi
  spin_cmd "正在下载并安装 OpenResty（通常 1–5 分钟）" \
    "$PM" -y install openresty || die "OpenResty 安装失败"
  step_ok "OpenResty 安装完成"
}

install_openresty_debian() {
  export DEBIAN_FRONTEND=noninteractive
  step_begin "安装系统依赖"
  spin_cmd "apt 更新软件源" apt-get update -y || die "apt update 失败"
  spin_cmd "安装 curl / python3 / openssl / sqlite" \
    apt-get install -y --no-install-recommends curl ca-certificates python3 openssl sqlite3 wget gnupg \
    || die "依赖安装失败"
  step_ok "依赖已就绪"

  step_begin "安装 OpenResty"
  if command -v openresty >/dev/null 2>&1 || [[ -x /usr/local/openresty/nginx/sbin/nginx ]]; then
    step_ok "OpenResty 已存在，跳过下载"
    return
  fi
  note "此步最久，请勿关闭窗口"
  wget -qO - https://openresty.org/package/pubkey.gpg | gpg --dearmor -o /usr/share/keyrings/openresty.gpg
  codename="$(. /etc/os-release; echo "${VERSION_CODENAME:-$(lsb_release -sc 2>/dev/null || echo stable)}")"
  echo "deb [signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/debian ${codename} openresty" \
    >/etc/apt/sources.list.d/openresty.list
  spin_cmd "apt 更新 OpenResty 源" apt-get update -y || die "apt update 失败"
  spin_cmd "正在安装 OpenResty（通常 1–5 分钟）" apt-get install -y openresty || die "OpenResty 安装失败"
  step_ok "OpenResty 安装完成"
}

case "$OS_FAMILY" in
  el|el9|rhel) install_openresty_el ;;
  debian|ubuntu) install_openresty_debian ;;
  *) die "不支持的 --os: $OS_FAMILY" ;;
esac

command -v python3 >/dev/null || die "需要 python3"
NGINX_BIN=""
for c in openresty /usr/local/openresty/nginx/sbin/nginx /usr/bin/openresty; do
  if command -v "$c" >/dev/null 2>&1; then NGINX_BIN="$(command -v "$c")"; break; fi
  if [[ -x "$c" ]]; then NGINX_BIN="$c"; break; fi
done
[[ -n "$NGINX_BIN" ]] || die "未找到 openresty"

step_begin "准备目录与登录凭据"
CONSOLE_PORT="$(rand_port)"
API_PORT="$(rand_port)"
while [[ "$API_PORT" == "$CONSOLE_PORT" ]]; do API_PORT="$(rand_port)"; done
ADMIN_USER="$(rand_user)"
ADMIN_PASS="$(rand_pass)"
SAFE_ENTRY="$(python3 - <<'PY'
import secrets
print(secrets.token_hex(4))
PY
)"
MACHINE_ID="$(cat /etc/machine-id 2>/dev/null || sha256sum /etc/hostname | awk '{print $1}')"
HOSTNAME_S="$(hostname -f 2>/dev/null || hostname)"
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
mkdir -p "$PREFIX"/{bin,conf,lua,data,logs,sites,console/assets}
chmod 750 "$PREFIX/data"
mkdir -p "$PREFIX/data/waf" "$PREFIX/sites" "$PREFIX/console/assets/echarts"
step_ok "目录 $PREFIX · 控制台端口 $CONSOLE_PORT"

step_begin "拉取控制台 / 引擎 / 地图资源"
note "大文件会显示下载进度条"
fetch "$PORTAL/assets/app.css" "$PREFIX/console/assets/app.css" "控制台样式"
fetch "$PORTAL/appliance/console/assets/echarts/echarts.5.4.1.min.js" "$PREFIX/console/assets/echarts/echarts.5.4.1.min.js" "图表库"
fetch "$PORTAL/appliance/console/assets/echarts/china.js" "$PREFIX/console/assets/echarts/china.js" "中国地图"
fetch "$PORTAL/appliance/console/assets/echarts/world.js" "$PREFIX/console/assets/echarts/world.js" "世界地图"
fetch "$PORTAL/appliance/console/assets/echarts/three.min.js" "$PREFIX/console/assets/echarts/three.min.js" "三维引擎"
fetch "$PORTAL/appliance/console/assets/echarts/globe3d.js" "$PREFIX/console/assets/echarts/globe3d.js" "地球组件"
fetch "$PORTAL/appliance/console/assets/echarts/screen-map.js" "$PREFIX/console/assets/echarts/screen-map.js" "大屏地图"
fetch "$PORTAL/appliance/console/assets/echarts/world.json" "$PREFIX/console/assets/echarts/world.json" "世界数据"
fetch "$PORTAL/appliance/console/assets/icons.svg" "$PREFIX/console/assets/icons.svg" "图标"
fetch "$PORTAL/appliance/console/login.html" "$PREFIX/console/login.html" "登录页"
fetch "$PORTAL/appliance/console/index.html" "$PREFIX/console/index.html" "控制台"
fetch "$PORTAL/appliance/console/screen.html" "$PREFIX/console/screen.html" "防护大屏"
fetch "$PORTAL/appliance/console/entry-denied.html" "$PREFIX/console/entry-denied.html" "入口拒绝页"
fetch "$PORTAL/appliance/bin/console_api.py" "$PREFIX/bin/console_api.py" "控制面 API"
fetch "$PORTAL/appliance/bin/waf_store.py" "$PREFIX/bin/waf_store.py" "配置存储"
fetch "$PORTAL/appliance/bin/ip_region.py" "$PREFIX/bin/ip_region.py" "IP 归属"
fetch "$PORTAL/appliance/bin/malicious_intel.py" "$PREFIX/bin/malicious_intel.py" "情报模块"
fetch "$PORTAL/appliance/bin/tutuwaf" "$PREFIX/bin/tutuwaf" "运维命令"
fetch "$PORTAL/appliance/lua/init.lua" "$PREFIX/lua/init.lua" "引擎初始化"
fetch "$PORTAL/appliance/lua/waf.lua" "$PREFIX/lua/waf.lua" "防护引擎"
fetch "$PORTAL/appliance/lua/traf.lua" "$PREFIX/lua/traf.lua" "流量统计"
fetch "$PORTAL/appliance/lua/traf_worker.lua" "$PREFIX/lua/traf_worker.lua" "流量工作进程"
fetch "$PORTAL/appliance/data/cn_ipv4.json" "$PREFIX/data/cn_ipv4.json" "国内 IP 库"
cp -f "$PREFIX/data/cn_ipv4.json" "$PREFIX/data/waf/cn_ipv4.json"
fetch "$PORTAL/appliance/data/malicious_ipv4.json" "$PREFIX/data/malicious_ipv4.json" "恶意 IP 库"
cp -f "$PREFIX/data/malicious_ipv4.json" "$PREFIX/data/waf/malicious_ipv4.json"
fetch "$PORTAL/appliance/data/malicious_meta.json" "$PREFIX/data/malicious_meta.json" "情报元数据"
cp -f "$PREFIX/data/malicious_meta.json" "$PREFIX/data/waf/malicious_meta.json"
chmod 644 "$PREFIX/data/"*.json "$PREFIX/data/waf/"*.json 2>/dev/null || true
chmod +x "$PREFIX/bin/console_api.py" "$PREFIX/bin/tutuwaf"
ln -sfn "$PREFIX/bin/tutuwaf" /usr/local/bin/tutuwaf
ln -sfn "$PREFIX/bin/tutuwaf" /usr/local/bin/tw
step_ok "文件已全部就位"

step_begin "初始化数据库与默认规则"
export TUTU_WAF_PREFIX="$PREFIX"
export TUTU_WAF_PORTAL="$PORTAL"
python3 - <<PY
import hashlib, os, secrets, sys, time
sys.path.insert(0, os.environ["TUTU_WAF_PREFIX"] + "/bin")
import waf_store
con = waf_store.init_db()
def meta(k,v): waf_store.meta_set(con, k, v)
meta("version", "$VERSION")
meta("plan", "free")
meta("portal", "$PORTAL")
meta("machine_id", "$MACHINE_ID")
meta("hostname", "$HOSTNAME_S")
meta("console_port", "$CONSOLE_PORT")
meta("api_port", "$API_PORT")
meta("safe_entry", "$SAFE_ENTRY")
meta("license_key", "")
meta("expires_at", "")
meta("config_version", "1")
user = "$ADMIN_USER"
pw = "$ADMIN_PASS"
salt = secrets.token_hex(8)
h = hashlib.sha256((salt+pw).encode()).hexdigest()
con.execute("DELETE FROM admins")
con.execute("DELETE FROM sessions")
con.execute("INSERT INTO admins(username,password_hash,salt,created_at) VALUES(?,?,?,?)",
            (user, h, salt, time.time()))
con.commit()
waf_store.export_runtime(con)
try:
    waf_store.write_console_nginx(con, reload=False)
except Exception as e:
    print("console_conf", e)
os.chmod(waf_store.DB, 0o600)
print("DB_OK", waf_store.DB)
PY
step_ok "SQLite 控制面已初始化"

step_begin "写入 Nginx 与系统服务"
if [[ -f /usr/local/openresty/nginx/conf/mime.types ]]; then
  ln -sfn /usr/local/openresty/nginx/conf/mime.types "$PREFIX/conf/mime.types"
elif [[ -f /etc/nginx/mime.types ]]; then
  ln -sfn /etc/nginx/mime.types "$PREFIX/conf/mime.types"
else
  echo 'types { text/html html htm; text/css css; application/javascript js; application/json json; }' >"$PREFIX/conf/mime.types"
fi

cat >"$PREFIX/conf/nginx.conf" <<EOF
worker_processes auto;
error_log $PREFIX/logs/error.log info;
pid $PREFIX/logs/nginx.pid;
events { worker_connections 10240; }
http {
  include       mime.types;
  default_type  application/octet-stream;
  log_format tutu_combined '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                     '\$status \$body_bytes_sent "\$http_referer" "\$http_user_agent" '
                     'rt=\$request_time urt=\$upstream_response_time';
  access_log    $PREFIX/logs/access.log tutu_combined;
  sendfile      on;
  keepalive_timeout 65;
  lua_package_path "$PREFIX/lua/?.lua;;";
  lua_shared_dict tutu_waf_limit 50m;
  lua_shared_dict tutu_waf_ban 20m;
  init_by_lua_file $PREFIX/lua/init.lua;
  log_by_lua_file $PREFIX/lua/traf.lua;
  init_worker_by_lua_file $PREFIX/lua/traf_worker.lua;
  server {
    listen 80 default_server;
    server_name _;
    rewrite_by_lua_file $PREFIX/lua/waf.lua;
    location / {
      return 200 '鹏佳云WAF edge is running.\\n';
      add_header Content-Type text/plain;
    }
  }
  include $PREFIX/sites/*.conf;
  include $PREFIX/conf/console.conf;
}
EOF

cat >"$PREFIX/conf/console.conf" <<EOF
server {
  listen $CONSOLE_PORT;
  server_name _;
  root $PREFIX/console;
  index index.html;

  location = /login {
    return 302 /login.html;
  }

  location /assets/ {
    try_files \$uri =404;
  }

  location /api/ {
    proxy_pass http://127.0.0.1:$API_PORT;
    proxy_http_version 1.1;
    proxy_set_header Host \$host;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header Cookie \$http_cookie;
  }

  location = / {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    add_header Pragma "no-cache";
    etag off;
    if_modified_since off;
    try_files /index.html =404;
  }

  location = /index.html {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    add_header Pragma "no-cache";
    etag off;
    if_modified_since off;
  }

  location = /login.html {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    add_header Pragma "no-cache";
    etag off;
    if_modified_since off;
  }

  location / {
    try_files \$uri \$uri/ /index.html;
  }
}
EOF

python3 - <<PY
import os,sys
sys.path.insert(0, os.environ.get("TUTU_WAF_PREFIX","$PREFIX")+"/bin")
import waf_store
con=waf_store.init_db()
print("rewrite_console", waf_store.write_console_nginx(con, reload=False))
PY

cat >"$PREFIX/bin/tutu-waf" <<EOF
#!/usr/bin/env bash
set -euo pipefail
PREFIX="$PREFIX"
NGINX="$NGINX_BIN"
CONF="\$PREFIX/conf/nginx.conf"
case "\${1:-}" in
  start) "\$NGINX" -p "\$PREFIX/" -c "\$CONF" ;;
  stop) "\$NGINX" -p "\$PREFIX/" -c "\$CONF" -s stop ;;
  reload) "\$NGINX" -p "\$PREFIX/" -c "\$CONF" -s reload ;;
  test) "\$NGINX" -p "\$PREFIX/" -c "\$CONF" -t ;;
  status)
    if [[ -f "\$PREFIX/logs/nginx.pid" ]] && kill -0 "\$(cat "\$PREFIX/logs/nginx.pid")" 2>/dev/null; then
      echo running; else echo stopped; fi ;;
  *) echo "usage: tutu-waf {start|stop|reload|test|status}"; exit 1 ;;
esac
EOF
chmod +x "$PREFIX/bin/tutu-waf"
ln -sfn "$PREFIX/bin/tutu-waf" /usr/local/bin/tutu-waf

cat >/etc/systemd/system/tutu-waf-api.service <<EOF
[Unit]
Description=鹏佳云WAF Console API
After=network.target
[Service]
Type=simple
Environment=TUTU_WAF_PREFIX=$PREFIX
Environment=TUTU_WAF_PORTAL=$PORTAL
Environment=TUTU_WAF_API_BIND=127.0.0.1
Environment=TUTU_WAF_API_PORT=$API_PORT
ExecStart=/usr/bin/python3 $PREFIX/bin/console_api.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF

cat >/etc/systemd/system/tutu-waf.service <<EOF
[Unit]
Description=鹏佳云WAF (OpenResty)
After=network.target tutu-waf-api.service
Requires=tutu-waf-api.service
[Service]
Type=forking
Environment=TZ=Asia/Shanghai
PIDFile=$PREFIX/logs/nginx.pid
ExecStartPre=$PREFIX/bin/tutu-waf test
ExecStart=$PREFIX/bin/tutu-waf start
ExecReload=$PREFIX/bin/tutu-waf reload
ExecStop=$PREFIX/bin/tutu-waf stop
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
step_ok "Nginx / systemd 已写入"

step_begin "配置防火墙"
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
  firewall-cmd --permanent --add-port=80/tcp >/dev/null 2>&1 || true
  firewall-cmd --permanent --add-port=443/tcp >/dev/null 2>&1 || true
  firewall-cmd --permanent --add-port="${CONSOLE_PORT}/tcp" >/dev/null 2>&1 || true
  firewall-cmd --reload >/dev/null 2>&1 || true
  step_ok "已放行 80 / 443 / 控制台端口 $CONSOLE_PORT"
else
  step_ok "未检测到 firewalld，请自行放行 80 / 443 / $CONSOLE_PORT"
fi

step_begin "启动服务"
systemctl daemon-reload
systemctl enable --now tutu-waf-api
sleep 1
"$PREFIX/bin/tutu-waf" test
systemctl enable --now tutu-waf
systemctl restart tutu-waf
step_ok "tutu-waf-api 与 tutu-waf 已启动"

CRED="/root/tutu-waf-console.txt"
cat >"$CRED" <<EOF
鹏佳云WAF 控制台
地址: http://${IP:-0.0.0.0}:${CONSOLE_PORT}/${SAFE_ENTRY}
用户: ${ADMIN_USER}
密码: ${ADMIN_PASS}
机器指纹: ${MACHINE_ID}
数据: ${PREFIX}/data/waf.db (SQLite)
忘记入口请执行: tutuwaf default
EOF
chmod 600 "$CRED"

step_begin "登记节点并完成"
python3 - <<PY || true
import json, urllib.request
payload = {
  "event": "install",
  "machine_id": "$MACHINE_ID",
  "hostname": "$HOSTNAME_S",
  "version": "$VERSION",
  "plan": "free",
  "public_ip": "$IP",
  "os_family": "$OS_FAMILY",
  "console_port": "$CONSOLE_PORT",
}
req = urllib.request.Request(
  "$PORTAL/api/node/heartbeat",
  data=json.dumps(payload).encode(),
  headers={"Content-Type": "application/json"},
  method="POST",
)
try:
    urllib.request.urlopen(req, timeout=8).read()
except Exception:
    pass
PY
printf "  "
draw_bar 100
echo
echo
echo "============================================================"
echo -e "  ${C_G}${APP_NAME} v${VERSION} 安装完成${C_N}  总耗时 $(elapsed)s"
echo "============================================================"
echo "  登录地址: http://${IP:-0.0.0.0}:${CONSOLE_PORT}/${SAFE_ENTRY}"
echo "  账号:     ${ADMIN_USER}"
echo "  密码:     ${ADMIN_PASS}"
echo "  凭据文件: ${CRED}"
echo "============================================================"
echo "  数据面 HTTP : http://${IP:-0.0.0.0}/"
echo "  控制面存储  : SQLite ${PREFIX}/data/waf.db"
echo "  授权门户    : ${PORTAL}"
