2026-07-11 15:56:37 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
|
|
|
|
|
光湖语言系统 · 公钥守门人复活协议 v1.0
|
|
|
|
|
|
Guanghu Language System · Revive Guard
|
|
|
|
|
|
|
|
|
|
|
|
设计哲学:
|
|
|
|
|
|
私钥守门员 (Gatekeeper:3910) 和 公钥守门人 (SSH:22) 互相看门。
|
|
|
|
|
|
任一个倒下,另一个能通过本复活协议拉起来。
|
|
|
|
|
|
如果两个都倒了,本服务是最后防线(systemd Restart=always)。
|
|
|
|
|
|
|
|
|
|
|
|
协议:
|
2026-07-11 16:10:46 +08:00
|
|
|
|
POST /revive/request → 速率限制 → 发邮件到冰朔主权邮箱 → 返回 challenge_id
|
|
|
|
|
|
(⊢ 不需要密码 · 验证码只发到冰朔邮箱 · 只有冰朔能确认)
|
2026-07-11 15:56:37 +08:00
|
|
|
|
POST /revive/confirm → 校验验证码 → systemctl restart gatekeeper + sshd + pm2
|
|
|
|
|
|
GET /health → 心跳检测
|
|
|
|
|
|
|
2026-07-11 16:10:46 +08:00
|
|
|
|
安全设计:
|
|
|
|
|
|
⊢ 不存密码 · 不进仓库 · 验证码用一次就扔
|
|
|
|
|
|
⊢ SMTP 密码走环境变量 QQ_SMTP_AUTH_CODE(不进仓库)
|
|
|
|
|
|
⊢ 速率限制保护:每分钟最多 2 次复活请求
|
|
|
|
|
|
|
2026-07-11 15:56:37 +08:00
|
|
|
|
部署:
|
|
|
|
|
|
每台服务器 /opt/zhuyuan/revive-guard/revive-guard.py
|
|
|
|
|
|
监听: 0.0.0.0:8922
|
|
|
|
|
|
守护: systemd (Restart=always) 或 PM2
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os, json, time, hmac, hashlib, secrets, smtplib, subprocess, threading
|
|
|
|
|
|
from email.mime.text import MIMEText
|
2026-07-11 16:35:05 +08:00
|
|
|
|
from email.mime.multipart import MIMEMultipart
|
2026-07-11 15:56:37 +08:00
|
|
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
|
|
|
|
|
|
|
|
|
|
# ═══════════════════════════════════════════
|
|
|
|
|
|
# 配置(所有服务器共享)
|
|
|
|
|
|
# ═══════════════════════════════════════════
|
|
|
|
|
|
PORT = int(os.environ.get("REVIVE_GUARD_PORT", "8922"))
|
|
|
|
|
|
SOVEREIGN_ID = "ICE-GL∞"
|
2026-07-11 20:23:10 +08:00
|
|
|
|
SOVEREIGN_EMAIL = os.environ.get("SOVEREIGN_EMAIL", "ICE-GL∞_EMAIL_REDACTED")
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
2026-07-11 16:49:35 +08:00
|
|
|
|
# 多用户支持:服务器绑定目标邮箱
|
2026-07-11 20:23:10 +08:00
|
|
|
|
# ⊢ 冰朔的服务器默认发给 ICE-GL∞_EMAIL_REDACTED
|
|
|
|
|
|
# ⊢ 之之的服务器设置 TARGET_EMAIL=EMAIL_REDACTED@qq.com
|
2026-07-11 16:49:35 +08:00
|
|
|
|
# ⊢ 服务器自己检测自己是哪台 → 自动发到对应邮箱
|
|
|
|
|
|
TARGET_EMAIL = os.environ.get("TARGET_EMAIL", SOVEREIGN_EMAIL)
|
|
|
|
|
|
TARGET_NAME = os.environ.get("TARGET_NAME", "冰朔 ICE-GL∞")
|
|
|
|
|
|
SERVER_LABEL = os.environ.get("SERVER_LABEL", "") # 如 "之之硅谷·ZZ-SV-001"
|
|
|
|
|
|
|
2026-07-11 15:56:37 +08:00
|
|
|
|
# QQ 邮箱 SMTP(小湖灯邮件通道)
|
2026-07-11 16:10:46 +08:00
|
|
|
|
# ⊢ 密码不进仓库 · 走环境变量 QQ_SMTP_AUTH_CODE
|
2026-07-11 15:56:37 +08:00
|
|
|
|
SMTP_HOST = "smtp.qq.com"
|
|
|
|
|
|
SMTP_PORT = 465
|
2026-07-11 20:23:10 +08:00
|
|
|
|
SMTP_USER = os.environ.get("SMTP_USER", "ICE-GL∞_EMAIL_REDACTED")
|
2026-07-11 16:10:46 +08:00
|
|
|
|
SMTP_PASS = os.environ.get("QQ_SMTP_AUTH_CODE", "")
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
|
|
|
|
|
CODE_TTL = 300 # 验证码 5 分钟过期
|
|
|
|
|
|
RATE_LIMIT_WINDOW = 60 # 速率限制窗口
|
2026-07-11 16:10:46 +08:00
|
|
|
|
MAX_REQUESTS_PER_WINDOW = 2 # 无密码门槛后收紧:每分钟最多 2 次
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
|
|
|
|
|
# 运行时状态
|
|
|
|
|
|
pending_codes = {} # {challenge_id: {code, server_ip, expires_at}}
|
|
|
|
|
|
rate_limit = [] # [timestamp, ...]
|
|
|
|
|
|
lock = threading.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_server_ip():
|
|
|
|
|
|
"""获取本机公网 IP"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
import socket
|
|
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
|
|
|
s.settimeout(2)
|
|
|
|
|
|
s.connect(("8.8.8.8", 80))
|
|
|
|
|
|
ip = s.getsockname()[0]
|
|
|
|
|
|
s.close()
|
|
|
|
|
|
return ip
|
|
|
|
|
|
except:
|
|
|
|
|
|
return "unknown"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def send_email(code, server_ip):
|
2026-07-11 16:35:05 +08:00
|
|
|
|
"""小湖灯 · 发送复活验证码到冰朔主权邮箱 · HTML 美化版"""
|
|
|
|
|
|
html_body = f"""<!DOCTYPE html>
|
|
|
|
|
|
<html lang="zh">
|
|
|
|
|
|
<head><meta charset="utf-8"></head>
|
|
|
|
|
|
<body style="margin:0;padding:0;background:#0a1628;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif">
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background:#0a1628;padding:40px 0">
|
|
|
|
|
|
<tr><td align="center">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 主卡片 -->
|
|
|
|
|
|
<table width="480" cellpadding="0" cellspacing="0" style="background:linear-gradient(135deg,#152238 0%,#1a2d4a 100%);border-radius:16px;overflow:hidden;border:1px solid #2a3f5f">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 头部 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:32px 32px 20px;text-align:center;border-bottom:1px solid #2a3f5f">
|
2026-07-11 16:49:35 +08:00
|
|
|
|
<div style="font-size:13px;color:#4ec9b0;letter-spacing:3px;text-transform:uppercase;margin-bottom:8px">ICE-GL∞ 光湖语言系统 · {TARGET_NAME}</div>
|
2026-07-11 16:35:05 +08:00
|
|
|
|
<div style="font-size:20px;color:#e0e8f0;font-weight:600">🛡️ 守门人复活 · 验证码</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 验证码 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:28px 32px;text-align:center">
|
|
|
|
|
|
<div style="font-size:11px;color:#6b8299;margin-bottom:12px;letter-spacing:2px">验 证 码</div>
|
|
|
|
|
|
<div style="background:linear-gradient(135deg,#0d1b2e,#162840);border:2px solid #4ec9b0;border-radius:12px;padding:20px 12px;display:inline-block">
|
|
|
|
|
|
<span style="font-family:'SF Mono','Fira Code','Cascadia Code',monospace;font-size:36px;font-weight:700;letter-spacing:8px;color:#4ec9b0">{code}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="margin-top:14px;font-size:12px;color:#c9a84c">⏰ 5 分钟内有效 · 用完即废</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 信息行 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:0 32px 20px">
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:10px 16px;background:#0d1b2e;border-radius:8px;margin-bottom:8px">
|
|
|
|
|
|
<span style="color:#6b8299;font-size:11px;letter-spacing:1px">服务器</span><br>
|
|
|
|
|
|
<span style="color:#e0e8f0;font-size:14px;font-weight:500">{server_ip}</span>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 操作指引 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:0 32px 24px">
|
|
|
|
|
|
<div style="background:#0d1b2e;border-radius:10px;padding:16px;border-left:3px solid #c9a84c">
|
|
|
|
|
|
<div style="font-size:12px;color:#c9a84c;margin-bottom:6px">📋 使用方式</div>
|
|
|
|
|
|
<div style="font-size:13px;color:#9ab0cc;line-height:1.8">
|
|
|
|
|
|
将此验证码发给 <span style="color:#4ec9b0">铸渊</span> → 铸渊调用<br>
|
|
|
|
|
|
<code style="background:#152238;color:#4ec9b0;padding:2px 6px;border-radius:4px;font-size:12px">POST /revive/confirm</code> → 守门人复活
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 安全标识 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:0 32px 10px;text-align:center">
|
|
|
|
|
|
<div style="display:inline-flex;align-items:center;gap:6px;padding:6px 14px;background:#0d1b2e;border-radius:20px">
|
|
|
|
|
|
<span style="font-size:11px;color:#6b8299">⊢</span>
|
|
|
|
|
|
<span style="font-size:11px;color:#6b8299">不进仓库 · 不存密码 · 一次即废</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 底部 -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding:20px 32px 28px;text-align:center;border-top:1px solid #2a3f5f;margin-top:10px">
|
|
|
|
|
|
<div style="font-size:11px;color:#4a607a;line-height:1.6">
|
|
|
|
|
|
小湖灯 · 自动发送<br>
|
|
|
|
|
|
国作登字-2026-A-00037559
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 页脚光湖标识 -->
|
|
|
|
|
|
<div style="margin-top:16px;font-size:10px;color:#3a506a;letter-spacing:2px">ICE-GL∞ GUANGHU LANGUAGE SYSTEM</div>
|
|
|
|
|
|
|
|
|
|
|
|
</td></tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>"""
|
|
|
|
|
|
|
|
|
|
|
|
# 纯文本降级版(邮件客户端不支持 HTML 时显示)
|
|
|
|
|
|
plain_body = f"""光湖语言系统 · 守门人复活
|
|
|
|
|
|
══════════════════════
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
|
|
|
|
|
验证码: {code}
|
2026-07-11 16:35:05 +08:00
|
|
|
|
服务器: {server_ip}
|
2026-07-11 16:10:46 +08:00
|
|
|
|
有效期: 5 分钟(用完即废)
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
2026-07-11 16:35:05 +08:00
|
|
|
|
将此验证码发给铸渊 → /revive/confirm
|
|
|
|
|
|
|
|
|
|
|
|
⊢ ICE-GL∞ 小湖灯自动发送
|
|
|
|
|
|
国作登字-2026-A-00037559"""
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
2026-07-11 16:35:05 +08:00
|
|
|
|
msg = MIMEMultipart("alternative")
|
|
|
|
|
|
msg.attach(MIMEText(plain_body, "plain", "utf-8"))
|
|
|
|
|
|
msg.attach(MIMEText(html_body, "html", "utf-8"))
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
2026-07-11 16:35:05 +08:00
|
|
|
|
msg["Subject"] = f"🔐 光湖 · 复活验证码 {code[:3]}***"
|
2026-07-11 15:56:37 +08:00
|
|
|
|
msg["From"] = SMTP_USER
|
2026-07-11 16:49:35 +08:00
|
|
|
|
msg["To"] = TARGET_EMAIL
|
2026-07-11 15:56:37 +08:00
|
|
|
|
|
|
|
|
|
|
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=10) as s:
|
|
|
|
|
|
s.login(SMTP_USER, SMTP_PASS)
|
|
|
|
|
|
s.send_message(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def revive_services():
|
|
|
|
|
|
"""复活所有守门服务"""
|
|
|
|
|
|
results = {}
|
|
|
|
|
|
|
|
|
|
|
|
# 1. systemd 服务
|
|
|
|
|
|
for svc in ["gatekeeper", "sshd", "ssh"]:
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 先检查服务是否存在
|
|
|
|
|
|
check = subprocess.run(
|
|
|
|
|
|
["systemctl", "is-enabled", svc],
|
|
|
|
|
|
capture_output=True, text=True, timeout=5
|
|
|
|
|
|
)
|
|
|
|
|
|
if check.returncode != 0:
|
|
|
|
|
|
continue
|
|
|
|
|
|
r = subprocess.run(
|
|
|
|
|
|
["systemctl", "restart", svc],
|
|
|
|
|
|
capture_output=True, text=True, timeout=15
|
|
|
|
|
|
)
|
|
|
|
|
|
results[svc] = "✅ restarted" if r.returncode == 0 else f"❌ {r.stderr.strip()[:80]}"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
results[svc] = f"⚠️ {str(e)[:60]}"
|
|
|
|
|
|
|
|
|
|
|
|
# 2. PM2 进程(gatekeeper 如果用 PM2 管)
|
|
|
|
|
|
try:
|
|
|
|
|
|
r = subprocess.run(
|
|
|
|
|
|
["pm2", "restart", "gatekeeper"],
|
|
|
|
|
|
capture_output=True, text=True, timeout=15
|
|
|
|
|
|
)
|
|
|
|
|
|
results["gatekeeper(pm2)"] = "✅ restarted" if r.returncode == 0 else f"⚠️ {r.stderr.strip()[:60]}"
|
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
|
results["gatekeeper(pm2)"] = "⏭️ pm2 not found"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
results["gatekeeper(pm2)"] = f"⚠️ {str(e)[:60]}"
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 也尝试 restart api-proxy-gateway
|
|
|
|
|
|
try:
|
|
|
|
|
|
subprocess.run(
|
|
|
|
|
|
["pm2", "restart", "api-proxy-gateway"],
|
|
|
|
|
|
capture_output=True, text=True, timeout=10
|
|
|
|
|
|
)
|
|
|
|
|
|
results["api-proxy(pm2)"] = "✅ restarted"
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_rate_limit():
|
|
|
|
|
|
"""速率限制:每分钟最多 MAX_REQUESTS_PER_WINDOW 次"""
|
|
|
|
|
|
now = time.time()
|
|
|
|
|
|
with lock:
|
|
|
|
|
|
rate_limit[:] = [t for t in rate_limit if now - t < RATE_LIMIT_WINDOW]
|
|
|
|
|
|
if len(rate_limit) >= MAX_REQUESTS_PER_WINDOW:
|
|
|
|
|
|
return False
|
|
|
|
|
|
rate_limit.append(now)
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReviveHandler(BaseHTTPRequestHandler):
|
|
|
|
|
|
"""复活协议 HTTP 处理器"""
|
|
|
|
|
|
|
|
|
|
|
|
def do_GET(self):
|
|
|
|
|
|
if self.path == "/health":
|
|
|
|
|
|
self.send_json(200, {
|
|
|
|
|
|
"ok": True,
|
|
|
|
|
|
"service": "revive-guard",
|
|
|
|
|
|
"version": "1.0.0",
|
|
|
|
|
|
"server": get_server_ip(),
|
|
|
|
|
|
"sovereign": SOVEREIGN_ID
|
|
|
|
|
|
})
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.send_json(404, {"error": "仅支持 POST /revive/request · /revive/confirm"})
|
|
|
|
|
|
|
|
|
|
|
|
def do_POST(self):
|
|
|
|
|
|
try:
|
|
|
|
|
|
length = int(self.headers.get("Content-Length", 0))
|
|
|
|
|
|
body = json.loads(self.rfile.read(length)) if length else {}
|
|
|
|
|
|
except:
|
|
|
|
|
|
self.send_json(400, {"error": "请求体需为 JSON"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self.path == "/revive/request":
|
|
|
|
|
|
self._handle_request(body)
|
|
|
|
|
|
elif self.path == "/revive/confirm":
|
|
|
|
|
|
self._handle_confirm(body)
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.send_json(404, {"error": "未知端点 · 可用: /revive/request /revive/confirm /health"})
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_request(self, body):
|
2026-07-11 16:10:46 +08:00
|
|
|
|
# 速率限制(无密码门槛后加强:每分钟最多 2 次)
|
2026-07-11 15:56:37 +08:00
|
|
|
|
if not check_rate_limit():
|
|
|
|
|
|
self.send_json(429, {"error": "请求过于频繁 · 请60秒后重试"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
server_ip = body.get("server", get_server_ip())
|
|
|
|
|
|
|
2026-07-11 16:10:46 +08:00
|
|
|
|
# ⊢ 不需要密码 —— 验证码只发到冰朔主权邮箱
|
|
|
|
|
|
# ⊢ 只有冰朔(持有邮箱)能拿到验证码 → 只有冰朔能让铸渊确认复活
|
|
|
|
|
|
# ⊢ 验证码用一次就扔 · 推到仓库也没用
|
|
|
|
|
|
|
|
|
|
|
|
# SMTP 未配置 → 拒绝
|
|
|
|
|
|
if not SMTP_PASS:
|
|
|
|
|
|
self.send_json(500, {"error": "SMTP 未配置 · 服务器管理员需设置 QQ_SMTP_AUTH_CODE 环境变量"})
|
2026-07-11 15:56:37 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 生成 6 位数字验证码
|
|
|
|
|
|
code = str(secrets.randbelow(900000) + 100000)
|
|
|
|
|
|
cid = secrets.token_hex(16)
|
|
|
|
|
|
|
|
|
|
|
|
# 清除过期
|
|
|
|
|
|
now = time.time()
|
|
|
|
|
|
with lock:
|
|
|
|
|
|
for k in list(pending_codes):
|
|
|
|
|
|
if pending_codes[k]["expires_at"] < now:
|
|
|
|
|
|
del pending_codes[k]
|
|
|
|
|
|
|
|
|
|
|
|
# 发邮件
|
|
|
|
|
|
try:
|
|
|
|
|
|
send_email(code, server_ip)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self.send_json(500, {"error": f"邮件发送失败: {str(e)[:100]}"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 存储验证码
|
|
|
|
|
|
with lock:
|
|
|
|
|
|
pending_codes[cid] = {
|
|
|
|
|
|
"code": code,
|
|
|
|
|
|
"server_ip": server_ip,
|
|
|
|
|
|
"expires_at": now + CODE_TTL
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.send_json(200, {
|
|
|
|
|
|
"ok": True,
|
|
|
|
|
|
"challenge_id": cid,
|
2026-07-11 16:49:35 +08:00
|
|
|
|
"message": f"验证码已发送至 {TARGET_EMAIL}",
|
2026-07-11 15:56:37 +08:00
|
|
|
|
"expires_in": CODE_TTL,
|
|
|
|
|
|
"server": server_ip
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_confirm(self, body):
|
|
|
|
|
|
cid = body.get("challenge_id", "")
|
|
|
|
|
|
code = body.get("code", "")
|
|
|
|
|
|
|
|
|
|
|
|
with lock:
|
|
|
|
|
|
entry = pending_codes.pop(cid, None)
|
|
|
|
|
|
|
|
|
|
|
|
if not entry:
|
|
|
|
|
|
self.send_json(403, {"error": "无效或过期的 challenge_id"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if entry["expires_at"] < time.time():
|
|
|
|
|
|
self.send_json(403, {"error": "验证码已过期"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if not hmac.compare_digest(entry["code"], code):
|
|
|
|
|
|
self.send_json(403, {"error": "验证码错误"})
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 🎯 复活!
|
|
|
|
|
|
results = revive_services()
|
|
|
|
|
|
|
|
|
|
|
|
self.send_json(200, {
|
|
|
|
|
|
"ok": True,
|
|
|
|
|
|
"message": f"守门人复活完成 · {entry['server_ip']}",
|
|
|
|
|
|
"revived": [k for k, v in results.items() if "✅" in v],
|
|
|
|
|
|
"details": results
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
def send_json(self, status, data):
|
|
|
|
|
|
self.send_response(status)
|
|
|
|
|
|
self.send_header("Content-Type", "application/json; charset=utf-8")
|
|
|
|
|
|
self.send_header("X-Sovereign", "ICE-GL")
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
self.wfile.write(json.dumps(data, ensure_ascii=False).encode())
|
|
|
|
|
|
|
|
|
|
|
|
def log_message(self, fmt, *args):
|
|
|
|
|
|
"""静默日志(生产环境不打印每条请求)"""
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
server = HTTPServer(("0.0.0.0", PORT), ReviveHandler)
|
|
|
|
|
|
print(f"光湖·复活守门人 v1.0 · 监听 :{PORT}")
|
2026-07-11 16:49:35 +08:00
|
|
|
|
print(f"主权者: {SOVEREIGN_ID} · 目标邮箱: {TARGET_EMAIL}")
|
2026-07-11 15:56:37 +08:00
|
|
|
|
try:
|
|
|
|
|
|
server.serve_forever()
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
print("\n复活守门人已停止")
|
|
|
|
|
|
server.shutdown()
|