#!/bin/sh
set -e
PKG="strpanel-v1.0.0.tar.gz"
RES_BASE="https://panel.stryinl.xyz"
DIR="/opt/strpanel"
PORT=8090

echo "=== StRPanel install ==="

echo "[1] dependencies check"
if command -v node >/dev/null 2>&1; then
  NODE_BIN="$(command -v node)"
  NV="$(node -v 2>/dev/null | sed "s/^v//")"
  NM="${NV%%.*}"
  if [ "${NM:-0}" -ge 16 ]; then
    echo "  Node.js: $(node --version)"
  else
    echo "  Node.js >= 16 required"
    exit 1
  fi
else
  echo "  Node.js missing, attempting install..."
  if command -v apt-get >/dev/null 2>&1; then
    (apt-get update -qq && apt-get install -y nodejs) >/dev/null || { echo "  install failed"; exit 1; }
  elif command -v yum >/dev/null 2>&1; then
    yum install -y nodejs >/dev/null || { echo "  install failed"; exit 1; }
  elif command -v apk >/dev/null 2>&1; then
    apk add nodejs >/dev/null || { echo "  install failed"; exit 1; }
  else
    echo "  install Node.js >= 16 manually"
    exit 1
  fi
  NODE_BIN="$(command -v node)"
fi

if [ -d "$DIR" ]; then
  echo "[0] stop old instance"
  if command -v pkill >/dev/null 2>&1; then
    pkill -f "$DIR/strpanel-keepalive.sh" 2>/dev/null || true
    pkill -f "$DIR/strpanel.js" 2>/dev/null || true
  fi
  sleep 1
fi

echo "[2] fetch panel files"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)"
MANIFEST="strpanel-checksums.json"

# 统一下载：curl / wget / node 三通道，跟随重定向，失败即返回非零
dl() {
  _url="$1"; _out="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$_url" -o "$_out" || return 1
  elif command -v wget >/dev/null 2>&1; then
    wget -q "$_url" -O "$_out" || return 1
  else
    "$NODE_BIN" -e '
      const fs=require("fs"),https=require("https"),http=require("http"),{URL}=require("url");
      function fetch(u,out,hops){
        if(hops>5){console.error("too many redirects");process.exit(1)}
        const lib=u.startsWith("https")?https:http;
        lib.get(u,function(res){
          if(res.statusCode>=300&&res.statusCode<400&&res.headers.location){
            res.destroy();
            return fetch(new URL(res.headers.location,u).toString(),out,hops+1);
          }
          if(res.statusCode!==200){console.error("HTTP "+res.statusCode);process.exit(1)}
          const chunks=[];
          res.on("data",function(c){chunks.push(c)});
          res.on("end",function(){fs.writeFileSync(out,Buffer.concat(chunks))});
        }).on("error",function(e){console.error(e.message);process.exit(1)});
      }
      fetch(process.argv[1],process.argv[2],0);
    ' "$_url" "$_out" || return 1
  fi
}

if [ -f "$SCRIPT_DIR/$PKG" ]; then
  echo "  using local package"
  tar xzf "$SCRIPT_DIR/$PKG" -C "$WORK"
  SRC="$WORK/strpanel-v1.0.0/strpanel"
  [ -f "$WORK/strpanel-v1.0.0/$MANIFEST" ] && cp "$WORK/strpanel-v1.0.0/$MANIFEST" "$WORK/$MANIFEST"
else
  echo "  downloading from $RES_BASE"
  SRC="$WORK"
  mkdir -p "$WORK/js" "$WORK/css"
  for f in strpanel.js strpanel.html strpanel-login.html strpanel.png "$MANIFEST"; do
    echo "  fetch $RES_BASE/$f"
    dl "$RES_BASE/$f" "$WORK/$f" || { echo "  fetch failed: $f"; exit 1; }
  done
  # 文件清单来自发布清单，不能依赖开发机上的 /root/workspace 目录
  JS_LIST="$("$NODE_BIN" -e 'const m=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));process.stdout.write(Object.keys(m).filter(function(k){return /^js\/[A-Za-z0-9_.-]+\.js$/.test(k)}).map(function(k){return k.slice(3)}).join(" "))' "$WORK/$MANIFEST")" || { echo "  parse manifest failed"; exit 1; }
  CSS_LIST="$("$NODE_BIN" -e 'const m=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));process.stdout.write(Object.keys(m).filter(function(k){return /^css\/[A-Za-z0-9_.-]+\.css$/.test(k)}).map(function(k){return k.slice(4)}).join(" "))' "$WORK/$MANIFEST")" || { echo "  parse manifest failed"; exit 1; }
  [ -n "$JS_LIST" ] || { echo "  manifest has no js files"; exit 1; }
  [ -n "$CSS_LIST" ] || { echo "  manifest has no css files"; exit 1; }
  for f in $JS_LIST; do
    echo "  fetch $RES_BASE/js/$f"
    dl "$RES_BASE/js/$f" "$WORK/js/$f" || { echo "  fetch failed: js/$f"; exit 1; }
  done
  for f in $CSS_LIST; do
    echo "  fetch $RES_BASE/css/$f"
    dl "$RES_BASE/css/$f" "$WORK/css/$f" || { echo "  fetch failed: css/$f"; exit 1; }
  done
