chore: 仓库与线上一致性补齐
- pixiv.py: 补录线上 2026-09-17 版(图源错误码处理/fetch_illust_url 返回 api_status) - nginx/00-limits.conf: 限流 zone 定义入库(站点 conf 引用, 缺失将导致 nginx -t 失败) - nginx/00-websocket.conf: connection_upgrade map 入库(同上) - docs: 迁移第 4 步改为套用 nginx/ 三件套; 记录一致性核对结果
This commit is contained in:
@@ -55,7 +55,9 @@ source/
|
|||||||
├── bilibili/ # 视频缓存
|
├── bilibili/ # 视频缓存
|
||||||
└── jm/ # jm 临时 ZIP(TTL 10 分钟,返回后删除)
|
└── jm/ # jm 临时 ZIP(TTL 10 分钟,返回后删除)
|
||||||
nginx/
|
nginx/
|
||||||
└── openapi.rrrosmontis.icu.conf # 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/,其余路径由 location / 兜底至聚合层)
|
├── openapi.rrrosmontis.icu.conf # 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/,其余路径由 location / 兜底至聚合层;含按 IP 限流)
|
||||||
|
├── 00-limits.conf # 限流 zone(limit_req_zone rate=10r/s、limit_conn_zone、429)——缺失会导致 nginx -t 失败
|
||||||
|
└── 00-websocket.conf # $connection_upgrade map 定义——缺失会导致 nginx -t 失败
|
||||||
systemd/
|
systemd/
|
||||||
├── openapi.service # 聚合层
|
├── openapi.service # 聚合层
|
||||||
├── pixiv.service # Pixiv 图片服务(waitress --threads=8)
|
├── pixiv.service # Pixiv 图片服务(waitress --threads=8)
|
||||||
|
|||||||
+8
-1
@@ -113,7 +113,7 @@
|
|||||||
1. **环境**:解压 `env/env_general.tar.gz`,直接使用其 `bin/python`(依赖齐全,含 waitress);若整包过旧需补 `pip install jmcomic`
|
1. **环境**:解压 `env/env_general.tar.gz`,直接使用其 `bin/python`(依赖齐全,含 waitress);若整包过旧需补 `pip install jmcomic`
|
||||||
2. **源码**:恢复 `source/{main.py,pixiv/,bilibili/,dufe/,jm/}` 到 `/home/openapi/source/`
|
2. **源码**:恢复 `source/{main.py,pixiv/,bilibili/,dufe/,jm/}` 到 `/home/openapi/source/`
|
||||||
3. **systemd**:恢复 `systemd/*.service` 到 `/etc/systemd/system/`,`daemon-reload` + `enable` + `start`
|
3. **systemd**:恢复 `systemd/*.service` 到 `/etc/systemd/system/`,`daemon-reload` + `enable` + `start`
|
||||||
4. **nginx**:套用 `conf.d/openapi.rrrosmontis.icu.conf`(并确保 `00-websocket.conf` 的 map 存在)
|
4. **nginx**:套用 `nginx/` 下三个文件到 `/etc/nginx/conf.d/`:`openapi.rrrosmontis.icu.conf` + `00-websocket.conf`(`$connection_upgrade` map,**必须**)+ `00-limits.conf`(限流 zone,**必须**,否则站点 conf 的 `limit_req zone=perip_req` 会使 `nginx -t` 失败),再 `nginx -t && systemctl reload nginx`
|
||||||
5. **SSL**:恢复 `cert/<域名>/` 到 `/etc/nginx/cert/<域名>/`
|
5. **SSL**:恢复 `cert/<域名>/` 到 `/etc/nginx/cert/<域名>/`
|
||||||
6. **凭据**:放回 `source/pixiv/tokens.json`(600) 与 `source/bilibili/cookies.txt`
|
6. **凭据**:放回 `source/pixiv/tokens.json`(600) 与 `source/bilibili/cookies.txt`
|
||||||
7. **软链**:`ln -s /home/openapi/source/cache/pixiv/images /home/images`
|
7. **软链**:`ln -s /home/openapi/source/cache/pixiv/images /home/images`
|
||||||
@@ -137,6 +137,13 @@
|
|||||||
|
|
||||||
## 变更记录
|
## 变更记录
|
||||||
|
|
||||||
|
**2026-09-23(仓库与线上一致性核对 + 完整性补齐)**
|
||||||
|
|
||||||
|
- 逐文件 md5 核对:源码/main.py、systemd 全部 7 个单元、ginkgo/jm/bilibili/refresh 各模块、站点 nginx conf 均与仓库一致
|
||||||
|
- **补录漂移**:`source/pixiv/pixiv.py` 线上版为 2026-09-17 的"图源错误码处理"增强(官方 API 404/401/403/429 分别返回明确错误、`fetch_illust_url` 返回 api_status、不再对确定性错误做无意义备用源重试),此前未入库;本次以线上版为准补录
|
||||||
|
- **仓库补齐恢复依赖**:新增 `nginx/00-limits.conf`(限流 zone,站点 conf 引用 `perip_req`/`perip`,缺失会使 `nginx -t` 失败)与 `nginx/00-websocket.conf`(`$connection_upgrade` map,同样必须);迁移步骤第 4 步已同步
|
||||||
|
- 凭据快照另存于本机备份目录(`tokens.json`、`cookies.txt`,0600),不入库
|
||||||
|
|
||||||
**2026-09-23(dufe mode=auto + bot 基准同步)**
|
**2026-09-23(dufe mode=auto + bot 基准同步)**
|
||||||
|
|
||||||
- dufe 签到服务新增 **`&mode=auto`**(url 参数可省略):SSO 登录一次后复用登录态 → 拉取通知列表(`GET /student/message/getMessageAll` pageSize=20/readFlag=0 + `POST /student/busiMessage/list`)→ 按 bot ginkgo 同款规则发现活动(仅互动消息、文本含"签到"、jumpUrl 可解析为 `/course/detail/.../signin/<id>/<type>`,query 不入库,按 signInId 去重)→ 按时间序逐个自动签到
|
- dufe 签到服务新增 **`&mode=auto`**(url 参数可省略):SSO 登录一次后复用登录态 → 拉取通知列表(`GET /student/message/getMessageAll` pageSize=20/readFlag=0 + `POST /student/busiMessage/list`)→ 按 bot ginkgo 同款规则发现活动(仅互动消息、文本含"签到"、jumpUrl 可解析为 `/course/detail/.../signin/<id>/<type>`,query 不入库,按 signInId 去重)→ 按时间序逐个自动签到
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
limit_conn_zone $binary_remote_addr zone=perip:10m;
|
||||||
|
limit_conn_zone $server_name zone=perserver:10m;
|
||||||
|
|
||||||
|
# 2026-09-23 新增:按 IP 请求速率限制(轻防滥用档)
|
||||||
|
# openapi 域名 DNS 直连本机(无 CDN),$binary_remote_addr 即真实客户端 IP
|
||||||
|
limit_req_zone $binary_remote_addr zone=perip_req:10m rate=10r/s;
|
||||||
|
limit_req_status 429;
|
||||||
|
limit_conn_status 429;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
+25
-10
@@ -31,9 +31,11 @@ def get_access_token():
|
|||||||
|
|
||||||
|
|
||||||
def fetch_illust_url(pid, page):
|
def fetch_illust_url(pid, page):
|
||||||
|
"""返回 (original_url, api_status)。api_status: 200 成功 / 404 作品不存在 /
|
||||||
|
401|403 认证失败 / 429 限流 / 0 网络等临时错误 / -1 数据异常。"""
|
||||||
access_token = get_access_token()
|
access_token = get_access_token()
|
||||||
if not access_token:
|
if not access_token:
|
||||||
return None
|
return None, 403
|
||||||
try:
|
try:
|
||||||
resp = requests.get(
|
resp = requests.get(
|
||||||
f"https://{API_HOST}/v1/illust/detail",
|
f"https://{API_HOST}/v1/illust/detail",
|
||||||
@@ -47,16 +49,17 @@ def fetch_illust_url(pid, page):
|
|||||||
verify=False
|
verify=False
|
||||||
)
|
)
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
return None
|
return None, resp.status_code
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
if data.get("error"):
|
if data.get("error"):
|
||||||
return None
|
return None, -1
|
||||||
illust = data.get("illust")
|
illust = data.get("illust")
|
||||||
if not illust:
|
if not illust:
|
||||||
return None
|
return None, -1
|
||||||
page_count = illust.get("page_count", 1)
|
page_count = illust.get("page_count", 1)
|
||||||
if page >= page_count:
|
if page >= page_count:
|
||||||
return None
|
# 页码超出:作品存在但请求的分页不存在,按 404 语义处理
|
||||||
|
return None, 404
|
||||||
original_url = None
|
original_url = None
|
||||||
if page_count > 1:
|
if page_count > 1:
|
||||||
meta_pages = illust.get("meta_pages")
|
meta_pages = illust.get("meta_pages")
|
||||||
@@ -69,10 +72,12 @@ def fetch_illust_url(pid, page):
|
|||||||
if not original_url:
|
if not original_url:
|
||||||
original_url = illust.get("image_urls", {}).get("large")
|
original_url = illust.get("image_urls", {}).get("large")
|
||||||
if not original_url:
|
if not original_url:
|
||||||
return None
|
return None, -1
|
||||||
return original_url.replace("i.pximg.net", IMAGE_PROXY_DOMAIN)
|
return original_url.replace("i.pximg.net", IMAGE_PROXY_DOMAIN), 200
|
||||||
|
except requests.RequestException:
|
||||||
|
return None, 0 # 网络等临时错误:可继续尝试备用源
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None, -1
|
||||||
|
|
||||||
|
|
||||||
def make_re_url(pid, page):
|
def make_re_url(pid, page):
|
||||||
@@ -146,9 +151,19 @@ def serve_image(path):
|
|||||||
return send_file(cached_file, mimetype=mime, max_age=3600)
|
return send_file(cached_file, mimetype=mime, max_age=3600)
|
||||||
|
|
||||||
urls_to_try = []
|
urls_to_try = []
|
||||||
official_url = fetch_illust_url(pid, page)
|
official_url, api_status = fetch_illust_url(pid, page)
|
||||||
if official_url:
|
if official_url:
|
||||||
urls_to_try.append(official_url)
|
urls_to_try.append(official_url)
|
||||||
|
# 明确的作品级错误直接返回,不再做无意义的备用源重试:
|
||||||
|
# 404 = 作品不存在/已删除/分页不存在(官方 API 已裁决)
|
||||||
|
# 401/403 = token 失效(refresh 由独立服务负责,这里明确报错)
|
||||||
|
# 429 = pixiv API 限流(备用源同样是 pixiv 数据,重试无意义)
|
||||||
|
if api_status == 404:
|
||||||
|
return jsonify(error="作品不存在或已删除", code="not_found"), 404
|
||||||
|
if api_status in (401, 403):
|
||||||
|
return jsonify(error="pixiv 认证失败,token 可能失效", code="auth_error"), 502
|
||||||
|
if api_status == 429:
|
||||||
|
return jsonify(error="pixiv API 限流,请稍后再试", code="rate_limited"), 503
|
||||||
urls_to_try.append(make_re_url(pid, page))
|
urls_to_try.append(make_re_url(pid, page))
|
||||||
urls_to_try.append(make_cat_url(pid, page))
|
urls_to_try.append(make_cat_url(pid, page))
|
||||||
|
|
||||||
@@ -157,7 +172,7 @@ def serve_image(path):
|
|||||||
if img_path:
|
if img_path:
|
||||||
return send_file(img_path, mimetype=mime, max_age=3600)
|
return send_file(img_path, mimetype=mime, max_age=3600)
|
||||||
|
|
||||||
return jsonify(error="Failed to fetch image from all sources"), 500
|
return jsonify(error="所有图源均取图失败,请稍后再试", code="all_sources_failed"), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route('/health')
|
@app.route('/health')
|
||||||
|
|||||||
Reference in New Issue
Block a user