feat: server.py 迁移部署到 NGCQ(1Panel Nginx 反代 eink.njcqit.com)+境内可达 BTC 行情源+页面访问令牌

- btc_api:新增 gateio/huobi_aws/binance_vision 三源,config 增 BTC_SOURCES 控制尝试顺序(境内 okx/coinbase/huobi 主域名不通)
- server:IMAGE_TOKEN 非空时三个渲染端点须带 ?token=…,否则与未知路径同样 404(防爬虫扫描);推送令牌改常量时间比较
- 文档:CLAUDE.md/README/.env.example 记录 NGCQ 部署(systemd eink-server、1Panel API 建站坑)、本机 server/cloudflared 已卸载

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 11:42:23 +08:00
parent 7c60d3680c
commit 04fa31cb94
6 changed files with 86 additions and 22 deletions

View File

@@ -20,14 +20,17 @@ HTML 控件填同一个 URL 则**每个刷新周期都重抓**。所以对外只
- 三路上游过期时**并行**实拉,整体最多等 FETCH_BUDGET 秒(默认 5超预算的一路本次用旧值、后台拉完更新缓存见 build_data
所以推送时机与设备抓取时机不必对齐。未推送过Claude 显示 "--"ChatGPT 用 mock 并标「示例数据」。
- 推送鉴权:请求头 X-Push-Token 必须等于 PUSH_TOKENPUSH_TOKEN 为空时拒绝一切推送(服务在公网,务必配置)。
- 页面鉴权IMAGE_TOKEN 非空时,三个渲染端点必须带 ?token=<IMAGE_TOKEN>,否则返回与未知路径一样的 404防爬虫扫描
其他端点GET /health → JSON 运行状态GET /push/usage、/push/usage_gpt → 查看暂存的原始快照(需同样令牌)。
部署python3 server.py监听 SERVER_HOST:SERVER_PORT前置 Caddy/Nginx 做 HTTPS或 cloudflared tunnel。
"""
import hmac
import io
import json
import os
import threading
import time
import urllib.parse
from concurrent.futures import ThreadPoolExecutor, wait
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -221,12 +224,24 @@ class Handler(BaseHTTPRequestHandler):
def _authed(self):
tok = config.PUSH_TOKEN
return bool(tok) and self.headers.get("X-Push-Token", "") == tok
return bool(tok) and hmac.compare_digest(self.headers.get("X-Push-Token", ""), tok)
def _page_authed(self):
"""页面令牌IMAGE_TOKEN 为空则放行;否则查询串里 token 必须相等(常量时间比较)。"""
tok = config.IMAGE_TOKEN
if not tok:
return True
qs = urllib.parse.parse_qs(urllib.parse.urlsplit(self.path).query)
return any(hmac.compare_digest(v, tok) for v in qs.get("token", []))
# ---------- GET ----------
def do_GET(self):
path = self.path.split("?", 1)[0]
try:
if path in (config.IMAGE_PATH, config.HTML_PATH, config.HTML_PNG_PATH) and not self._page_authed():
# 令牌不对与路径不存在返回完全一样的 404不暴露端点存在
self._log(404, "bad page token")
return self._json(404, {"ok": False, "error": "not found"})
if path == config.IMAGE_PATH:
t0 = time.time()
d, st = build_data()