fi

echo "[3] write files to $DIR"
mkdir -p "$DIR/js" "$DIR/css"
for f in strpanel.js strpanel.html strpanel-login.html strpanel.png; do
  cp "$SRC/$f" "$DIR/"
done
for f in $SRC/js/*.js; do
  [ -f "$f" ] && cp "$f" "$DIR/js/"
done
for f in $SRC/css/*.css; do
  [ -f "$f" ] && cp "$f" "$DIR/css/"
done
[ -f "$WORK/$MANIFEST" ] && cp "$WORK/$MANIFEST" "$DIR/$MANIFEST"
# 装完自检：CSS 与 JS 目录不能为空，避免再次出现"页面只有文本"
for f in base.css components.css responsive.css; do
  [ -s "$DIR/css/$f" ] || { echo "  missing css/$f - install incomplete"; exit 1; }
done
[ -n "$(ls "$DIR/js"/*.js 2>/dev/null)" ] || { echo "  missing js files - install incomplete"; exit 1; }
echo "  css: $(ls "$DIR/css" | wc -l) files, js: $(ls "$DIR/js" | wc -l) files"
rm -rf "$WORK"

echo "[4] control scripts"
cat > "$DIR/strpanel-keepalive.sh" << 'KEEPALIVE_SH'
#!/bin/sh
NODE_BIN="__NODE_BIN__"
while true; do
  "$NODE_BIN" "__DIR__/strpanel.js" >> "__DIR__/strpanel.log" 2>&1
  sleep 2
done
KEEPALIVE_SH
sed -i "s|__NODE_BIN__|"$NODE_BIN"|g; s|__DIR__|"$DIR"|g" "$DIR/strpanel-keepalive.sh"

cat > "$DIR/strpanel-start.sh" << 'START_SH'
#!/bin/sh
DIR="$(cd "$(dirname "$0")" && pwd)"
pgrep -f "$DIR/strpanel.js" >/dev/null 2>&1 && { echo "running"; exit 0; }
if command -v setsid >/dev/null 2>&1; then
  setsid /bin/sh "$DIR/strpanel-keepalive.sh" </dev/null >/dev/null 2>&1 &
else
  nohup /bin/sh "$DIR/strpanel-keepalive.sh" </dev/null >/dev/null 2>&1 &
fi
sleep 2
pgrep -f "$DIR/strpanel.js" >/dev/null 2>&1 && echo "started" || { echo "start failed"; exit 1; }
START_SH

cat > "$DIR/strpanel-stop.sh" << 'STOP_SH'
#!/bin/sh
DIR="$(cd "$(dirname "$0")" && pwd)"
if command -v pkill >/dev/null 2>&1; then
  pkill -f "$DIR/strpanel-keepalive.sh" 2>/dev/null || true
  pkill -f "$DIR/strpanel.js" 2>/dev/null || true
else
  for f in /proc/[0-9]*/cmdline; do
    [ -r "$f" ] || continue
    C="$(tr "\\0" " " < "$f" 2>/dev/null)" || continue
    case "$C" in *"$DIR/strpanel.js"*|*"$DIR/strpanel-keepalive.sh"*) p=${f#/proc/}; p=${p%/cmdline}; kill "$p" 2>/dev/null || true ;; esac
  done
fi
sleep 1
STOP_SH

cat > "$DIR/strpanel-restart.sh" << 'RESTART_SH'
#!/bin/sh
D="$(cd "$(dirname "$0")" && pwd)"
sh "$D/strpanel-stop.sh"
sleep 1
sh "$D/strpanel-start.sh"
RESTART_SH

chmod 755 "$DIR"/strpanel-keepalive.sh "$DIR"/strpanel-start.sh "$DIR"/strpanel-stop.sh "$DIR"/strpanel-restart.sh

echo "[5] config"
cat > "$DIR/.panel-config.json" << CONF_EOF
{"entry":"strpanel","port":8090,"domains":[],"ipAllow":[],"ipBlock":[]}
CONF_EOF
chmod 600 "$DIR/.panel-config.json"

echo "[6] start"
sh "$DIR/strpanel-start.sh"

IP="$(hostname -I 2>/dev/null | tr " " "\\n" | grep -E "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$" | head -1)"
IP="${IP:-127.0.0.1}"

echo ""
echo "=== done ==="
echo "panel: http://$IP:$PORT/strpanel"
echo "login: admin / strpanel888"
echo "start: sh $DIR/strpanel-start.sh"
echo "stop:  sh $DIR/strpanel-stop.sh"
echo "log:   tail -f $DIR/strpanel.log"
