Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f0c3e3e29 | ||
|
|
c03f6ca9ba | ||
|
|
66f3ba7d06 | ||
|
|
511ba90378 | ||
|
|
01806d20dd | ||
|
|
049278c458 | ||
|
|
0aaa2d44a0 | ||
|
|
3f139a593e | ||
|
|
bee6fef69c | ||
|
|
f283a8a4c6 | ||
|
|
0d2433d850 | ||
|
|
a6b01bd8d5 | ||
|
|
8f2279b7e2 | ||
|
|
d37d26708d | ||
|
|
6234d7c49a | ||
|
|
f9a7bba32a | ||
|
|
068734da16 | ||
|
|
09a4108bcc | ||
|
|
a7f08ec7a7 | ||
|
|
f075fcce36 | ||
|
|
0bb96169e5 | ||
|
|
34320b2385 | ||
|
|
aeed9f0e57 | ||
|
|
c04a4c41e4 | ||
|
|
3b28ead0e4 | ||
|
|
d0ea5d09b1 | ||
|
|
a119973ec0 | ||
|
|
b8b91ac17e | ||
|
|
34f2c7e123 | ||
|
|
172f1e0209 | ||
|
|
1d5ed5d0cb | ||
|
|
1e150f26f1 | ||
|
|
03e735a44b | ||
|
|
011c1faad4 | ||
|
|
2325a8abdf | ||
|
|
201df489db | ||
|
|
2d680c5fc7 | ||
|
|
e77b7c0319 | ||
|
|
2283da7421 | ||
|
|
4a28f7f395 | ||
|
|
9a38fb62d0 | ||
|
|
fefd5dc60c | ||
|
|
89aa1ead33 | ||
|
|
a5f2fb53ed | ||
|
|
bd26f1011d | ||
|
|
cfaf156e03 | ||
|
|
75cf50ce0c | ||
|
|
e2b6bbc347 | ||
|
|
cd5160d1ea | ||
|
|
040827aa27 | ||
|
|
d00317a645 | ||
|
|
60317b4444 | ||
|
|
a0bb0f30f2 | ||
|
|
cc6f7bf958 | ||
|
|
583e0b00d8 | ||
|
|
c27f2fced9 | ||
|
|
8baa141339 | ||
|
|
2eceb43ac3 | ||
|
|
8ce9eb256b | ||
|
|
bf2cfb7da0 | ||
|
|
5dfaa19cca | ||
|
|
3404666b8e | ||
|
|
b64ab95658 | ||
|
|
f19d165ece | ||
|
|
602919dc18 | ||
|
|
722db9f6d1 | ||
|
|
970867f3c7 | ||
|
|
e22d8d3cd0 | ||
|
|
cfe6dd59c8 | ||
|
|
b46e257545 | ||
|
|
3e9a82a657 | ||
|
|
4cf687f436 | ||
|
|
e88cf4ac5b | ||
|
|
898349d22e | ||
|
|
9ea188cb6d | ||
|
|
03d4c6160a | ||
|
|
cf4433cdd6 | ||
|
|
ae80f25c67 | ||
|
|
5b2c294c61 | ||
|
|
a1595ab0c5 | ||
|
|
0be1e92ac5 | ||
|
|
cf3e5dca2f |
103
README.md
103
README.md
@@ -12,8 +12,11 @@
|
||||
|
||||
## ✨ 核心极客特性 (Core Architecture)
|
||||
|
||||
- 🎛️ **L5 降维中枢与动态状态机 (Microservices Console)**:引入四级战区降维解析与双轨身份制。全 Inline Keyboard 原位重绘交互,支持在 TG 面板一键下发毫秒级模块热启停 (Toggle)、丝滑改名与日志抓取,底层数据库实时刷新,彻底告别刷屏烦恼。
|
||||
- 🔄 **SSOT 溯源与热更新装甲 (Smooth Upgrade Engine)**:全系脚本彻底消灭硬编码,部署时动态抓取云端版本信标。搭载状态机嗅探逻辑与 OTA 预警探针,一键回车瞬间完成配置继承、数据同步与无损换代。
|
||||
- ⚡ **无损高并发引擎 (WAL Concurrency)**:司令部 SQLite 数据库全面激活 `WAL` (Write-Ahead Logging) 模式与毫秒级排队削峰算法。即使您同时对 500 台边缘节点发起全军总攻,也能完美规避 `database is locked` 与 Telegram `429` 频率拦截,实现 100% 战报送达。
|
||||
- 🪶 **抽脂级极简部署 (Zero-Bloat Native)**:全栈剔除 `pip`、`flask` 等臃肿第三方依赖,完全基于 Python3 原生标准库运行。安装底层强制注入 `--no-install-recommends` 防捆绑参数。无论是 128MB 内存的极简 NAT 小鸡,还是 Alpine/Arch Linux 特种系统,均可如丝般顺滑运行。
|
||||
- 🎛️ **扁平化指挥矩阵 (Flat Command Matrix)**:[v3.6.1 重构] 引入扁平化 L0-L3 四级战区降维视图与双轨身份制。深度定制 Inline Keyboard 逃生舱交互,支持在统一哨兵终端进行原位丝滑重绘 (In-place UI Edit),实现毫秒级模块热启停与日志抓取,彻底告别刷屏烦恼。
|
||||
- 🔄 **全栈零信任 OTA 引擎 (Zero-Trust OTA Upgrade)**:首创双端物理熔断机制。长官可通过私有中枢,一键向全舰队下发静默热重载指令;更支持**「司令部金蝉脱壳」**,中枢大脑可在此面板自我抛出幽灵进程进行免交互直装覆盖,实现真正的全栈去 SSH 化运维。
|
||||
- 🛡️ **SSOT 溯源与热更新装甲 (Smooth Upgrade Engine)**:全系脚本彻底消灭硬编码,部署时动态抓取云端版本信标。自带状态机嗅探逻辑与防撞甲探测,即使是手动在终端运行安装,也仅需回车瞬间完成配置继承、数据同步与无损换代。
|
||||
- 🗺️ **全球拓扑矩阵与活体词库 (Global Nexus)**:守护版图横跨亚欧美三大洲。接入 GitHub Actions 云端流水线,每日静默同步全球各大区当日 Google 真实热搜榜单与高权重本土站点,让伪装行为永远贴合当地网络脉搏。
|
||||
- 👻 **资产持久化与错峰调度 (Hash-Seeded Persona)**:摒弃随机抽取指纹,基于节点物理 IP 哈希永久锁定 3 个绝对专属设备,完美构建高权重真实家庭内网画像。叠加按需智能分频与随机防并发休眠,化解“惊群效应”。
|
||||
- 🖧 **底层路由死锁与高精度探针 (Hard-Bind Routing)**:底层探测引擎强力接管 curl 核心参数 (`--interface`),将发出的每一滴伪装流量死死绑定在物理网卡或隧道 IP 上。配合多级 ISP 容灾链路,彻底杜绝双栈环境下的流量溢出与 API 误判。
|
||||
@@ -21,7 +24,7 @@
|
||||
**—— 💎 骨干基建特征 ——**
|
||||
- 🏭 **自动化指纹兵工厂**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守锻造 4000+ 带绝对物理分区的真实终端设备数据。
|
||||
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),未授权请求直接触发系统级 403 物理熔断,彻底免疫中间人抓包与重放攻击。
|
||||
- ☁️ **云端中枢 (Public Master)**:官方公共机器人 @OmniBeacon_bot,新手免自建,一键接入极速入伍!同时支持硬核极客私有化 SQLite 分布式部署。
|
||||
- ☁️ **云端中枢 (Public Master)**:官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) ,新手免自建,一键接入极速入伍!同时支持硬核极客私有化 SQLite 分布式部署。
|
||||
- 👁️🗨️ **玻璃房透明遥测 (Glasshouse)**:基于 Cloudflare Workers 的全透明计数中枢,绝对零隐私收集,仅作原子累加,底层网关源码全开源。
|
||||
|
||||
## 📂 项目架构 (Monorepo)
|
||||
@@ -39,54 +42,60 @@
|
||||
┃ ┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
|
||||
┃ ┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
|
||||
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
|
||||
┣ 📜 version.txt # 🚩 双端版本信标:Agent/Master 独立解耦的 KV 环境配置 (v3.5.1)
|
||||
┣ 📜 version.txt # 🚩 双端版本信标:Agent/Master 独立解耦的 KV 环境配置
|
||||
┗ 📂 telemetry/ # 👁️🗨️ 玻璃房计划:Cloudflare Workers 透明计数器网关源码
|
||||
```
|
||||
|
||||
## 🚀 极速部署 (Quick Start)
|
||||
> 🛡️ **跨平台装甲支持**:Debian / Ubuntu / CentOS / RHEL / Alpine Linux / Arch Linux
|
||||
系统现提供两种接入模式,请根据您的战术需求选择:
|
||||
|
||||
v3.5.x 提供了两种接入模式,请根据您的战术需求选择:
|
||||
### 🔹 模式 A:私有独立模式 (全自主、强烈推荐)
|
||||
适合追求绝对数据隐私与舰队最高控制权的领主。
|
||||
|
||||
### 🔹 模式 A:官方公共模式 (最简、推荐)
|
||||
**适合不想折腾、只想快速养护 IP 的新兵。**
|
||||
|
||||
1. **关注机器人**:在 TG 中关注 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) 并发送 `/start`。
|
||||
2. **部署 Agent**:在目标 VPS 上执行以下指令,安装过程中**直接回车**使用官方机器人,并输入您的 Chat ID:
|
||||
```Bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
|
||||
> ☢️ **核按钮系统已就绪**:采用私有部署,您将解锁 **OTA 远程静默升级** 权限!所有私有前线节点均可通过您的 TG 面板实现一键全网代码热重载换代!
|
||||
|
||||
- **部署 Master (中枢大脑)**:找一台 VPS 作为司令部(仅需部署一台),执行:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh -o /tmp/ins_master.sh && sudo bash /tmp/ins_master.sh
|
||||
```
|
||||
3. **激活节点**:安装完成后,您的手机会收到一条 #REGISTER# 暗号,将其转发给机器人即可完成入库。
|
||||
- 部署 Agent (边缘哨兵):在需要养护的机器上执行 Agent 脚本,安装时选择私有独立中枢,并分别输入您自建机器人的 [Token](https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather) 以及您的个人 [Chat ID](https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot) :
|
||||
|
||||
### 🔸 模式 B:私有独立模式 (全自主、硬核)
|
||||
**适合追求绝对数据隐私、需自建机器人的领主。**
|
||||
|
||||
1. **部署 Master**:找一台 VPS 作为大脑(仅需部署一台),执行:
|
||||
```Bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh)
|
||||
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh -o /tmp/ins_agent.sh && sudo bash /tmp/ins_agent.sh
|
||||
```
|
||||
2. **部署 Agent**:在需要养护的机器上执行 Agent 脚本,输入您自建机器人的 Token 以及与 Master 一致的配置。
|
||||
- 激活节点:安装完成后,您的手机会收到一条 #REGISTER# 注册暗号,将其转发给您自己的机器人即可完成编队入库。
|
||||
|
||||
### 🔸 模式 B:官方公共模式 (最简体验)
|
||||
适合不想折腾、只想快速体验养护效果的新兵。
|
||||
|
||||
- 关注机器人:在 TG 中关注官方安全网关 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) 并发送 /start。
|
||||
|
||||
- 部署 Agent:在目标 VPS 上执行以下指令,安装过程中选择官方公共网关,并输入您的 Chat ID:
|
||||
|
||||
```Bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh)
|
||||
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh -o /tmp/ins_agent.sh && sudo bash /tmp/ins_agent.sh
|
||||
```
|
||||
3. **激活节点**:同上,将暗号转发给您自己的机器人即可。
|
||||
- 激活节点:同上,将收到的暗号转发给官方机器人即可。
|
||||
|
||||
### ⚠️ 架构级热升级指引 (Upgrade to v3.5.0)
|
||||
## 🆙 架构级无损热升级指引 (Upgrade Guide)
|
||||
|
||||
得益于 **v3.5.0 全新引入的 SSOT 版本锚点与状态机路由**,系统升级现已变得极其智能化。
|
||||
### 📡 方式一:OTA 远程静默升级 (私有中枢专属)
|
||||
如果您是私有中枢领主,当司令部首页 (`/start`) 或每日战报提示发现新版本时:
|
||||
|
||||
**如果您是从远古旧版 (v3.3.1 / v3.3.2) 升级:**
|
||||
1. 在终端再次运行对应的官方部署指令。
|
||||
2. 脚本会识别到您处于“前版本锚点时代”,会自动为您执行【跨代架构重组】。
|
||||
3. **关键动作**:由于节点命名防撞机制变更,升级后您的 TG 会收到一条新的 `#REGISTER#` 指令,请点击并发送一次以同步新身份。
|
||||
4. **清理**:在面板中手动剔除失联的旧节点即可。
|
||||
1. **升级 Master 司令部自身**:在司令部顶级菜单,点击最上方的 `[ 🆙 升级司令部至 vX.X.X ]`。中枢将释放幽灵进程静默重构,数秒后向您发送捷报。
|
||||
2. **升级全舰队 Agent**:在司令部顶级菜单,点击 `[ ☢️ 全舰队 OTA 热重载 ]`。
|
||||
3. **升级单节点 Agent**:进入 `🌍 全球战区雷达` -> 选择目标节点 -> 在统一终端面板点击 `[ 🆙 OTA 静默升级 ]`。
|
||||
*(⚠️ 节点收到指令后会在后台挂起静默拉取,全程无需登录 SSH,完成后将主动发回心跳确认!)*
|
||||
|
||||
**如果您已处于 v3.4.0+:**
|
||||
所有的升级已进入**“极致静默平滑模式”**。安装引擎会动态抓取云端 `version.txt`,自动修正本地 `config.conf` 的版本号,一键回车,3 秒即可完成全系组件的热重载换代!
|
||||
### 💻 方式二:SSH 终端平滑直装 (适用于官方网关或老旧节点)
|
||||
如果您的节点不支持 OTA,或者您的节点版本过于陈旧 (如 v3.3.1):
|
||||
|
||||
🗑️ 一键无痕卸载
|
||||
- 登录该节点的 SSH 终端,再次运行上面的 core/install.sh 官方安装指令。
|
||||
|
||||
- 安装引擎自带状态机嗅探逻辑,它会自动读取老旧数据,您只需一路回车,3 秒即可在本地完成配置继承、数据同步与新内核的无损覆盖热重载!
|
||||
|
||||
## 🗑️ 一键无痕卸载
|
||||
如果你需要清理某个边缘节点,只需重新运行 `core/install.sh` 并选择 **[2]**,或直接在节点终端执行:
|
||||
|
||||
```Bash
|
||||
@@ -94,7 +103,7 @@ bash /opt/ip_sentinel/core/uninstall.sh
|
||||
|
||||
```
|
||||
|
||||
### 🧓 传家宝老旧系统专用通道 (Debian 9)
|
||||
## 🧓 传家宝老旧系统专用通道 (Debian 9)
|
||||
|
||||
如果你的小鸡系统版本过低(如 Debian 9),由于官方 APT 源已关闭且 Python 版本过旧,无法使用主线版本,请使用 **Legacy 兼容分支** 部署。
|
||||
*(注意:该分支仅作基础维护,不享受新功能迭代,请尽可能升级你的系统)*
|
||||
@@ -103,20 +112,36 @@ bash /opt/ip_sentinel/core/uninstall.sh
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy/core/install.sh)
|
||||
```
|
||||
|
||||
📡 战术联络 (Community)
|
||||
## 📡 战术联络 (Community)
|
||||
|
||||
如果你在使用过程中遇到任何疑难杂症,或者想围观大佬们的养护战报,欢迎加入我们的基地:
|
||||
- Telegram 频道: [@IP_Sentinel_Matrix](https://t.me/IP_Sentinel_Matrix)
|
||||
|
||||
🤝 参与贡献
|
||||
如果你想为项目增加新的节点区域(例如德国、英国、新加坡等),或者提供更丰富的本土化搜索词库,非常欢迎提交 Pull Request!
|
||||
## 🤝 参与贡献 (Contributors)
|
||||
|
||||
**v3.0 全球节点贡献规范:**
|
||||
**🌟 感谢以下所有为 IP-Sentinel 添砖加瓦的指挥官们!** 你们的每一次 PR 都在让这艘战舰的全球雷达覆盖得更广。
|
||||
|
||||
<a href="https://github.com/hotyue/IP-Sentinel/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=hotyue/IP-Sentinel" alt="Contributors" />
|
||||
</a>
|
||||
|
||||
如果你想为项目增加新的节点区域(例如德国、英国、大洋洲等),或者提供更丰富的本土化搜索词库,非常欢迎提交 Pull Request!
|
||||
|
||||
**💡 全球节点贡献规范:**
|
||||
1. 在 `data/regions/国家代码/省州代码/` 目录下新增对应城市的配置 `.json`。
|
||||
2. 在 `data/keywords/` 目录下新增或完善配套国家的词库 `kw_XX.txt`。
|
||||
3. **最重要的一步:** 在 `data/map.json` 中登记你的国家、省州与城市信息。安装脚本将自动读取地图,在全球雷达中点亮你的节点!
|
||||
|
||||
⚠️ 免责声明
|
||||
## ⚠️ 免责声明
|
||||
|
||||
本项目仅供网络原理研究、个人 VPS 维护学习使用。请遵守当地法律法规及目标服务商的 TOS(服务条款),切勿用于恶意高频请求或任何非法用途。使用者需自行承担因不当使用造成的 IP 封禁或其他相关风险。
|
||||
|
||||
## 保持联系
|
||||
|
||||
[](https://blog.iot-architect.com)
|
||||
|
||||
如果你觉得这个项目对你有帮助,欢迎关注我的个人博客,我会定期分享技术教程。
|
||||
|
||||
|
||||
## Stargazers over time
|
||||
[](https://starchart.cc/hotyue/IP-Sentinel)
|
||||
@@ -24,10 +24,6 @@ if [ -z "$NODE_NAME" ]; then
|
||||
fi
|
||||
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
|
||||
|
||||
# --- [重点升级 1: 守护进程防冲突自检] ---
|
||||
if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. 尝试获取实时公网 IP
|
||||
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||
@@ -65,6 +61,20 @@ if [ -n "$AGENT_IP" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.6.3 新增: 自动生成自签名 TLS 加密证书] ==================
|
||||
# [修复] 仅在私有中枢模式下生成证书。官方网关模式下,CF Worker 严格拒绝自签名,必须回退 HTTP
|
||||
if [ "$TG_TOKEN" != "OFFICIAL_GATEWAY_MODE" ]; then
|
||||
CERT_FILE="${INSTALL_DIR}/core/cert.pem"
|
||||
KEY_FILE="${INSTALL_DIR}/core/key.pem"
|
||||
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||
echo "🔐 [Agent] 正在生成本地自签名 TLS 加密证书 (2048位 RSA)..."
|
||||
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||
-keyout "$KEY_FILE" -out "$CERT_FILE" \
|
||||
-subj "/C=US/O=IP-Sentinel/CN=Agent-Sec" >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
# ==============================================================================
|
||||
|
||||
# 3. 启动轻量级 Python3 Webhook 监听服务 (v3.0.4 动态 HMAC 签名防重放)
|
||||
cat > "${INSTALL_DIR}/core/webhook.py" << 'EOF'
|
||||
import http.server
|
||||
@@ -340,6 +350,80 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
|
||||
# ================== [v3.6.0 新增: 零信任 OTA 远程静默升级路由] ==================
|
||||
elif req_path == '/trigger_ota':
|
||||
try:
|
||||
# 动态读取最新 config 内存态
|
||||
config_mem = {}
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r', errors='ignore') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if '=' in line and not line.startswith('#'):
|
||||
key, val = line.split('=', 1)
|
||||
config_mem[key] = val.strip('"\'')
|
||||
|
||||
# 🛡️ 熔断校验 1: Agent 本地是否开启了 OTA 授权
|
||||
if config_mem.get('ENABLE_OTA', 'false').lower() != 'true':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA Upgrade Disabled locally\n")
|
||||
return
|
||||
|
||||
# 🛡️ 熔断校验 2: 是否处于官方公共网关下 (强行硬编码拦截)
|
||||
if config_mem.get('TG_TOKEN', '') == 'OFFICIAL_GATEWAY_MODE':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA strictly disabled under Public Gateway mode\n")
|
||||
return
|
||||
|
||||
# 校验通过,立即返回 200 回执,释放 Master 连接池
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_ota\n")
|
||||
|
||||
# [修复] 逃逸 Systemd Cgroup,并引入 bash -n 语法树校验防砖机制
|
||||
import shutil
|
||||
import base64
|
||||
repo_url = "https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
|
||||
# 动态构建报错回执文本 (第一层 Base64 隔离换行与特殊字符)
|
||||
err_msg = f"❌ **OTA 熔断告警**\n📍 节点: `{config_mem.get('NODE_ALIAS', '未知')}`\n⚠️ 原因: 脚本语法校验(bash -n)未通过,下载可能不完整。\n🚀 状态: 升级已取消,节点安全。"
|
||||
err_msg_b64 = base64.b64encode(err_msg.encode('utf-8')).decode('utf-8')
|
||||
|
||||
tg_url = config_mem.get('TG_API_URL', '')
|
||||
chat_id = config_mem.get('CHAT_ID', '')
|
||||
|
||||
# [v3.6.3 究极防御] 采用 Base64 将整个 OTA 执行脚本封装 (第二层隔离)
|
||||
# 彻底免疫因为 python 变量掺杂引号而导致的 shell 注入或截断
|
||||
ota_script = f"""
|
||||
export SILENT_OTA="true"
|
||||
curl -fsSL {repo_url}/core/install.sh -o /tmp/ota_agent.sh
|
||||
if bash -n /tmp/ota_agent.sh; then
|
||||
bash /tmp/ota_agent.sh > /opt/ip_sentinel/logs/ota_upgrade.log 2>&1
|
||||
else
|
||||
MSG=$(echo '{err_msg_b64}' | base64 -d)
|
||||
curl -s -m 10 -X POST "{tg_url}" -d "chat_id={chat_id}" -d "text=$MSG" -d "parse_mode=Markdown" > /dev/null 2>&1
|
||||
echo "OTA Checksum Failed: Script corrupted" > /opt/ip_sentinel/logs/ota_upgrade.log
|
||||
fi
|
||||
"""
|
||||
ota_script_b64 = base64.b64encode(ota_script.encode('utf-8')).decode('utf-8')
|
||||
|
||||
# 安全解包并执行
|
||||
if shutil.which("systemd-run"):
|
||||
full_cmd = f"systemd-run --quiet --no-block bash -c \"echo '{ota_script_b64}' | base64 -d | bash\""
|
||||
else:
|
||||
full_cmd = f"nohup bash -c \"echo '{ota_script_b64}' | base64 -d | bash\" >/dev/null 2>&1 &"
|
||||
|
||||
subprocess.Popen(full_cmd, shell=True)
|
||||
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
@@ -349,21 +433,49 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
import socket
|
||||
# ================== [v3.0.3 变更: 引入多线程模型抵抗 Slowloris 攻击] ==================
|
||||
class ThreadedDualStackServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
allow_reuse_address = True # 开启端口复用,防止热重启时端口冲突
|
||||
address_family = socket.AF_INET6 if socket.has_ipv6 else socket.AF_INET
|
||||
|
||||
try:
|
||||
bind_addr = "::" if socket.has_ipv6 else ""
|
||||
with ThreadedDualStackServer((bind_addr, PORT), AgentHandler) as httpd:
|
||||
httpd.serve_forever()
|
||||
# 1. 优先尝试监听双栈/IPv6 (大多数 Linux 默认支持 IPv4 映射接入)
|
||||
ThreadedServer.address_family = socket.AF_INET6
|
||||
httpd = ThreadedServer(("::", PORT), AgentHandler)
|
||||
except Exception:
|
||||
# 2. [核心修复 Issue #23] 若系统内核已禁用 IPv6,抛弃报错,智能回退至纯 IPv4 监听
|
||||
ThreadedServer.address_family = socket.AF_INET
|
||||
httpd = ThreadedServer(("0.0.0.0", PORT), AgentHandler)
|
||||
|
||||
# ================== [v3.6.3 核心: 挂载 TLS 加密隧道 (动态适配兼容版)] ==================
|
||||
import ssl
|
||||
cert_path = '/opt/ip_sentinel/core/cert.pem'
|
||||
key_path = '/opt/ip_sentinel/core/key.pem'
|
||||
|
||||
# 核心判定:提取配置中的 TOKEN 标识
|
||||
is_official_gateway = False
|
||||
if os.path.exists('/opt/ip_sentinel/config.conf'):
|
||||
with open('/opt/ip_sentinel/config.conf', 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('TG_TOKEN=') and 'OFFICIAL_GATEWAY_MODE' in line:
|
||||
is_official_gateway = True
|
||||
break
|
||||
|
||||
# 仅在非官方网关且证书存在时,才挂载 TLS 装甲
|
||||
if not is_official_gateway and os.path.exists(cert_path) and os.path.exists(key_path):
|
||||
try:
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
context.load_cert_chain(certfile=cert_path, keyfile=key_path)
|
||||
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
|
||||
except Exception as e:
|
||||
print(f"SSL 隧道构建失败,退化为 HTTP: {e}")
|
||||
# ======================================================================================
|
||||
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except Exception as e:
|
||||
sys.exit(1)
|
||||
# ====================================================================================
|
||||
EOF
|
||||
|
||||
# --- [重点升级 3: 真正的静默后台启动] ---
|
||||
echo "🚀 [Agent] 正在后台启动 Webhook 监听服务 (端口: $AGENT_PORT)..."
|
||||
nohup python3 "${INSTALL_DIR}/core/webhook.py" "$AGENT_PORT" > /dev/null 2>&1 &
|
||||
disown 2>/dev/null || true
|
||||
echo "✅ [Agent] 守护进程启动完毕,可安全关闭终端。"
|
||||
# --- [重点升级 3: 移交系统级守护进程接管 (阻塞模式)] ---
|
||||
echo "🚀 [Agent] 正在启动 Webhook 监听服务 (端口: $AGENT_PORT)..."
|
||||
exec python3 "${INSTALL_DIR}/core/webhook.py" "$AGENT_PORT"
|
||||
320
core/install.sh
320
core/install.sh
@@ -5,10 +5,19 @@
|
||||
# 核心功能: 战区分组菜单、模块按需开启、官方机器人一键配置、版本状态机路由
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 部署 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为私库地址用于测试
|
||||
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
|
||||
@@ -44,37 +53,41 @@ if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
# Debian / Ubuntu 系列
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
apt-get install -y curl jq cron procps python3 >/dev/null 2>&1
|
||||
# [v3.6.3 抽脂级优化] 注入 --no-install-recommends 拒绝捆绑销售,大幅节省磁盘与内存
|
||||
apt-get install -y --no-install-recommends curl jq cron procps python3 >/dev/null 2>&1
|
||||
systemctl enable cron >/dev/null 2>&1 && systemctl start cron >/dev/null 2>&1
|
||||
|
||||
elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1; then
|
||||
# RHEL / CentOS / AlmaLinux 系列
|
||||
PKG_MGR="yum"
|
||||
command -v dnf >/dev/null 2>&1 && PKG_MGR="dnf"
|
||||
$PKG_MGR install -y curl jq cronie procps-ng python3 >/dev/null 2>&1
|
||||
OPT_ARGS=""
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="dnf"
|
||||
# [v3.6.3 抽脂级优化] 强行关闭 DNF 的弱依赖拉取
|
||||
OPT_ARGS="--setopt=install_weak_deps=False"
|
||||
fi
|
||||
$PKG_MGR install -y $OPT_ARGS curl jq cronie procps-ng python3 >/dev/null 2>&1
|
||||
systemctl enable crond >/dev/null 2>&1 && systemctl start crond >/dev/null 2>&1
|
||||
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
# [核心修复 Issue #21] Alpine Linux 系列
|
||||
# Alpine 本身就是极致精简,无需特殊参数
|
||||
echo "Alpine 探测到系统类型为 Alpine Linux,正在执行轻量级安装..."
|
||||
apk add --no-cache curl jq dcron procps python3 bash >/dev/null 2>&1
|
||||
# Alpine 下必须手动创建 cron spool 目录并启动 crond
|
||||
mkdir -p /var/spool/cron/crontabs
|
||||
rc-update add crond default >/dev/null 2>&1
|
||||
service crond start >/dev/null 2>&1
|
||||
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
# [核心修复 Issue #250] Arch Linux 系列
|
||||
# Arch Linux 系列
|
||||
pacman -Sy --noconfirm curl jq cronie procps-ng python >/dev/null 2>&1
|
||||
# Arch 下某些 cronie 实现可能缺少 /root/.cache 权限,做个兼容保障
|
||||
mkdir -p /root/.cache/crontab 2>/dev/null
|
||||
systemctl enable cronie >/dev/null 2>&1 && systemctl start cronie >/dev/null 2>&1
|
||||
|
||||
else
|
||||
# 无法识别的系统:退出并给出清晰的引导信息
|
||||
# 无法识别的系统:退出并给出清晰的引导信息 (同步更新防捆绑参数)
|
||||
echo -e "\033[31m❌ 自动安装失败:系统未知的包管理器。\033[0m"
|
||||
echo -e "\033[33m⚠️ 请根据您的操作系统,手动执行以下安装命令后重新运行本脚本:\033[0m"
|
||||
echo -e " Debian/Ubuntu: \033[36mapt-get update && apt-get install -y curl jq cron procps python3\033[0m"
|
||||
echo -e " Debian/Ubuntu: \033[36mapt-get update && apt-get install -y --no-install-recommends curl jq cron procps python3\033[0m"
|
||||
echo -e " CentOS/RHEL: \033[36myum install -y curl jq cronie procps-ng python3\033[0m"
|
||||
echo -e " Alpine Linux: \033[36mapk add --no-cache curl jq dcron procps python3 bash\033[0m"
|
||||
echo -e " Arch Linux: \033[36mpacman -Sy curl jq cronie procps-ng python\033[0m"
|
||||
@@ -102,48 +115,64 @@ if [ ! -s "/tmp/map.json" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署边缘节点 (进入全球节点配置)"
|
||||
echo " 2) 🗑️ 一键卸载 IP-Sentinel"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
# ==========================================================
|
||||
# [v3.6.0 核心] 拦截静默 OTA 升级模式 (强行接管执行流,跳过人工交互)
|
||||
# ==========================================================
|
||||
if [ "$SILENT_OTA" == "true" ]; then
|
||||
echo -e "\n⏳ [OTA] 静默升级指令已确认,正在剥离控制台交互..."
|
||||
ACTION_CHOICE=1
|
||||
UPGRADE_MODE="true"
|
||||
KEEP_LOGS="true"
|
||||
source "$CONFIG_FILE"
|
||||
else
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署边缘节点 (进入全球节点配置)"
|
||||
echo " 2) 🗑️ 一键卸载 IP-Sentinel"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
echo -e "\n⏳ 正在拉取卸载程序..."
|
||||
curl -sL "${REPO_RAW_URL}/core/uninstall.sh" -o "/tmp/ip_uninstall.sh"
|
||||
chmod +x "/tmp/ip_uninstall.sh"
|
||||
bash "/tmp/ip_uninstall.sh"
|
||||
rm -f "/tmp/ip_uninstall.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_LOGS="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "$CONFIG_FILE" ]; then
|
||||
echo -e "\n\033[33m💡 哨兵雷达提示:检测到本机已部署过 IP-Sentinel。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史运行日志?(y/n, 默认y): " LOG_CHOICE
|
||||
if [[ "$LOG_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_LOGS="false"
|
||||
fi
|
||||
|
||||
# 将原配置读入环境变量,为后续跳过配置步骤提供燃料
|
||||
source "$CONFIG_FILE"
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心装甲...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的哨兵数据将被彻底抹除。\033[0m"
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
echo -e "\n⏳ 正在拉取卸载程序..."
|
||||
curl -sL "${REPO_RAW_URL}/core/uninstall.sh" -o "/tmp/ip_uninstall.sh"
|
||||
chmod +x "/tmp/ip_uninstall.sh"
|
||||
bash "/tmp/ip_uninstall.sh"
|
||||
rm -f "/tmp/ip_uninstall.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_LOGS="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "$CONFIG_FILE" ]; then
|
||||
echo -e "\n\033[33m💡 哨兵雷达提示:检测到本机已部署过 IP-Sentinel。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史运行日志?(y/n, 默认y): " LOG_CHOICE
|
||||
if [[ "$LOG_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_LOGS="false"
|
||||
fi
|
||||
|
||||
# 将原配置读入环境变量,为后续跳过配置步骤提供燃料
|
||||
source "$CONFIG_FILE"
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心装甲...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的哨兵数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
# ====================================================================
|
||||
fi
|
||||
# ====================================================================
|
||||
|
||||
# ================== [v3.1.1/v3.2.2 优化: 安装前环境纯净度清理] ==================
|
||||
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务..."
|
||||
# [新增] 优雅停止 Systemd 服务,防止代码替换时引发无限复活风暴
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl stop ip-sentinel-runner.timer ip-sentinel-updater.timer ip-sentinel-report.timer ip-sentinel-agent-daemon.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# 1. 强制超度可能存活的 Webhook 及各类看门狗进程,释放端口
|
||||
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
|
||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
|
||||
@@ -238,15 +267,17 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
IFS="|" read -r CITY_ID CITY_NAME < /tmp/cities.txt
|
||||
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
|
||||
else
|
||||
i=1; CITY_MAP=()
|
||||
i=1; CITY_MAP=(); CITY_NAME_MAP=()
|
||||
while IFS="|" read -r c_id c_name; do
|
||||
echo " $i) $c_name"
|
||||
CITY_MAP[$i]="$c_id"
|
||||
CITY_NAME_MAP[$i]="$c_name"
|
||||
((i++))
|
||||
done < /tmp/cities.txt
|
||||
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
|
||||
CI_SEL=${CI_SEL:-1}
|
||||
CITY_ID="${CITY_MAP[$CI_SEL]}"
|
||||
CITY_NAME="${CITY_NAME_MAP[$CI_SEL]}"
|
||||
fi
|
||||
|
||||
# 清理临时文件 (增加清理 continents.txt)
|
||||
@@ -279,10 +310,17 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
if [ "$MASTER_TYPE" == "2" ]; then
|
||||
TG_TOKEN="OFFICIAL_GATEWAY_MODE"
|
||||
TG_API_URL="https://omni-gateway.samanthaestime296.workers.dev"
|
||||
ENABLE_OTA="false"
|
||||
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
|
||||
echo -e "\033[33m👉 请确保您已在 TG 中关注官方机器人并发送过 /start,否则将无法接收消息。\033[0m"
|
||||
# [v3.6.0 安全熔断]
|
||||
echo -e "\n\033[33m⚠️ 【安全熔断提示】\033[0m"
|
||||
echo -e "\033[33m由于您使用了官方公共网关,为防止潜在的滥用或供应链风险,本节点的 [OTA 远程升级] 权限已被系统底层强制禁用。\033[0m"
|
||||
echo -e "\033[33m💡 若未来需要启用 OTA,请自建私有中枢后重新部署本节点。\033[0m"
|
||||
else
|
||||
echo -e "\n\033[36m📘 私有 Bot 创建教程: https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather/\033[0m"
|
||||
# [v3.6.0 优化] 使用 OSC 8 终端超链接协议,实现“点击即打开”的极客交互
|
||||
echo -e "\n\033[36m📘 私有 Bot 创建教程: \033[4m\033]8;;https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather/\033\\👉 [点击此处直接在浏览器中打开] 👈\033]8;;\033\\\033[0m"
|
||||
echo -e "\033[90m (若您的终端较老不支持点击,请手动复制: https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather/ )\033[0m"
|
||||
read -p "请输入您的私有 Telegram Bot Token: " USER_TOKEN
|
||||
|
||||
# 🛡️ 核心防误触修复:拦截空回车或粘贴换行导致的跳过 Bug
|
||||
@@ -293,10 +331,23 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
TG_TOKEN="$USER_TOKEN"
|
||||
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
|
||||
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
|
||||
|
||||
# [v3.6.0] 私有模式开放 OTA 授权向导
|
||||
echo -e "\n\033[36m[4.1/7] OTA 远程静默升级授权\033[0m"
|
||||
echo -e "💡 开启后,您可以在 TG 面板一键将本节点热更新至最新版本。"
|
||||
read -p "是否允许本节点接收 OTA 升级指令?(y/n, 默认y): " OTA_CHOICE
|
||||
if [[ "$OTA_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
ENABLE_OTA="false"
|
||||
echo -e "🛡️ \033[33m已关闭 OTA 权限,本节点未来将只能通过 SSH 手动升级。\033[0m"
|
||||
else
|
||||
ENABLE_OTA="true"
|
||||
echo -e "✅ \033[32m已开启 OTA 权限,核按钮已挂载至您的私有中枢。\033[0m"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\n\033[33m💡 提示:如果您不知道下方自己的 Chat ID 是什么,可以关注 @userinfobot 获取。\033[0m"
|
||||
echo -e "\033[36m📘 查看图文教程: https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot/\033[0m"
|
||||
echo -e "\033[36m📘 查看图文教程: \033[4m\033]8;;https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot/\033\\👉 [点击此处直接在浏览器中打开] 👈\033]8;;\033\\\033[0m"
|
||||
echo -e "\033[90m (若您的终端较老不支持点击,请手动复制: https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot/ )\033[0m"
|
||||
read -p "请输入你的 Chat ID (必须准确,否则无法联控): " CHAT_ID
|
||||
|
||||
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
|
||||
@@ -483,6 +534,9 @@ BIND_IP="$BIND_IP"
|
||||
# [v3.5.2新增: 双轨身份系统]
|
||||
NODE_NAME="$NODE_NAME"
|
||||
NODE_ALIAS="$NODE_ALIAS"
|
||||
|
||||
# [v3.6.0新增: OTA 权限标识]
|
||||
ENABLE_OTA="$ENABLE_OTA"
|
||||
EOF
|
||||
|
||||
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
|
||||
@@ -544,6 +598,14 @@ if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "NODE_ALIAS=\"$NODE_ALIAS\"" >> "$CONFIG_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# [v3.6.0 热修复] 兼容老版本没有 ENABLE_OTA 的情况,无损补齐默认关闭以防滥用
|
||||
if ! grep -q "^ENABLE_OTA=" "$CONFIG_FILE"; then
|
||||
echo "ENABLE_OTA=\"false\"" >> "$CONFIG_FILE"
|
||||
ENABLE_OTA="false"
|
||||
else
|
||||
ENABLE_OTA=$(grep "^ENABLE_OTA=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||
fi
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
@@ -580,43 +642,145 @@ fi
|
||||
chmod +x ${INSTALL_DIR}/core/*.sh
|
||||
|
||||
# 7. 配置系统定时任务 (高频调度与看门狗)
|
||||
echo -e "\n[7/7] 正在注入系统定时任务与看门狗进程..."
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup || true
|
||||
|
||||
# 核心养护模块: 每 30 分钟触发一次
|
||||
echo "*/30 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
# 养料更新模块: (v3.3.0升级) 每天凌晨 3 点触发,由中枢自动进行分频调度
|
||||
echo "0 3 * * * ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
echo -e "\n[7/7] 正在注入系统守护进程与调度器..."
|
||||
|
||||
# [v3.3.0 新增] 初始化 UA 指纹库更新时间戳,确立 30 天滚动周期的计算锚点
|
||||
echo $(date +%s) > "${INSTALL_DIR}/core/.ua_last_update"
|
||||
|
||||
# 如果配置了联控,启动 Webhook 与战报任务
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
# 每天早上 8 点发送昨天的统计战报
|
||||
echo "0 8 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在部署原生守护服务..."
|
||||
|
||||
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP,直接使用我们上方锁定的 BIND_IP]
|
||||
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
|
||||
# [修复竞态]: 提前写入公网 IP 缓存,彻底阻断 agent_daemon 首次启动时的抢跑推送
|
||||
echo "$SAFE_PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
# 双保险守护进程看门狗
|
||||
echo "@reboot nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
echo "* * * * * nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
|
||||
# 安装时立刻启动一次边缘守护进程
|
||||
nohup bash "${INSTALL_DIR}/core/agent_daemon.sh" >/dev/null 2>&1 &
|
||||
fi
|
||||
# 1. Runner 核心养护模块服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-runner.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Runner Service
|
||||
After=network.target
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash ${INSTALL_DIR}/core/runner.sh
|
||||
User=root
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
EOF
|
||||
|
||||
[ -f /tmp/cron_backup ] && crontab /tmp/cron_backup 2>/dev/null
|
||||
rm -f /tmp/cron_backup
|
||||
cat > /etc/systemd/system/ip-sentinel-runner.timer << EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Runner Service
|
||||
[Timer]
|
||||
OnBootSec=10
|
||||
OnUnitActiveSec=30min
|
||||
RandomizedDelaySec=180
|
||||
Persistent=true
|
||||
Unit=ip-sentinel-runner.service
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
# 2. Updater 养料更新模块服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-updater.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Updater Service
|
||||
After=network.target
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash ${INSTALL_DIR}/core/updater.sh
|
||||
User=root
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
EOF
|
||||
|
||||
cat > /etc/systemd/system/ip-sentinel-updater.timer << EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Updater Service
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 03:00:00
|
||||
Persistent=true
|
||||
Unit=ip-sentinel-updater.service
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ip-sentinel-runner.timer ip-sentinel-updater.timer
|
||||
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
# 3. TG 战报服务与定时器
|
||||
cat > /etc/systemd/system/ip-sentinel-report.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Telegram Report Service
|
||||
After=network.target
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash ${INSTALL_DIR}/core/tg_report.sh
|
||||
User=root
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
EOF
|
||||
|
||||
cat > /etc/systemd/system/ip-sentinel-report.timer << EOF
|
||||
[Unit]
|
||||
Description=Timer for IP-Sentinel Telegram Report Service
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 08:00:00
|
||||
Unit=ip-sentinel-report.service
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
# 4. [排雷修复] Agent Daemon Webhook 监听守护服务 (Type=simple, 常驻执行)
|
||||
cat > /etc/systemd/system/ip-sentinel-agent-daemon.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Agent Daemon Service
|
||||
After=network.target
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=simple
|
||||
ExecStart=/bin/bash ${INSTALL_DIR}/core/agent_daemon.sh
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
User=root
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# [修复竞态]: 提前写入公网 IP 缓存,阻断重复推送
|
||||
echo "$SAFE_PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ip-sentinel-report.timer
|
||||
systemctl enable --now ip-sentinel-agent-daemon.service
|
||||
fi
|
||||
else
|
||||
echo "💡 未检测到 Systemd (可能是 Alpine Linux),回退到 Cron 调度模式..."
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup || true
|
||||
echo "*/30 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
echo "0 3 * * * ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
echo "0 8 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||
echo "$SAFE_PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||
echo "@reboot nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
echo "* * * * * nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||
nohup bash "${INSTALL_DIR}/core/agent_daemon.sh" >/dev/null 2>&1 &
|
||||
fi
|
||||
[ -f /tmp/cron_backup ] && crontab /tmp/cron_backup 2>/dev/null
|
||||
rm -f /tmp/cron_backup
|
||||
fi
|
||||
|
||||
# ================== [v3.4.0 核心: 状态机驱动的热更新路由] ==================
|
||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||
|
||||
# [v3.5.2 核心] 发送携带双轨身份的注册指令 (追加 NODE_ALIAS 作为第 6 个字段)
|
||||
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}|${NODE_ALIAS}"
|
||||
# [v3.6.0 核心] 发送携带全套身份属性的注册指令 (追加 ENABLE_OTA 作为第 7 个字段)
|
||||
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}|${NODE_ALIAS}|${ENABLE_OTA}"
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
# 读取本地老版本号,如果没有则视为远古版本 v3.3.1
|
||||
@@ -727,5 +891,5 @@ if [ -n "$AGENT_COUNT" ] && [[ "$AGENT_COUNT" =~ ^[0-9]+$ ]]; then
|
||||
echo -e "\033[32m✅ 感谢您成为全球第 ${AGENT_COUNT} 名 IP-Sentinel 哨兵!\033[0m"
|
||||
else
|
||||
echo -e "\033[32m✅ 感谢您加入 IP-Sentinel 哨兵阵列!\033[0m"
|
||||
fi
|
||||
echo -e "\n"
|
||||
fi
|
||||
echo -e "\n"
|
||||
|
||||
@@ -97,14 +97,21 @@ CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "$MODULE_NAME" "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为私库地址用于测试
|
||||
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 基础环境校验
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
@@ -99,14 +99,21 @@ CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log_msg "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -31,11 +31,17 @@ CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -86,6 +92,7 @@ case "$REGION_CODE" in
|
||||
"SG") FLAG="🇸🇬" ;;
|
||||
"HK") FLAG="🇭🇰" ;;
|
||||
"GB"|"UK") FLAG="🇬🇧" ;;
|
||||
"AU") FLAG="🇦🇺" ;;
|
||||
*) FLAG="🌐" ;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 - 动态锚点版)
|
||||
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
|
||||
echo "========================================================"
|
||||
@@ -18,13 +26,31 @@ if [ -f "$CONFIG_FILE" ]; then
|
||||
fi
|
||||
echo "========================================================"
|
||||
|
||||
# 1. 停止运行中的守护进程与主控模块 (涵盖所有历史版本进程)
|
||||
echo "[1/3] 正在终止后台守护进程与所有养护任务..."
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
systemctl disable --now ip-sentinel-runner.service ip-sentinel-runner.timer \
|
||||
ip-sentinel-updater.service ip-sentinel-updater.timer \
|
||||
ip-sentinel-report.service ip-sentinel-report.timer \
|
||||
ip-sentinel-agent-daemon.service >/dev/null 2>&1
|
||||
rm -f /etc/systemd/system/ip-sentinel-runner.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-runner.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-updater.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-updater.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-report.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-report.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-agent-daemon.service
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed
|
||||
else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 使用 pkill 替代传统的 pgrep | xargs,指令更短、容错率更高
|
||||
# 2. 停止运行中的守护进程与主控模块 (兜底清理老版进程)
|
||||
echo "[2/4] 正在终止后台守护进程与所有养护任务..."
|
||||
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
|
||||
# [v3.4.0 优化] 确保清理所有 python3 调起的 Webhook 实例
|
||||
pkill -9 -f "python3.*webhook.py" >/dev/null 2>&1
|
||||
pkill -9 -f "webhook.py" >/dev/null 2>&1
|
||||
pkill -9 -f "runner.sh" >/dev/null 2>&1
|
||||
@@ -33,16 +59,16 @@ pkill -9 -f "tg_report.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_google.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_trust.sh" >/dev/null 2>&1
|
||||
|
||||
# 2. 清除系统定时任务 (Cron)
|
||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||
# 3. 清除系统定时任务 (Cron)
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
if crontab -l >/dev/null 2>&1; then
|
||||
crontab -l | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
fi
|
||||
|
||||
# 3. 删除所有文件、日志与临时缓存
|
||||
echo "[3/3] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||
# 4. 删除所有文件、日志与临时缓存
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
@@ -11,6 +11,8 @@ UA_TIME_FILE="${INSTALL_DIR}/core/.ua_last_update"
|
||||
|
||||
# GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 加载本地冷数据配置
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
@@ -40,7 +42,12 @@ CURL_CMD="curl -${IP_PREF:-4} -sL"
|
||||
if [ -n "$BIND_IP" ]; then
|
||||
# curl 的 --interface 参数不支持带方括号的 IPv6 地址,必须强行脱壳
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
CURL_CMD="$CURL_CMD --interface $RAW_BIND_IP"
|
||||
# [v3.6.3 容错层补丁] 探测网卡存活状态,防止 IP 漂移导致永久断网
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "Updater" "WARN " "检测到绑定的出口 IP ($RAW_BIND_IP) 已丢失,自动退回默认路由!"
|
||||
else
|
||||
CURL_CMD="$CURL_CMD --interface $RAW_BIND_IP"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
|
||||
169
data/keywords/kw_AU.txt
Normal file
169
data/keywords/kw_AU.txt
Normal file
@@ -0,0 +1,169 @@
|
||||
crystal palace vs west ham
|
||||
d4vd
|
||||
danish malewar
|
||||
mi vs gt
|
||||
perth weather
|
||||
elijah hollands carlton football club
|
||||
fair work commission fuel
|
||||
vanguard
|
||||
sydney sweeney
|
||||
cailee spaeny
|
||||
psg vs lyon
|
||||
sporting vs benfica
|
||||
celtics vs 76ers
|
||||
psl
|
||||
bundesliga
|
||||
pl
|
||||
afc cup
|
||||
mars rover
|
||||
celtic fc
|
||||
bayern vs vfb stuttgart
|
||||
bom radar
|
||||
sydney weather
|
||||
melbourne weather
|
||||
brisbane weather
|
||||
adelaide weather
|
||||
myGov login
|
||||
news.com.au
|
||||
abc news
|
||||
nrl ladder
|
||||
afl scores
|
||||
afl fixture
|
||||
matildas
|
||||
sam kerr
|
||||
bunnings opening hours
|
||||
coles catalogue
|
||||
woolies specials
|
||||
qantas
|
||||
virgin australia
|
||||
jetstar flights
|
||||
commbank
|
||||
asx 200
|
||||
sydney morning herald
|
||||
the age
|
||||
ticketek
|
||||
ticketmaster
|
||||
opal card top up
|
||||
myki top up
|
||||
translink timetable
|
||||
transperth journey planner
|
||||
adelaide metro
|
||||
adelaide 500
|
||||
adelaide crows
|
||||
port adelaide
|
||||
penrith panthers
|
||||
brisbane broncos
|
||||
collingwood fc
|
||||
sydney swans
|
||||
west coast eagles
|
||||
fremantle dockers
|
||||
canberra raiders
|
||||
mcg events
|
||||
optus stadium
|
||||
the gabba
|
||||
state of origin
|
||||
australian open
|
||||
melbourne cup
|
||||
masterchef australia
|
||||
mafs australia
|
||||
petrol prices near me
|
||||
australia post tracking
|
||||
service nsw login
|
||||
vicroads
|
||||
queensland health
|
||||
medicare
|
||||
ato
|
||||
jb hi-fi
|
||||
kmart
|
||||
amazon.com.au
|
||||
vivid sydney
|
||||
mona hobart
|
||||
dark mofo
|
||||
floriade canberra
|
||||
rottnest island ferry
|
||||
sydney airport arrivals
|
||||
rba interest rate
|
||||
nsw school holidays
|
||||
qld school holidays
|
||||
wa school holidays
|
||||
m4 traffic updates
|
||||
west gate tunnel updates
|
||||
bruce highway traffic
|
||||
taylor swift
|
||||
netflix
|
||||
gemini
|
||||
chatgpt
|
||||
margot robbie
|
||||
ange postecoglou
|
||||
oscar piastri
|
||||
formula 1
|
||||
supercars championship
|
||||
ufc
|
||||
optus sport
|
||||
telstra outage
|
||||
kayo sports
|
||||
domain real estate
|
||||
realestate.com.au
|
||||
seek jobs
|
||||
gumtree
|
||||
westpac login
|
||||
anz internet banking
|
||||
nab login
|
||||
aldi catalogue
|
||||
big w
|
||||
target australia
|
||||
the good guys
|
||||
officeworks
|
||||
dan murphys
|
||||
bws
|
||||
chemist warehouse
|
||||
priceline
|
||||
uber eats
|
||||
menulog
|
||||
dominos
|
||||
centrelink login
|
||||
medicare online
|
||||
passport renewal
|
||||
aec enrolment
|
||||
agl energy
|
||||
origin energy
|
||||
stan
|
||||
binge
|
||||
9now
|
||||
7plus
|
||||
abc iview
|
||||
hoyts cinemas
|
||||
event cinemas
|
||||
big bash league
|
||||
the ashes
|
||||
socceroos
|
||||
bathurst 1000
|
||||
booking.com
|
||||
airbnb
|
||||
webjet
|
||||
bali weather
|
||||
smartraveller
|
||||
lotto results
|
||||
powerball results
|
||||
oz lotto
|
||||
public holidays 2026
|
||||
mothers day 2026
|
||||
carsales
|
||||
redbook
|
||||
nrma
|
||||
racv
|
||||
racq
|
||||
petrol spy
|
||||
optus login
|
||||
vodafone
|
||||
kogan
|
||||
catch.com.au
|
||||
mecca
|
||||
sydney fish market
|
||||
queen victoria market
|
||||
taronga zoo
|
||||
dreamworld
|
||||
movie world
|
||||
wotif
|
||||
anytime fitness
|
||||
time in london
|
||||
@@ -1,5 +1,44 @@
|
||||
syria
|
||||
will trent
|
||||
ibm
|
||||
lecce vs fiorentina
|
||||
celeste rivas hernandez
|
||||
grand theft auto vi
|
||||
abhishek bachchan
|
||||
national guard of the united states
|
||||
triathlon
|
||||
vincent trocheck
|
||||
benyamin netanyahou
|
||||
kings vs avalanche
|
||||
crème solaire
|
||||
trabzonspor vs istanbul başakşehir
|
||||
indigenous rights
|
||||
meghan, duchess of sussex
|
||||
gwyneth paltrow
|
||||
toronto gas prices
|
||||
président
|
||||
mets vs cubs
|
||||
matt fitzpatrick
|
||||
sénateurs – hurricanes
|
||||
senators vs hurricanes
|
||||
man utd
|
||||
spurs
|
||||
evan mobley
|
||||
chelsea vs man united
|
||||
atlético madrid vs real sociedad
|
||||
roma vs atalanta
|
||||
cf montreal
|
||||
jakob poeltl
|
||||
ukraine
|
||||
contrôle routier québec
|
||||
bachelorette 2026
|
||||
lens – toulouse
|
||||
arber xhekaj
|
||||
leylah fernandez
|
||||
anthropic
|
||||
paige wwe
|
||||
inter – cagliari
|
||||
carrie ann inaba
|
||||
syria
|
||||
highway hotline sk
|
||||
real betis vs braga
|
||||
vincent bolloré
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
heppenheim
|
||||
ministerpräsident
|
||||
robbie williams
|
||||
brian littrell
|
||||
saarbrücken hauptbahnhof
|
||||
frauen-bundesliga
|
||||
lemgo
|
||||
kommissar rex
|
||||
hemsbach
|
||||
benjamin weber
|
||||
sydney sweeney
|
||||
martin schindler
|
||||
robert kennedy
|
||||
antoni kowalski
|
||||
luna
|
||||
paris-sg – lyon
|
||||
arte live
|
||||
dominik kohr
|
||||
dumbledores geheimnisse
|
||||
kampf der realitystars
|
||||
kerner
|
||||
tennessee
|
||||
billy idol
|
||||
kfc uerdingen
|
||||
bastian pastewka
|
||||
gnabry
|
||||
lok leipzig heute live ticker
|
||||
true lies wahre lügen
|
||||
челси – манчестер юнайтед
|
||||
nurburgring
|
||||
fortnite server status
|
||||
süperlig
|
||||
ronaldinho
|
||||
esther schweins let's dance
|
||||
michael jackson film 2026
|
||||
oliver pocher
|
||||
die queen film
|
||||
milano
|
||||
straße von hormus
|
||||
motsi mabuse
|
||||
rayo vallecano
|
||||
harry meghan
|
||||
irland
|
||||
@@ -45,7 +85,6 @@ jeff bezos
|
||||
dan brown
|
||||
паспорт громадянина україни для виїзду за кордон
|
||||
serena williams
|
||||
kampf der realitystars
|
||||
манчестер юнайтед – лидс
|
||||
catherine deneuve
|
||||
bobzin
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
moreirense - estoril
|
||||
downton abbey
|
||||
koldo garcía izaguirre
|
||||
jorge martín
|
||||
mike james
|
||||
carla leite
|
||||
eclipse solar del 12 de agosto de 2026
|
||||
crystal palace - west ham
|
||||
luis merlo
|
||||
luz valdenebro
|
||||
porto - tondela
|
||||
santos - fluminense
|
||||
juventus
|
||||
almería - málaga
|
||||
punjab kings vs lucknow super giants standings
|
||||
mönchengladbach – mainz
|
||||
victor eloy
|
||||
paris-sg – lyon
|
||||
psg vs lyon
|
||||
zamora - osasuna b
|
||||
estrecho
|
||||
nurburgring
|
||||
la 1 directo
|
||||
oyarzabal
|
||||
enrique cerezo
|
||||
fraude
|
||||
tasa
|
||||
la 1
|
||||
tve directo
|
||||
cuántas copas del rey tiene la real sociedad
|
||||
eugenia martínez de irujo
|
||||
ccoo
|
||||
racing de santander
|
||||
racing
|
||||
baliza v16
|
||||
st. pauli – köln
|
||||
iphone 18
|
||||
st. pauli - colonia
|
||||
nico paz
|
||||
lionel messi
|
||||
armengol
|
||||
pau víctor
|
||||
braga fc
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
madonna age
|
||||
margot haddad
|
||||
ana riera
|
||||
hinaupoko devèze
|
||||
bruce toussaint
|
||||
cheque energie
|
||||
reid wiseman
|
||||
mma
|
||||
loto 20 avril 2026
|
||||
from serie
|
||||
toulouse
|
||||
racing 92 – stade français
|
||||
juventus - bologna
|
||||
film une annee difficile
|
||||
échouement
|
||||
programme tv ce soir
|
||||
porto – tondela
|
||||
matthieu pigasse
|
||||
santos – fluminense
|
||||
gta 6
|
||||
laetitia milot
|
||||
loto 18 avril 2026
|
||||
bercy
|
||||
pierre lellouche
|
||||
adele
|
||||
adil rami
|
||||
castres – toulouse
|
||||
angel
|
||||
stéphane bern
|
||||
anne claire coudray
|
||||
pmu résultat
|
||||
laury thilleman et paul mirabel
|
||||
quinté du jour
|
||||
euromillions 17 avril 2026
|
||||
alain bauer
|
||||
uson
|
||||
guillaume meurice
|
||||
pmu
|
||||
grenoble – oyonnax
|
||||
bagarre
|
||||
programme télé
|
||||
tchernobyl
|
||||
géraldine maillet
|
||||
|
||||
@@ -1,3 +1,41 @@
|
||||
水晶宮 對 西漢姆聯
|
||||
吉達艾阿里
|
||||
山口智子
|
||||
百佳超級市場
|
||||
利息
|
||||
戴祖儀
|
||||
陳若思
|
||||
麥當勞
|
||||
首岸
|
||||
中国商飞c919
|
||||
pl
|
||||
bundesliga
|
||||
hailey bieber
|
||||
德甲
|
||||
cherki
|
||||
now
|
||||
曼城
|
||||
now tv
|
||||
al nassr
|
||||
洪金寶
|
||||
曼聯
|
||||
車路士
|
||||
切爾西 對 曼聯
|
||||
英超
|
||||
何沛珈
|
||||
熱刺
|
||||
tottenham vs brighton
|
||||
熱刺 對 布萊頓
|
||||
epl
|
||||
司機
|
||||
补贴
|
||||
華富邨
|
||||
江美儀
|
||||
零售
|
||||
藍莓
|
||||
商湯科技
|
||||
周國豐
|
||||
啟點
|
||||
歐聯
|
||||
神戶勝利船
|
||||
潘宏彬
|
||||
@@ -5,7 +43,6 @@
|
||||
木乃伊
|
||||
ios 26
|
||||
李克寧木乃伊
|
||||
江美儀
|
||||
田啟文
|
||||
曼寧加
|
||||
arsenal
|
||||
|
||||
@@ -1,10 +1,49 @@
|
||||
町田ゼルビア
|
||||
モンスターハンターシリーズ
|
||||
クリスタル・パレス 対 ウェストハム
|
||||
吉田正尚
|
||||
旭琉會
|
||||
神戸
|
||||
kick
|
||||
てんちむ
|
||||
浜辺美波
|
||||
デーブ ロバーツ
|
||||
皇治
|
||||
小雪
|
||||
にじさんじデビュー
|
||||
ちゃんよた
|
||||
坂本 花織
|
||||
松山 千春
|
||||
dior
|
||||
リーグアン
|
||||
伊勢谷友介
|
||||
bayern vs vfb stuttgart
|
||||
chelsea vs man united
|
||||
唐田えりか
|
||||
102回目のプロポーズ
|
||||
デゼルビ
|
||||
アトレティコ 対 ソシエダ
|
||||
チェルシー 対 マンu
|
||||
スパーズ
|
||||
清春
|
||||
ディエゴ・ゴメス
|
||||
ps5
|
||||
ハイウェイ の 堕 天使 興行 収入
|
||||
カブス 対 メッツ
|
||||
dazn
|
||||
サッスオーロ 対 コモ
|
||||
杉咲花
|
||||
町田 対 アル・イテハド
|
||||
家計
|
||||
週末旅の極意
|
||||
北斗の拳
|
||||
qvc
|
||||
小芝風花
|
||||
中井亜美
|
||||
afc u20女子アジアカップ
|
||||
ネオジオ
|
||||
uefaヨーロッパリーグ
|
||||
加藤史帆
|
||||
町田ゼルビア
|
||||
志田未来
|
||||
伊藤英明
|
||||
島田麻央
|
||||
|
||||
47
data/keywords/kw_KR.txt
Normal file
47
data/keywords/kw_KR.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
크리스털 팰리스 대 웨스트 햄
|
||||
비상계엄
|
||||
연기금
|
||||
배틀그라운드
|
||||
제이홉
|
||||
두산로보틱스
|
||||
부부
|
||||
미노이 우원재
|
||||
danish malewar
|
||||
mi vs gt
|
||||
양치 승
|
||||
프리미어리그
|
||||
pl
|
||||
bundesliga
|
||||
맨체스터 시티 fc
|
||||
fotmob
|
||||
윤아
|
||||
김영인
|
||||
манчестер сити – арсенал
|
||||
케이뱅크
|
||||
날씨
|
||||
환율
|
||||
삼성전자 주가
|
||||
손흥민
|
||||
토트넘 경기 일정
|
||||
넷플릭스
|
||||
국민은행
|
||||
네이버웹툰
|
||||
로또 당첨번호
|
||||
쿠팡
|
||||
KBO 리그
|
||||
프로야구 순위
|
||||
아이폰 15
|
||||
챗gpt
|
||||
인스타그램
|
||||
유튜브 밴스드
|
||||
이강인
|
||||
김민재
|
||||
LCK 일정
|
||||
T1
|
||||
페이커
|
||||
무빙
|
||||
카카오톡 PC버전
|
||||
당근마켓
|
||||
배달의민족
|
||||
올리브영
|
||||
메이플스토리
|
||||
@@ -1,3 +1,43 @@
|
||||
lecce - fiorentina
|
||||
pogoda jutro
|
||||
vierhouten
|
||||
weather tomorrow
|
||||
over mijn lijk 2026 overleden
|
||||
crystal palace - west ham
|
||||
f-16 fighting falcon
|
||||
klagenfurt
|
||||
italië
|
||||
ruud gullit
|
||||
perfil falso
|
||||
psg - lyon
|
||||
juventus - bologna
|
||||
troy parrott
|
||||
az - nec opstellingen
|
||||
caroline tensen
|
||||
asielbeleid
|
||||
philippe sandler
|
||||
keerbergen
|
||||
peer koopmeiners
|
||||
nederlands elftal
|
||||
pogoń szczecin – lech poznań
|
||||
stand premier league
|
||||
bulgarije
|
||||
chelsea - man utd
|
||||
atlético madrid - real sociedad
|
||||
xavi simons
|
||||
ayase ueda
|
||||
roma - atalanta
|
||||
rtv noord
|
||||
paraguay
|
||||
sergio herman
|
||||
fed
|
||||
keuken kampioen
|
||||
legia warszawa – zagłębie lubin
|
||||
ripple
|
||||
voorzitter fed
|
||||
bahamas
|
||||
overtreding
|
||||
almere city
|
||||
real betis
|
||||
sean connery
|
||||
l1 nieuws
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
crystal palace vs west ham
|
||||
grok
|
||||
gemini
|
||||
claude ai
|
||||
gpt
|
||||
ai
|
||||
is chatgpt down
|
||||
strait of hormuz news
|
||||
danish malewar
|
||||
mi vs gt
|
||||
psg vs lyon
|
||||
sporting vs benfica
|
||||
bayern munich
|
||||
pl
|
||||
bangkok weather
|
||||
starhub
|
||||
廖子妤
|
||||
曼城 - 阿森纳
|
||||
bayern vs vfb stuttgart
|
||||
英超
|
||||
chelsea vs man united
|
||||
perfect crown
|
||||
ayush mhatre
|
||||
sarfaraz khan
|
||||
napoli vs lazio
|
||||
tottenham vs brighton
|
||||
abhishek sharma
|
||||
tinie tempah
|
||||
wrexham vs stoke city
|
||||
sassuolo vs como
|
||||
the straits times
|
||||
india women vs south africa women
|
||||
beef season 2
|
||||
loyang valley
|
||||
world cup 2026
|
||||
afc champions league
|
||||
hormuz
|
||||
malacca strait
|
||||
kkr vs gt
|
||||
aston villa vs bologna
|
||||
mumbai indians vs punjab kings standings
|
||||
al sadd vs vissel kobe
|
||||
|
||||
@@ -1,3 +1,41 @@
|
||||
洪敬堯
|
||||
華邦電子
|
||||
柔美的細胞小將 3
|
||||
底特律老虎
|
||||
三 商 美邦
|
||||
川 普
|
||||
英雄聯盟
|
||||
聯電adr
|
||||
rklb
|
||||
amd stock
|
||||
宋仲基
|
||||
兵工廠
|
||||
德甲
|
||||
塞爾提克 對 76人
|
||||
justin bieber 演唱會
|
||||
拜仁慕尼黑
|
||||
何潤東
|
||||
廖子妤
|
||||
英超
|
||||
許凱
|
||||
tottenham vs brighton
|
||||
長野縣
|
||||
交通安全
|
||||
朋友收集夢想生活
|
||||
麥克傑克森
|
||||
王濛
|
||||
騎士 對 暴龍
|
||||
熱刺 對 布萊頓
|
||||
iem rio 2026
|
||||
garret anderson
|
||||
墓乃伊
|
||||
曹格
|
||||
claude design
|
||||
柯文哲
|
||||
金剛
|
||||
荷 姆 茲 海峽
|
||||
東北 季風
|
||||
斯圖加特公開賽
|
||||
歐聯
|
||||
菡生婦幼診所
|
||||
台鐵訂票
|
||||
@@ -5,7 +43,6 @@
|
||||
東光路
|
||||
货币
|
||||
amd
|
||||
rklb
|
||||
航空母艦
|
||||
axti
|
||||
Yahoo奇摩
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
oscar isaac
|
||||
xrp ledger
|
||||
believe me itv
|
||||
pablo
|
||||
is tane leaving home and away
|
||||
julie andrews
|
||||
danny boyle
|
||||
nina eastenders
|
||||
john stones
|
||||
amazon vega os fire tv
|
||||
porto vs tondela
|
||||
santos vs fluminense
|
||||
martin brundle lost f1 seat
|
||||
keegan bradley
|
||||
david attenborough
|
||||
antoni kowalski
|
||||
kezia dugdale
|
||||
car
|
||||
beef netflix
|
||||
juventus vs bologna
|
||||
losc vs nice
|
||||
david szalay
|
||||
the killer
|
||||
joe cole
|
||||
lille fc
|
||||
simon cowell
|
||||
pl
|
||||
frank lampard everton
|
||||
nottingham forest fixtures
|
||||
everton manager
|
||||
dragons vs bulls
|
||||
suede
|
||||
lahore
|
||||
wrestlemania 2026
|
||||
giants vs rhinos
|
||||
glenrothan
|
||||
york knights vs leopards
|
||||
tim sherwood
|
||||
redditch
|
||||
ccfc
|
||||
europa conference league
|
||||
bromley fc
|
||||
paul merton
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
arc raiders
|
||||
schd etf dividend yield
|
||||
deportación
|
||||
lecce - fiorentina
|
||||
valley forge high school
|
||||
betty yee
|
||||
seth trimble
|
||||
bill belichick
|
||||
winter storm
|
||||
jim parsons
|
||||
kings vs avalanche
|
||||
suns vs thunder
|
||||
wolf
|
||||
santos - fluminense
|
||||
mets - cubs
|
||||
alexander manninger
|
||||
santos vs fluminense
|
||||
disclosure day
|
||||
tobias myers
|
||||
vladimir putin
|
||||
knicks game
|
||||
ben rice
|
||||
prem
|
||||
timberwolves vs nuggets
|
||||
cody bellinger
|
||||
nik khamenia
|
||||
real sociedad
|
||||
nurburgring crash
|
||||
atlético madrid - real sociedad
|
||||
ruke orhorhoro
|
||||
radar
|
||||
the weather channel
|
||||
kttc
|
||||
luke gulbranson
|
||||
kttc weather
|
||||
comcast data breach settlement
|
||||
tornado watch
|
||||
moisés ballesteros
|
||||
mets game today
|
||||
giancarlo stanton
|
||||
real betis
|
||||
prosecution of daniel duggan
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
lecce vs fiorentina
|
||||
fiorentina
|
||||
crystal palace đấu với west ham
|
||||
afc champions league
|
||||
eduardo camavinga
|
||||
vissel kobe
|
||||
gemi
|
||||
idp
|
||||
bảo hiểm y tế
|
||||
crystal palace vs west ham
|
||||
sporting lisbon
|
||||
psg vs lyon
|
||||
ca sĩ
|
||||
juventus đấu với bologna
|
||||
gladbach đấu với mainz
|
||||
bayern munich
|
||||
twitch
|
||||
psg đấu với lyon
|
||||
juventus
|
||||
ligue 1
|
||||
trận đấu ngoại hạng anh
|
||||
chelsea đấu với man utd
|
||||
atlético madrid đấu với real sociedad
|
||||
roma đấu với atalanta
|
||||
epl
|
||||
iem rio 2026
|
||||
tot
|
||||
tập đoàn gelex
|
||||
napoli
|
||||
đường ray
|
||||
inter
|
||||
inter đấu với cagliari
|
||||
sassuolo vs como
|
||||
david alaba
|
||||
claude design
|
||||
fenerbahçe đấu với rizespor
|
||||
como
|
||||
como vs
|
||||
thẻ đỏ
|
||||
al ahli
|
||||
porto vs
|
||||
crystal palace
|
||||
porto
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "3.5.2",
|
||||
"updated_at": "2026-04-16",
|
||||
"updated_at": "2026-04-19",
|
||||
"continents": [
|
||||
{
|
||||
"id": "ASIA",
|
||||
@@ -22,6 +22,14 @@
|
||||
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Singapore", "name": "Singapore (新加坡)" } ] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KR",
|
||||
"name": "South Korea (韩国)",
|
||||
"keyword_file": "kw_KR.txt",
|
||||
"states": [
|
||||
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Seoul", "name": "Seoul (首尔)" } ] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "HK",
|
||||
"name": "Hong Kong (香港)",
|
||||
@@ -178,6 +186,54 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "OCEANIA",
|
||||
"name": "大洋洲战区 (Oceania)",
|
||||
"countries": [
|
||||
{
|
||||
"id": "AU",
|
||||
"name": "Australia (澳大利亚)",
|
||||
"keyword_file": "kw_AU.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "NSW",
|
||||
"name": "New South Wales (新南威尔士州)",
|
||||
"cities": [
|
||||
{ "id": "Sydney", "name": "Sydney (悉尼)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "VIC",
|
||||
"name": "Victoria (维多利亚州)",
|
||||
"cities": [
|
||||
{ "id": "Melbourne", "name": "Melbourne (墨尔本)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "QLD",
|
||||
"name": "Queensland (昆士兰州)",
|
||||
"cities": [
|
||||
{ "id": "Brisbane", "name": "Brisbane (布里斯班)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "WA",
|
||||
"name": "Western Australia (西澳大利亚州)",
|
||||
"cities": [
|
||||
{ "id": "Perth", "name": "Perth (珀斯)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "SA",
|
||||
"name": "South Australia (南澳大利亚州)",
|
||||
"cities": [
|
||||
{ "id": "Adelaide", "name": "Adelaide (阿德莱德)" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
35
data/regions/AU/NSW/Sydney.json
Normal file
35
data/regions/AU/NSW/Sydney.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"region_name": "Australia - Sydney",
|
||||
"google_module": {
|
||||
"base_lat": -33.8697,
|
||||
"base_lon": 151.2085,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://www.smh.com.au/",
|
||||
"https://www.dailytelegraph.com.au/",
|
||||
"https://www.service.nsw.gov.au/",
|
||||
"https://transportnsw.info/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.abc.net.au/",
|
||||
"https://www.news.com.au/",
|
||||
"https://www.nine.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.westpac.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.domain.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.nrl.com/",
|
||||
"https://premier.ticketek.com.au/",
|
||||
"https://www.amazon.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
36
data/regions/AU/QLD/Brisbane.json
Normal file
36
data/regions/AU/QLD/Brisbane.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"region_name": "Australia - Brisbane",
|
||||
"google_module": {
|
||||
"base_lat": -27.4697,
|
||||
"base_lon": 153.0252,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://www.couriermail.com.au/",
|
||||
"https://www.brisbanetimes.com.au/",
|
||||
"https://www.qld.gov.au/",
|
||||
"https://translink.com.au/",
|
||||
"https://www.health.qld.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.abc.net.au/",
|
||||
"https://www.news.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.suncorp.com.au/",
|
||||
"https://www.boq.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.domain.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.nrl.com/",
|
||||
"https://premier.ticketek.com.au/",
|
||||
"https://www.amazon.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
35
data/regions/AU/SA/Adelaide.json
Normal file
35
data/regions/AU/SA/Adelaide.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"region_name": "Australia - Adelaide",
|
||||
"google_module": {
|
||||
"base_lat": -34.9227,
|
||||
"base_lon": 138.6016,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://www.adelaidenow.com.au/",
|
||||
"https://indaily.com.au/",
|
||||
"https://www.sa.gov.au/",
|
||||
"https://www.adelaidemetro.com.au/",
|
||||
"https://www.sapowernetworks.com.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.abc.net.au/",
|
||||
"https://www.news.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.bendigobank.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.domain.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.afl.com.au/",
|
||||
"https://premier.ticketek.com.au/",
|
||||
"https://www.amazon.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
35
data/regions/AU/VIC/Melbourne.json
Normal file
35
data/regions/AU/VIC/Melbourne.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"region_name": "Australia - Melbourne",
|
||||
"google_module": {
|
||||
"base_lat": -37.8106,
|
||||
"base_lon": 144.9624,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://www.theage.com.au/",
|
||||
"https://www.heraldsun.com.au/",
|
||||
"https://www.vic.gov.au/",
|
||||
"https://www.ptv.vic.gov.au/",
|
||||
"https://www.vicroads.vic.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.kmart.com.au/",
|
||||
"https://www.abc.net.au/",
|
||||
"https://www.news.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.nab.com.au/",
|
||||
"https://www.anz.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.afl.com.au/",
|
||||
"https://www.ticketmaster.com.au/",
|
||||
"https://www.amazon.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
35
data/regions/AU/WA/Perth.json
Normal file
35
data/regions/AU/WA/Perth.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"region_name": "Australia - Perth",
|
||||
"google_module": {
|
||||
"base_lat": -31.9545,
|
||||
"base_lon": 115.8582,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://en.wikipedia.org/wiki/Special:Random",
|
||||
"https://thewest.com.au/",
|
||||
"https://www.perthnow.com.au/",
|
||||
"https://www.wa.gov.au/",
|
||||
"https://www.transperth.wa.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.kmart.com.au/",
|
||||
"https://www.abc.net.au/",
|
||||
"https://www.news.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.bankwest.com.au/",
|
||||
"https://reiwa.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.afl.com.au/",
|
||||
"https://premier.ticketek.com.au/",
|
||||
"https://www.amazon.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
22
data/regions/KR/Default/Seoul.json
Normal file
22
data/regions/KR/Default/Seoul.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"region_name": "South Korea - Seoul",
|
||||
"google_module": {
|
||||
"base_lat": 37.5665,
|
||||
"base_lon": 126.9780,
|
||||
"lang_params": "hl=ko&gl=KR",
|
||||
"valid_url_suffix": "co.kr"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://ko.wikipedia.org/wiki/특수:임의문서",
|
||||
"https://www.naver.com/",
|
||||
"https://www.daum.net/",
|
||||
"https://namu.wiki/",
|
||||
"https://www.tistory.com/",
|
||||
"https://www.coupang.com/",
|
||||
"https://www.chosun.com/",
|
||||
"https://www.yna.co.kr/",
|
||||
"https://www.kakaocorp.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,19 @@
|
||||
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 部署 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为私库地址用于测试
|
||||
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# [核心: 动态提取 Master 专属版本锚点 (KV 解析法)]
|
||||
# 通过 grep 定位 MASTER_VERSION 行,再通过 cut 提取等号右侧的值
|
||||
@@ -25,58 +34,81 @@ echo "========================================================"
|
||||
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢) v${TARGET_VERSION}"
|
||||
echo "========================================================"
|
||||
|
||||
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署 Master 控制中枢"
|
||||
echo " 2) 🗑️ 一键卸载 Master 中枢"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定被误删档
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
echo -e "\n⏳ 正在拉取卸载程序..."
|
||||
# [新增逻辑] 使用上面定义的 REPO_RAW_URL 动态拉取卸载脚本,执行后自动销毁临时文件
|
||||
curl -sL "${REPO_RAW_URL}/master/uninstall_master.sh" -o "/tmp/uninstall_master.sh"
|
||||
chmod +x "/tmp/uninstall_master.sh"
|
||||
bash "/tmp/uninstall_master.sh"
|
||||
rm -f "/tmp/uninstall_master.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_DB="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
echo -e "\n\033[33m💡 司令部雷达提示:检测到本机已部署过 Master 中枢。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史节点数据库 (SQLite)?(y/n, 默认y): " DB_CHOICE
|
||||
if [[ "$DB_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_DB="false"
|
||||
fi
|
||||
|
||||
# 汲取原配置进入内存
|
||||
# ==========================================================
|
||||
# [v3.6.1 核心] 拦截司令部静默 OTA 升级模式 (强行接管执行流)
|
||||
# ==========================================================
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ]; then
|
||||
echo -e "\n⏳ [OTA] 中枢重构指令已确认,正在剥离控制台交互..."
|
||||
ACTION_CHOICE=1
|
||||
UPGRADE_MODE="true"
|
||||
KEEP_DB="true"
|
||||
|
||||
# 汲取原配置进入内存
|
||||
if [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
|
||||
# [v3.4.0 核心] 升级后立即同步/补录版本号至配置文件
|
||||
# 同步新版本号至配置文件
|
||||
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||
else
|
||||
echo "MASTER_VERSION=\"$TARGET_VERSION\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],版本已锚定为 v${TARGET_VERSION}...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的中枢数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
echo -e "\033[32m✅ 已激活 [中枢静默重构模式],即将无损覆写内核...\033[0m"
|
||||
else
|
||||
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署 Master 控制中枢"
|
||||
echo " 2) 🗑️ 一键卸载 Master 中枢"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定被误删档
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
echo -e "\n⏳ 正在拉取卸载程序..."
|
||||
curl -sL "${REPO_RAW_URL}/master/uninstall_master.sh" -o "/tmp/uninstall_master.sh"
|
||||
chmod +x "/tmp/uninstall_master.sh"
|
||||
bash "/tmp/uninstall_master.sh"
|
||||
rm -f "/tmp/uninstall_master.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_DB="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
echo -e "\n\033[33m💡 司令部雷达提示:检测到本机已部署过 Master 中枢。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史节点数据库 (SQLite)?(y/n, 默认y): " DB_CHOICE
|
||||
if [[ "$DB_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_DB="false"
|
||||
fi
|
||||
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
|
||||
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||
else
|
||||
echo "MASTER_VERSION=\"$TARGET_VERSION\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],版本已锚定为 v${TARGET_VERSION}...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的中枢数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# ====================================================================
|
||||
|
||||
# ================== [v3.2.2 优化: 安装前环境纯净度清理与数据保护] ==================
|
||||
echo -e "\n⏳ 正在清理旧版 Master 守护进程..."
|
||||
# [新增] 优雅停止 Systemd 服务,防止代码替换时引发无限复活风暴
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl stop ip-sentinel-master.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
@@ -95,14 +127,67 @@ fi
|
||||
echo -e "\033[32m✅ 旧进程已肃清!\033[0m"
|
||||
# =======================================================================
|
||||
|
||||
# 1. 环境依赖安装
|
||||
echo -e "\n[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
||||
if [ -f /etc/debian_version ]; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
yum install -y curl jq sqlite >/dev/null 2>&1
|
||||
# 1. 依赖检查与智能安装 (v3.6.0 兼容性与优雅性升级)
|
||||
echo -e "\n[1/4] 正在探测核心依赖 (curl, jq, sqlite3, crontab, pgrep)..."
|
||||
|
||||
REQUIRED_CMDS=("curl" "jq" "sqlite3" "crontab" "pgrep")
|
||||
MISSING_CMDS=()
|
||||
|
||||
# 基础探测:预检查缺失的命令
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
MISSING_CMDS+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
# 如果有缺失,才执行包管理器拉取逻辑
|
||||
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
echo "⏳ 发现缺失依赖: ${MISSING_CMDS[*]},正在尝试自动补齐..."
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
# [v3.6.3 抽脂级优化] 注入 --no-install-recommends 拒绝捆绑销售
|
||||
apt-get install -y --no-install-recommends curl jq sqlite3 cron procps >/dev/null 2>&1
|
||||
systemctl enable cron >/dev/null 2>&1 && systemctl start cron >/dev/null 2>&1
|
||||
elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="yum"
|
||||
OPT_ARGS=""
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="dnf"
|
||||
# [v3.6.3 抽脂级优化] 强行关闭 DNF 的弱依赖拉取
|
||||
OPT_ARGS="--setopt=install_weak_deps=False"
|
||||
fi
|
||||
$PKG_MGR install -y $OPT_ARGS curl jq sqlite cronie procps-ng >/dev/null 2>&1
|
||||
systemctl enable crond >/dev/null 2>&1 && systemctl start crond >/dev/null 2>&1
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
echo "Alpine 探测到系统类型为 Alpine Linux,正在执行轻量级安装..."
|
||||
apk add --no-cache curl jq sqlite dcron procps bash >/dev/null 2>&1
|
||||
mkdir -p /var/spool/cron/crontabs
|
||||
rc-update add crond default >/dev/null 2>&1
|
||||
service crond start >/dev/null 2>&1
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
pacman -Sy --noconfirm curl jq sqlite cronie procps-ng >/dev/null 2>&1
|
||||
mkdir -p /root/.cache/crontab 2>/dev/null
|
||||
systemctl enable cronie >/dev/null 2>&1 && systemctl start cronie >/dev/null 2>&1
|
||||
else
|
||||
echo -e "\033[31m❌ 自动安装失败:系统未知的包管理器。\033[0m"
|
||||
echo -e "\033[33m⚠️ 请手动执行以下安装命令后重新运行本脚本:\033[0m"
|
||||
echo -e " Debian/Ubuntu: \033[36mapt-get update && apt-get install -y --no-install-recommends curl jq sqlite3 cron procps\033[0m"
|
||||
echo -e " CentOS/RHEL: \033[36myum install -y curl jq sqlite cronie procps-ng\033[0m"
|
||||
echo -e " Alpine Linux: \033[36mapk add --no-cache curl jq sqlite dcron procps bash\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装后二次复检
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo -e "\033[31m❌ 致命错误:核心命令 '$cmd' 仍未找到!\033[0m"
|
||||
echo -e "请手动修复您的包管理器源,或联系 VPS 供应商。"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "\033[32m✅ 基础环境检测通过。\033[0m"
|
||||
|
||||
mkdir -p "$MASTER_DIR"
|
||||
|
||||
@@ -113,6 +198,32 @@ if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
|
||||
# [v3.6.0 新增] 官方网关模式选项 (用于屏蔽全局 OTA 按钮)
|
||||
echo -e "\n请选择您的部署环境身份:"
|
||||
echo " 1) 🛡️ 私有独立中枢 (默认推荐,保留完整 OTA 遥控权限)"
|
||||
echo " 2) ☁️ 官方公共网关 (面向大众服务,将强制物理隐藏全局 OTA 按钮防滥用)"
|
||||
read -p "请输入选择 [1-2] (默认1): " GATEWAY_TYPE
|
||||
GATEWAY_TYPE=${GATEWAY_TYPE:-1}
|
||||
|
||||
IS_OFFICIAL_GATEWAY="false"
|
||||
ENABLE_MASTER_OTA="false"
|
||||
if [ "$GATEWAY_TYPE" == "2" ]; then
|
||||
IS_OFFICIAL_GATEWAY="true"
|
||||
echo -e "\033[33m⚠️ 已开启官方公共网关模式,全舰队与司令部的 OTA 将被强制屏蔽。\033[0m"
|
||||
else
|
||||
# [v3.6.1] 私有模式开放中枢 OTA 授权向导
|
||||
echo -e "\n[2.1/4] 司令部自我进化授权"
|
||||
echo -e "💡 开启后,您可以在 TG 菜单一键将中枢核心系统热更新至最新版本。"
|
||||
read -p "是否允许司令部接收 OTA 重构指令?(y/n, 默认y): " M_OTA_CHOICE
|
||||
if [[ "$M_OTA_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
ENABLE_MASTER_OTA="false"
|
||||
echo -e "🛡️ \033[33m已关闭司令部 OTA 权限,中枢内核未来仅支持 SSH 升级。\033[0m"
|
||||
else
|
||||
ENABLE_MASTER_OTA="true"
|
||||
echo -e "✅ \033[32m已开启司令部 OTA 权限,金蝉脱壳引信已挂载。\033[0m"
|
||||
fi
|
||||
fi
|
||||
|
||||
cat > "${MASTER_DIR}/master.conf" << EOF
|
||||
# IP-Sentinel Master 本地固化配置 (v${TARGET_VERSION})
|
||||
@@ -120,8 +231,22 @@ MASTER_VERSION="$TARGET_VERSION"
|
||||
TG_TOKEN="$TG_TOKEN"
|
||||
DB_FILE="$DB_FILE"
|
||||
MASTER_DIR="$MASTER_DIR"
|
||||
# [v3.6.0 核心] 官方网关 UI 熔断标识
|
||||
IS_OFFICIAL_GATEWAY="$IS_OFFICIAL_GATEWAY"
|
||||
# [v3.6.1 新增] 司令部自身 OTA 授权标识
|
||||
ENABLE_MASTER_OTA="$ENABLE_MASTER_OTA"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# [v3.6.1 热修复] 老司令部平滑升级时,自动补齐缺失字段
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if ! grep -q "^IS_OFFICIAL_GATEWAY=" "${MASTER_DIR}/master.conf"; then
|
||||
echo "IS_OFFICIAL_GATEWAY=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
if ! grep -q "^ENABLE_MASTER_OTA=" "${MASTER_DIR}/master.conf"; then
|
||||
echo "ENABLE_MASTER_OTA=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
fi
|
||||
# 🛑 拦截块结束
|
||||
|
||||
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下由 tg_master.sh 负责热修补)
|
||||
@@ -137,6 +262,7 @@ CREATE TABLE IF NOT EXISTS nodes (
|
||||
node_alias TEXT,
|
||||
enable_google TEXT DEFAULT 'true',
|
||||
enable_trust TEXT DEFAULT 'true',
|
||||
enable_ota TEXT DEFAULT 'false',
|
||||
PRIMARY KEY(chat_id, node_name)
|
||||
);
|
||||
EOF
|
||||
@@ -153,20 +279,64 @@ echo -e "\n[4/4] 部署 TG 调度守护进程..."
|
||||
curl -sL "${REPO_RAW_URL}/master/tg_master.sh" -o "${MASTER_DIR}/tg_master.sh"
|
||||
chmod +x "${MASTER_DIR}/tg_master.sh"
|
||||
|
||||
# 写入看门狗 Cron
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master
|
||||
echo "* * * * * pgrep -f tg_master.sh >/dev/null || nohup bash ${MASTER_DIR}/tg_master.sh >/dev/null 2>&1 &" >> /tmp/cron_master
|
||||
crontab /tmp/cron_master
|
||||
rm -f /tmp/cron_master
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在部署原生守护服务..."
|
||||
|
||||
cat > /etc/systemd/system/ip-sentinel-master.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Master Command Center Service
|
||||
After=network.target
|
||||
|
||||
# 立刻启动
|
||||
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=simple
|
||||
ExecStart=/bin/bash ${MASTER_DIR}/tg_master.sh
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
User=root
|
||||
WorkingDirectory=${MASTER_DIR}
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
|
||||
# ================== [v3.2.2 优化: 战报文案分流] ==================
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ip-sentinel-master.service
|
||||
systemctl restart ip-sentinel-master.service
|
||||
|
||||
# 清理可能残留的历史 Cron
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master || true
|
||||
[ -f /tmp/cron_master ] && crontab /tmp/cron_master 2>/dev/null
|
||||
rm -f /tmp/cron_master
|
||||
else
|
||||
echo "💡 未检测到 Systemd,回退到 Cron 看门狗调度模式..."
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master || true
|
||||
echo "* * * * * pgrep -f tg_master.sh >/dev/null || nohup bash ${MASTER_DIR}/tg_master.sh >/dev/null 2>&1 &" >> /tmp/cron_master
|
||||
[ -f /tmp/cron_master ] && crontab /tmp/cron_master 2>/dev/null
|
||||
rm -f /tmp/cron_master
|
||||
|
||||
pgrep -f tg_master.sh >/dev/null || { nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 & disown 2>/dev/null; }
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 优化 & v3.6.1 OTA捷报: 战报文案分流] ==================
|
||||
echo "========================================================"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "🎉 Master 控制中枢平滑热更新完成!"
|
||||
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
|
||||
|
||||
# [v3.6.1 核心] 静默 OTA 完成后,由幽灵进程主动向指挥官发送捷报
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ] && [ -n "$OTA_CHAT_ID" ] && [ -n "$TG_TOKEN" ]; then
|
||||
echo -e "\n📡 正在向指挥官发送司令部重构捷报..."
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${OTA_CHAT_ID}" \
|
||||
-d "parse_mode=Markdown" \
|
||||
-d "text=✨ *司令部中枢热重载完成!*
|
||||
🚀 当前内核已跃升至:\`v${TARGET_VERSION}\`
|
||||
🤖 新版金蝉脱壳引擎已接管阵地,全舰队指控链路恢复正常。" > /dev/null
|
||||
fi
|
||||
else
|
||||
echo "🎉 Master 控制中枢部署完成!"
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
|
||||
@@ -11,6 +11,8 @@ source "$CONF"
|
||||
|
||||
# [核心: 运行态版本继承与云通信地址]
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
# MASTER_VERSION 已经在上方的 source "$CONF" 中被载入
|
||||
# 如果本地极度陈旧没有该变量,才给定一个基础兜底值,避免变量为空导致崩溃
|
||||
MASTER_VERSION=${MASTER_VERSION:-"3.5.0"}
|
||||
@@ -20,32 +22,32 @@ OFFSET_FILE="${MASTER_DIR}/.tg_offset"
|
||||
|
||||
# --- 工具函数 ---
|
||||
send_ui() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$1\",\"text\":\"$2\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$3}}" > /dev/null
|
||||
}
|
||||
|
||||
send_msg() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=$1" -d "text=$2" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# ================== [v3.0.1 新增: 消息原位刷新函数] ==================
|
||||
edit_msg() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-d "chat_id=$1" -d "message_id=$2" -d "text=$3" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# [v3.5.3 新增: 支持内联键盘的原位 UI 重绘函数]
|
||||
edit_ui() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$1\",\"message_id\":\"$2\",\"text\":\"$3\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$4}}" > /dev/null
|
||||
}
|
||||
|
||||
# 数据库执行函数
|
||||
# 数据库执行函数 (v3.6.3 终极静默版: 动用 .timeout 点命令防泄露)
|
||||
db_exec() {
|
||||
sqlite3 "$DB_FILE" "$1"
|
||||
printf ".timeout 5000\n%s\n" "$1" | sqlite3 "$DB_FILE"
|
||||
}
|
||||
|
||||
# ================== [v3.0.4 核心: 动态 HMAC 签名生成器] ==================
|
||||
@@ -63,22 +65,28 @@ generate_signed_url() {
|
||||
local signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$CHAT_ID" | awk '{print $NF}')
|
||||
|
||||
# 返回最终带签名的 URL
|
||||
echo "http://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
|
||||
echo "https://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
|
||||
}
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v3.1.3/v3.5.3 核心: 数据库结构无损热升级] ==================
|
||||
# ================== [v3.6.3 核心: 激活 SQLite 高并发 WAL 引擎] ==================
|
||||
db_exec "PRAGMA journal_mode=WAL;" > /dev/null 2>&1
|
||||
db_exec "PRAGMA synchronous=NORMAL;" > /dev/null 2>&1
|
||||
# ==============================================================================
|
||||
|
||||
# ================== [v3.1.3-v3.6.0 核心: 数据库结构无损热升级] ==================
|
||||
# 自动探测并增加缺失字段,屏蔽已存在的报错,保护老节点数据
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN node_alias TEXT;" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_google TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_trust TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_ota TEXT DEFAULT 'false';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# --- 核心轮询循环 ---
|
||||
while true; do
|
||||
OFFSET=$(cat $OFFSET_FILE)
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${TG_TOKEN}/getUpdates?offset=${OFFSET}&timeout=30")
|
||||
UPDATES=$(curl -s --connect-timeout 5 -m 35 "https://api.telegram.org/bot${TG_TOKEN}/getUpdates?offset=${OFFSET}&timeout=30")
|
||||
|
||||
COUNT=$(echo "$UPDATES" | jq -r '.result | length' 2>/dev/null)
|
||||
|
||||
@@ -113,7 +121,7 @@ while true; do
|
||||
|
||||
# 告诉 TG 官方“指令已收到”,立刻消除按钮上的加载圈圈
|
||||
if [ -n "$CB_ID" ]; then
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" -d "callback_query_id=${CB_ID}" > /dev/null
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" -d "callback_query_id=${CB_ID}" > /dev/null
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
@@ -122,17 +130,22 @@ while true; do
|
||||
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
||||
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
||||
|
||||
# V3.5.2 兼容性拆包: 支持 6字段(双轨)、5字段(单轨)、4字段(远古)
|
||||
# V3.6.0 兼容性拆包: 支持 7字段(OTA)、6字段(双轨)、5字段(单轨)、4字段(远古)
|
||||
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||
if [ "$FIELD_COUNT" -ge 6 ]; then
|
||||
if [ "$FIELD_COUNT" -ge 7 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS RAW_OTA <<< "$REG_LINE"
|
||||
elif [ "$FIELD_COUNT" -eq 6 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS <<< "$REG_LINE"
|
||||
RAW_OTA="false"
|
||||
elif [ "$FIELD_COUNT" -eq 5 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
RAW_ALIAS="$RAW_NODE"
|
||||
RAW_OTA="false"
|
||||
else
|
||||
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
RAW_REGION="UNKNOWN"
|
||||
RAW_ALIAS="$RAW_NODE"
|
||||
RAW_OTA="false"
|
||||
fi
|
||||
|
||||
# 🛡️ 强制字符白名单过滤:保留历史特征不变
|
||||
@@ -143,6 +156,8 @@ while true; do
|
||||
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5)
|
||||
NODE_ALIAS=$(echo "$RAW_ALIAS" | tr -d '"'\''\`\$\|&;<>\n\r' | cut -c 1-30)
|
||||
[ -z "$NODE_ALIAS" ] && NODE_ALIAS="$NODE_NAME"
|
||||
AGENT_OTA=$(echo "$RAW_OTA" | tr -cd 'a-z')
|
||||
[ -z "$AGENT_OTA" ] && AGENT_OTA="false"
|
||||
|
||||
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
|
||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
||||
@@ -154,8 +169,8 @@ while true; do
|
||||
continue
|
||||
fi
|
||||
|
||||
# [核心] 入库时追加 node_alias 字段
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region, node_alias) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION', '$NODE_ALIAS') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION', node_alias='$NODE_ALIAS';"
|
||||
# [核心] 入库时追加 node_alias 与 enable_ota 字段
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region, node_alias, enable_ota) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION', '$NODE_ALIAS', '$AGENT_OTA') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION', node_alias='$NODE_ALIAS', enable_ota='$AGENT_OTA';"
|
||||
send_msg "$CHAT_ID" "✅ **司令部确认 (v${MASTER_VERSION})**%0A节点 \`${NODE_ALIAS}\` 档案已录入!"
|
||||
|
||||
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
|
||||
@@ -168,7 +183,8 @@ while true; do
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;; "BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;;
|
||||
"BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
@@ -189,12 +205,93 @@ while true; do
|
||||
REMOTE_VER=$(curl -s -m 2 "${REPO_RAW_URL}/version.txt" | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
VER_INFO="当前版本: \`v${MASTER_VERSION}\`"
|
||||
|
||||
BTN_MASTER_OTA=""
|
||||
if [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" != "$MASTER_VERSION" ]; then
|
||||
VER_INFO="${VER_INFO}\n✨ **发现新版本**: \`v${REMOTE_VER}\` (请尽快更新主控)"
|
||||
VER_INFO="${VER_INFO}\n✨ **发现新版本**: \`v${REMOTE_VER}\` (可执行中枢热重载)"
|
||||
|
||||
# 仅当非官方网关 且 开启了中枢 OTA 权限时,才渲染升级按钮
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "${ENABLE_MASTER_OTA:-false}" == "true" ]; then
|
||||
BTN_MASTER_OTA="[{\"text\":\"🆙 升级司令部至 v${REMOTE_VER}\",\"callback_data\":\"master_ota_confirm\"}],"
|
||||
fi
|
||||
fi
|
||||
|
||||
BTNS="[[{\"text\":\"🖥️ 我的节点列表\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全节点日报汇总\",\"callback_data\":\"all_reports\"}], [{\"text\":\"🛠️ 全节点一键维护\",\"callback_data\":\"all_run\"}]]"
|
||||
send_ui "$CHAT_ID" "🛡️ **IP-Sentinel 司令部**\n${VER_INFO}\n\n欢迎回来,长官。请下达指令:" "$BTNS"
|
||||
NODE_COUNT=$(db_exec "SELECT COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
[ -z "$NODE_COUNT" ] && NODE_COUNT=0
|
||||
|
||||
# L0 扁平化重构:将司令部升级按钮动态置于最顶层
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ]; then
|
||||
BTNS="[${BTN_MASTER_OTA}[{\"text\":\"🌍 进入全球战区雷达 (管理节点)\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全军总攻\",\"callback_data\":\"all_run\"}, {\"text\":\"📊 全军简报\",\"callback_data\":\"all_reports\"}], [{\"text\":\"☢️ 全舰队 OTA 热重载\",\"callback_data\":\"all_ota_confirm\"}]]"
|
||||
else
|
||||
BTNS="[[{\"text\":\"🌍 进入全球战区雷达 (管理节点)\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全军总攻\",\"callback_data\":\"all_run\"}, {\"text\":\"📊 全军简报\",\"callback_data\":\"all_reports\"}]]"
|
||||
fi
|
||||
TEXT_MSG="🛡️ **IP-Sentinel 司令部**\n${VER_INFO}\n\n📊 舰队状态: 共有 \`${NODE_COUNT}\` 台哨兵在线\n欢迎回来,长官。请下达战略指令:"
|
||||
send_ui "$CHAT_ID" "$TEXT_MSG" "$BTNS"
|
||||
;;
|
||||
|
||||
"all_ota_confirm")
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 我已了解风险,下发核按钮指令!\",\"callback_data\":\"all_ota_execute\"}], [{\"text\":\"取消操作\",\"callback_data\":\"/start\"}]]"
|
||||
WARNING_MSG="☢️ **【最高指令:全舰队 OTA 升级】**\n\n此操作将向您名下**所有开启 OTA 权限的节点**下发重组指令,强制从云端拉取最新代码并进行热重载。\n\n⚠️ **核按钮风险提示**:\n1. 升级过程中守护进程会短暂重启,节点可能出现临时离线。\n2. 若遇 GitHub 源屏蔽或网络极度恶劣,少数节点可能需要手动干预。\n\n**是否确定挂载并执行 OTA 指令?**"
|
||||
send_ui "$CHAT_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
;;
|
||||
|
||||
"all_ota_execute")
|
||||
NODE_DATA=$(db_exec "SELECT node_name, agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND enable_ota='true';")
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无开启 OTA 权限的在线节点。"
|
||||
else
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在唤醒全舰队执行 OTA 升级...**%0A*(节点升级成功后会主动发回新的入库确认,请注意查收)*"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_ota")
|
||||
{ curl -k -s -m 5 "$TARGET_URL" || curl -s -m 5 "${TARGET_URL/https:\/\//http:\/\/}"; } > /dev/null &
|
||||
sleep 0.3 # 严格流量削峰
|
||||
done
|
||||
fi
|
||||
;;
|
||||
|
||||
"master_ota_confirm")
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 确认重构司令部\",\"callback_data\":\"master_ota_execute\"}], [{\"text\":\"取消操作\",\"callback_data\":\"/start\"}]]"
|
||||
WARNING_MSG="☢️ **【最高指令:中枢金蝉脱壳】**\n\n此操作将拉取最新源码并强行覆盖司令部核心进程。\n\n⚠️ **风险提示**:\n升级期间司令部将短暂失联(约3-5秒)。完成后会自动发送捷报。\n\n**是否确定执行司令部自我升级?**"
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
"master_ota_execute")
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在下载重构图纸,司令部即将进入静默重启..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在下载重构图纸,司令部即将进入静默重启..."
|
||||
fi
|
||||
|
||||
# 下载最新的 master install 脚本作为幽灵进程
|
||||
curl -fsSL "${REPO_RAW_URL}/master/install_master.sh" -o "/tmp/install_master.sh"
|
||||
|
||||
# [v3.6.3 修复] 🚀 OTA 防砖机制:严格校验脚本完整性
|
||||
if ! bash -n "/tmp/install_master.sh" >/dev/null 2>&1; then
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "❌ OTA 传输受损:脚本下载不完整,已触发防砖熔断,升级取消!"
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ OTA 传输受损:脚本下载不完整,已触发防砖熔断,升级取消!"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
chmod +x "/tmp/install_master.sh"
|
||||
|
||||
# 抛出幽灵进程进行脱壳升级,传递静默变量与回执 ID
|
||||
# [修复] 必须显式将环境变量注入到 bash -c 的指令串中,防止被 systemd-run 沙盒隔离丢弃
|
||||
if command -v systemd-run >/dev/null 2>&1; then
|
||||
systemd-run --quiet --no-block /bin/bash -c "export SILENT_MASTER_OTA='true'; export OTA_CHAT_ID='$CHAT_ID'; bash /tmp/install_master.sh"
|
||||
else
|
||||
export SILENT_MASTER_OTA="true"
|
||||
export OTA_CHAT_ID="$CHAT_ID"
|
||||
nohup bash /tmp/install_master.sh >/dev/null 2>&1 & disown
|
||||
fi
|
||||
|
||||
# 当前旧进程休眠并等待被幽灵进程处决
|
||||
sleep 10
|
||||
;;
|
||||
|
||||
"all_reports")
|
||||
@@ -202,11 +299,13 @@ while true; do
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点。"
|
||||
else
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**"
|
||||
# [文案优化] 提前告知指挥官需要排队等待
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**%0A*(为防止触发 TG 官方限流,简报将排队依次送达,请耐心等待)*"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_report")
|
||||
curl -s -m 5 "$TARGET_URL" > /dev/null &
|
||||
sleep 0.2 # [新增] 流量削峰:每秒最多并发下发 5 个,保护 Master 网络栈
|
||||
{ curl -k -s -m 5 "$TARGET_URL" || curl -s -m 5 "${TARGET_URL/https:\/\//http:\/\/}"; } > /dev/null &
|
||||
# [致命修复] 强行休眠 2 秒!错开 TG 官方 1条/秒 的发信红线
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
;;
|
||||
@@ -220,7 +319,7 @@ while true; do
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在唤醒所有哨兵执行系统维护...**"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_run")
|
||||
curl -s -m 5 "$TARGET_URL" > /dev/null &
|
||||
curl -k -s -m 5 "$TARGET_URL" > /dev/null &
|
||||
sleep 0.2 # [新增] 流量削峰:防止瞬间 fork 导致句柄耗尽
|
||||
done
|
||||
fi
|
||||
@@ -238,14 +337,16 @@ while true; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG="🌐"
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;; "BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;;
|
||||
"BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||
# L1 追加返回中枢逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n已为您聚合当前舰队的部署大区,请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -278,9 +379,9 @@ while true; do
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
fi
|
||||
# 添加返回上级大区雷达的按钮
|
||||
BTNS="$BTNS[{\"text\":\"⬅️ 返回全球战区分布\",\"callback_data\":\"list_nodes\"}]]"
|
||||
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请下达控制指令:" "$BTNS"
|
||||
# L2 追加双重逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"⬅️ 返回战区地图\",\"callback_data\":\"list_nodes\"}, {\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请锁定要执行战术动作的具体目标:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -289,37 +390,43 @@ while true; do
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
# [v3.5.3] 将改名收纳至 L5 高级面板,替换为“高级控制功能”
|
||||
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"⚙️ 高级控制功能\",\"callback_data\":\"adv:$TARGET_NODE\"}, {\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回大区目录\",\"callback_data\":\"list_nodes\"}]]"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n*(身份标识: $TARGET_NODE)*\n请选择战术动作:" "$BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n*(身份标识: $TARGET_NODE)*\n请选择战术动作:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
adv:*)
|
||||
# [L5 高级控制面板渲染]
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
# 抓取当前节点的开关状态
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
# 抓取节点全景元数据
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
ST_OTA=$(echo "$TOGGLE_INFO" | cut -d'|' -f3)
|
||||
A_IP=$(echo "$TOGGLE_INFO" | cut -d'|' -f4)
|
||||
LAST_SEEN=$(echo "$TOGGLE_INFO" | cut -d'|' -f5)
|
||||
|
||||
# 动态渲染状态机红绿灯 UI
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
|
||||
# 动态渲染状态文字
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
BTNS="[[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}], [{\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}], [{\"text\":\"✏️ 修改节点备注\",\"callback_data\":\"rename:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回节点面板\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
# 模块一:即时战术动作
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n请下达控制指令:" "$BTNS"
|
||||
# 模块二:养护状态启停
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
# 模块三:深度配置管理 (结合 UI 熔断)
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}, {\"text\":\"🆙 OTA 静默升级\",\"callback_data\":\"ota_confirm:$TARGET_NODE\"}]"
|
||||
else
|
||||
send_ui "$CHAT_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n请下达控制指令:" "$BTNS"
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}]"
|
||||
fi
|
||||
|
||||
# 模块四:危险区与逃生舱
|
||||
BTN_DANGER="[{\"text\":\"🗑️ 从中枢销毁该档案\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回战区列表\",\"callback_data\":\"list_nodes\"}]"
|
||||
|
||||
# 组合终极矩阵
|
||||
BTNS="[$BTN_ACTION, $BTN_TOGGLE, $BTN_CONFIG, $BTN_DANGER]"
|
||||
|
||||
TEXT_MSG="⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n(底层标识: \`$TARGET_NODE\`)\n🌐 IP 坐标: \`$A_IP\`\n🕒 最后通讯: \`$LAST_SEEN\`\n\n请下达精确控制指令:"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$TEXT_MSG" "$BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "$TEXT_MSG" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -336,7 +443,13 @@ while true; do
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_toggle")
|
||||
TARGET_URL="${TARGET_URL}&mod=${MOD_NAME}&state=${TARGET_STATE}"
|
||||
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
# [向下兼容补丁] 若 HTTPS 拒绝或超时,回退 HTTP 试探老节点
|
||||
if [ "$RESPONSE" == "FAILED" ] || [ -z "$RESPONSE" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
fi
|
||||
|
||||
if [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
# 下发成功,更新 DB,原位重绘
|
||||
db_exec "UPDATE nodes SET enable_${MOD_NAME}='$TARGET_STATE' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
@@ -347,10 +460,32 @@ while true; do
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
|
||||
|
||||
BTNS="[[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}], [{\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}], [{\"text\":\"✏️ 修改节点备注\",\"callback_data\":\"rename:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回节点面板\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
# 切换后直接复用扁平化 L3 面板的重绘逻辑
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
ST_OTA=$(echo "$TOGGLE_INFO" | cut -d'|' -f3)
|
||||
A_IP=$(echo "$TOGGLE_INFO" | cut -d'|' -f4)
|
||||
LAST_SEEN=$(echo "$TOGGLE_INFO" | cut -d'|' -f5)
|
||||
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}, {\"text\":\"🆙 OTA 静默升级\",\"callback_data\":\"ota_confirm:$TARGET_NODE\"}]"
|
||||
else
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}]"
|
||||
fi
|
||||
BTN_DANGER="[{\"text\":\"🗑️ 从中枢销毁该档案\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回战区列表\",\"callback_data\":\"list_nodes\"}]"
|
||||
|
||||
BTNS="[$BTN_ACTION, $BTN_TOGGLE, $BTN_CONFIG, $BTN_DANGER]"
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n✅ 成功:模块 [$MOD_NAME] 状态已切换为 $TARGET_STATE!" "$BTNS"
|
||||
TEXT_MSG="⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n(底层标识: \`$TARGET_NODE\`)\n🌐 IP 坐标: \`$A_IP\`\n🕒 最后通讯: \`$LAST_SEEN\`\n\n✅ **执行成功**: 模块 [$MOD_NAME] 状态已切换为 $TARGET_STATE!"
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$TEXT_MSG" "$BTNS"
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 指令下发失败,节点可能离线或未更新至 v3.5.3。"
|
||||
fi
|
||||
@@ -377,6 +512,8 @@ while true; do
|
||||
case "$REGION_NAME" in
|
||||
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;;
|
||||
"BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||
esac
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
@@ -412,7 +549,12 @@ while true; do
|
||||
ALIAS_B64=$(echo -n "$NEW_ALIAS" | base64 | tr -d '\n' | tr '+/' '-_')
|
||||
TARGET_URL="${TARGET_URL}&b64=${ALIAS_B64}"
|
||||
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
# [向下兼容补丁] 若 HTTPS 拒绝或超时,回退 HTTP 试探老节点
|
||||
if [ "$RESPONSE" == "FAILED" ] || [ -z "$RESPONSE" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
fi
|
||||
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时!请检查节点连通性。"
|
||||
@@ -429,6 +571,54 @@ while true; do
|
||||
fi
|
||||
;;
|
||||
|
||||
ota_confirm:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
# 将取消动作引导回 manage,因为 adv 已经被删除了
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 确认执行远程升级\",\"callback_data\":\"ota_execute:$TARGET_NODE\"}], [{\"text\":\"取消\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
send_ui "$CHAT_ID" "☢️ **操作确认**:即将向 \`$TARGET_NODE\` 下发 OTA 热更新指令。\n节点更新完成后会自动发送包含新版本号的注册回执,确定执行?" "$CONFIRM_BTNS"
|
||||
;;
|
||||
|
||||
ota_execute:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在向 \`$TARGET_NODE\` 发送 OTA 触发报文..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` 发送 OTA 触发报文..."
|
||||
fi
|
||||
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_ota")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
# [向下兼容补丁] 若 HTTPS 拒绝或超时,回退 HTTP 试探老节点
|
||||
if [ "$RESPONSE" == "FAILED" ] || [ -z "$RESPONSE" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
fi
|
||||
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
TEXT_RES="❌ OTA 指令下发超时!请检查节点公网连通性。"
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
TEXT_RES="⚠️ **节点拒绝执行**:该节点本地未开启 OTA 权限或运行在官方网关下!"
|
||||
else
|
||||
TEXT_RES="✅ OTA 触发成功!节点正在后台执行拉取重构,请等待其发送更新完成的回执消息。"
|
||||
fi
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "$TEXT_RES"
|
||||
else
|
||||
send_msg "$CHAT_ID" "$TEXT_RES"
|
||||
fi
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
|
||||
fi
|
||||
;;
|
||||
|
||||
# 【核心升级】增加拦截规则,支持 google 和 trust 前缀
|
||||
google:*|trust:*|run:*|report:*|log:*)
|
||||
# 🛡️ 提取并强制过滤动作参数、节点名与 CHAT_ID
|
||||
@@ -450,7 +640,12 @@ while true; do
|
||||
|
||||
# 🛡️ [v3.0.4] 动态签名生成与触发 (防重放与防篡改)
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_${ACTION_TYPE}")
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
# [向下兼容补丁] 若 HTTPS 拒绝或超时,回退 HTTP 试探老节点
|
||||
if [ "$RESPONSE" == "FAILED" ] || [ -z "$RESPONSE" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
fi
|
||||
|
||||
# 结果判定
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
|
||||
@@ -5,6 +5,15 @@
|
||||
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MASTER_DIR="/opt/ip_sentinel_master"
|
||||
CONF_FILE="${MASTER_DIR}/master.conf"
|
||||
|
||||
@@ -25,19 +34,30 @@ if [[ ! "$CONFIRM_DEL" =~ ^[Yy]$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. 停止运行中的 Master 守护进程
|
||||
echo "[1/3] 正在终止后台中枢调度进程..."
|
||||
# [优化] 使用 pkill 替代 pgrep | xargs,指令更短、容错率更高
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
systemctl disable --now ip-sentinel-master.service >/dev/null 2>&1
|
||||
rm -f /etc/systemd/system/ip-sentinel-master.service
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed
|
||||
else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 2. 停止运行中的 Master 守护进程 (兜底清理老版进程)
|
||||
echo "[2/4] 正在终止后台中枢调度进程..."
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
# 2. 清除看门狗定时任务 (Cron)
|
||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||
# 3. 清除看门狗定时任务 (Cron)
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
|
||||
# 3. 删除所有文件、配置与数据库
|
||||
echo "[3/3] 正在抹除核心程序、配置文件与 SQLite 数据库..."
|
||||
# 4. 删除所有文件、配置与数据库
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与 SQLite 数据库..."
|
||||
if [ -d "$MASTER_DIR" ]; then
|
||||
rm -rf "$MASTER_DIR"
|
||||
fi
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
AGENT_VERSION=3.5.4
|
||||
MASTER_VERSION=3.5.4
|
||||
MASTER_VERSION=3.6.3
|
||||
AGENT_VERSION=3.6.3
|
||||
|
||||
Reference in New Issue
Block a user