Files
openapi/README.md
T
RRRosmontis 4dee7a3c0c 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/ 三件套; 记录一致性核对结果
2026-10-21 18:56:58 +08:00

157 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenAPI
统一接口服务。线上入口:`https://openapi.rrrosmontis.icu`
聚合层把 **pixiv 图片服务**、**bilibili 视频服务**、**dufe 银杏签到码查询服务**与 **jm 漫画下载服务**收敛到单一对外入口。五个后端进程全部只监听 `127.0.0.1`,对外仅由 nginx 暴露 80/443——即"一个入口、零暴露端口"。
## 架构
```
公网 443/80 nginx (server_name openapi.rrrosmontis.icu)
├── /pixiv/get_image?id=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9007 pixiv
├── /bilibili/get_video?bv=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9006 bilibili
├── /dufe/ginkgostu_sign_in -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9005 dufe 签到码查询(GET|POST)
├── /jm/get_comic?id=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9004 jm
├── /img-master/ /img-original/ -> nginx 直接反代 i.pximg.net(无本地后端)
└── / -> 127.0.0.1:9008(302 跳转文章页)
```
## 端口与进程
| 服务 | 监听 | 入口源码 | 说明 |
|---|---|---|---|
| openapi | `127.0.0.1:9008` | `source/main.py`(aiohttp) | 聚合层,纯转发,**唯一入口** |
| pixiv | `127.0.0.1:9007` | `source/pixiv/pixiv.py`(Flask + waitress) | 图片代理 |
| bilibili | `127.0.0.1:9006` | `source/bilibili/bilibili.py`(aiohttp + yt-dlp) | 视频代理 |
| dufe 签到 | `127.0.0.1:9005` | `source/dufe/ginkgostu_sign_in/ginkgo_sign_in.py`(Flask + waitress 16 线程) | 银杏(DUFE)签到码查询,`GET\|POST /dufe/ginkgostu_sign_in`;`&mode=auto` 时拉取通知列表自动签到(与 bot ginkgo 同逻辑,连续重试,结果列表聚合返回) |
| jm | `127.0.0.1:9004` | `source/jm/jm.py`(aiohttp + jmcomic) | 漫画下载打包(移植自 qqbot jm 模块),`GET /jm/get_comic?id=xxx[&range=起-止]` |
| pixiv-token-refresh | — | `source/pixiv/refresh_token.py` | token 刷新守护(每 30–60 分钟随机) |
| bilibili-refresh | — | `source/bilibili/refresh_cookie.py` | cookie 刷新守护(每 50–70 分钟随机) |
> 历史端口 `9888 / 9088 / 19990` 已于 2026-09-12 废弃,全部改为上述 `900x`。
> pixiv 早期误绑 `0.0.0.0:9088` 对公网裸奔(绕过 nginx 的日志与限流),现已收口为 `127.0.0.1`。
## 结构
```
source/
├── main.py # 聚合层 (127.0.0.1:9008),五条路由:/ /pixiv/get_image /bilibili/get_video /dufe/ginkgostu_sign_in /jm/get_comic
├── pixiv/
│ ├── pixiv.py # Pixiv 图片服务 (127.0.0.1:9007)
│ ├── refresh_token.py # Pixiv OAuth token 刷新守护
│ └── tokens.template.json # token 模板(实际 tokens.json 不入库)
├── bilibili/
│ ├── bilibili.py # bilibili 视频服务 (127.0.0.1:9006)
│ ├── refresh_cookie.py # cookie 定时刷新守护
│ ├── qr_login.py # 扫码登录获取 cookie
│ └── qr_login_fast.py
├── dufe/
│ └── ginkgostu_sign_in/
│ └── ginkgo_sign_in.py # 银杏(DUFE)签到码查询 (127.0.0.1:9005, GET|POST)
├── jm/
│ └── jm.py # JM 漫画下载打包服务 (127.0.0.1:9004)
└── cache/ # 运行时生成,不入库
├── pixiv/images # 图片缓存
├── bilibili/ # 视频缓存
└── jm/ # jm 临时 ZIP(TTL 10 分钟,返回后删除)
nginx/
├── 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/
├── openapi.service # 聚合层
├── pixiv.service # Pixiv 图片服务(waitress --threads=8)
├── bilibili.service # bilibili 视频服务
├── bilibili-refresh.service # cookie 刷新守护
├── pixiv-token-refresh.service # Pixiv token 刷新守护
├── ginkgo_sign_in.service # 银杏(DUFE)签到码查询服务
└── jm.service # JM 漫画下载打包服务
docs/
└── 部署与运维.md # 线上服务器的路径、缓存、迁移步骤与变更记录
```
## 缓存机制
**pixiv(图片)**:`check_cache()` 以 glob 命中即直接回文件;未命中按 `官方 App API → pixiv.re → pixiv.cat` 三级回退抓取,落盘用 `.tmp` + `os.replace` 原子写。
缓存目录不再硬编码,由模块位置推导:
```python
IMAGE_DIR = (Path(__file__).resolve().parent / "../cache/pixiv/images").resolve()
IMAGE_DIR.mkdir(parents=True, exist_ok=True)
```
即 `source/cache/pixiv/images`;线上常用 `/home/images` 软链指向它,便于访问。
**bilibili(视频)**:以 **BV 号为键**缓存到 `source/cache/bilibili/{BV}.mp4`
- 命中即直接 `FileResponse`,**不再调用 yt-dlp**
- 未命中才下载,且先落到缓存目录内的隐藏暂存目录 `.dl-XXXX`,成功后 `os.replace` 原子落盘
- **无论成功 / 失败 / 超时都会 `shutil.rmtree(tmpdir)`** —— 早期实现只清理 cookie 临时文件、从不清理视频暂存目录,导致每次取视频都把完整视频永久留在 `/tmp`(无上限磁盘泄漏),已修复
- 淘汰策略:单文件 `CACHE_TTL_SECONDS`(默认 7 天)未访问即过期;总量超过 `CACHE_MAX_BYTES`(默认 2 GiB)时按 mtime 从最旧开始删除
- 命中/落盘会在日志打印 `cache HIT / MISS / STORE`(已 `flush=True`,实时可见)
运维接口(只绑本机):
```bash
curl -s 127.0.0.1:9006/bilibili/cache_info
# {cache_dir, cache_max_bytes, cache_ttl_seconds, count, total_bytes, free_bytes, files[]}
```
**jm(漫画)**:**非持久缓存**,与 bilibili 不同——ZIP 仅作临时产物
- 请求 `id`(+ 可选 `range=起-止`,单次 ≤30 章)→ jmcomic 下载到 `cache/jm/` 内隐藏暂存目录 `.dl-<id>-XXXX` → webp 统一转 jpg 打包 ZIP(`{标题}_{id}_ch{起}-{止}.zip`)→ HTTP 返回
- 打包完成即 `rmtree` 图片暂存目录;ZIP 在返回后 `ZIP_TTL_SECONDS`(默认 600 秒)延时删除
- TTL 窗口内重复请求同一 ZIP 直接复用并重新计时;每次请求附带 `_cache_sweep()` 兜底清理过期 ZIP(防进程重启残留)
- 同一本子串行处理(per-album asyncio.Lock),并发请求同本返回 409
- 运维接口:`curl -s 127.0.0.1:9004/jm/cache_info`
## 部署
1. **Python 环境**:各服务共用一个环境,需含 `flask`、`waitress`、`aiohttp`、`httpx`、`yt-dlp`、`jmcomic`、`pillow`
2. **凭据**:
- 复制 `source/pixiv/tokens.template.json` 为 `source/pixiv/tokens.json`(`chmod 600`)并填入实际 pixiv token
- 放置 `source/bilibili/cookies.txt`(`name=value` 格式),或运行 `qr_login.py` 扫码生成
- jm 无需凭据,但要求部署机可直连源站(jmcomic 会自动更新 API 域名)
3. **服务**:将 `systemd/*.service` 安装到 `/etc/systemd/system/`,然后
`systemctl daemon-reload && systemctl enable --now openapi pixiv bilibili ginkgo_sign_in jm bilibili-refresh pixiv-token-refresh`
4. **nginx**:将 `nginx/openapi.rrrosmontis.icu.conf` 放入 `/etc/nginx/conf.d/`(需存在 `$connection_upgrade` 的 map 定义),`nginx -t && systemctl reload nginx`
5. **软链**(可选):`ln -s <部署根>/source/cache/pixiv/images /home/images`
6. **定时任务**:无 cron/timer——两个刷新守护均为常驻循环(内部随机间隔),systemd `Restart=always` 兜底
## 验证
```bash
curl -k "https://openapi.rrrosmontis.icu/pixiv/get_image?id=147113746"
curl -k "https://openapi.rrrosmontis.icu/bilibili/get_video?bv=BV16a41157Q4"
curl -kI "https://openapi.rrrosmontis.icu/bilibili/get_video?bv=BV16a41157Q4" # 只看头部,省流量
curl -k "https://openapi.rrrosmontis.icu/dufe/ginkgostu_sign_in" # 无参应返回 400 JSON
curl -k "https://openapi.rrrosmontis.icu/jm/get_comic?id=350234" -o jm.zip # 返回 ZIP
curl -s 127.0.0.1:9006/bilibili/cache_info
curl -s 127.0.0.1:9004/jm/cache_info
```
端口暴露自检(五个后端都应"不可达",仅 443/80 通):
```bash
for p in 9004 9005 9006 9007 9008; do timeout 3 bash -c "cat < /dev/null > /dev/tcp/<host>/$p" 2>/dev/null \
&& echo "$p 可连通(异常)" || echo "$p 不可达(正常)"; done
```
## 版本要点
- **2026-09-23**
- dufe 签到服务新增 **`mode=auto`**:拉取通知列表(getMessageAll + busiMessage/list)→ 按 bot ginkgo 同款规则发现签到活动(互动消息 + 文本含"签到" + 可解析课程链接,按 signInId 去重)→ 逐个自动签到;**连续重试**(首次 + 最多 5 次,间隔 3s,对齐 bot `MAX_SIGNIN_ATTEMPTS=6 / SIGNIN_BACKOFF`;token 疑似失效时重登录一次立即重试,对齐 bot refresh 语义);**所有结果聚合为 `data.results` 列表**返回(每活动含 success/message/attempts/title/signInCode 等)
- 非签到码签到逻辑与 bot ginkgo 比对后以 bot 为基准同步:移植限流防护体系(`check_response` / `is_rate_limited` / `safe_error` / `_LoginErrors`)、`parse_course_url` 的 courseId 放宽至 `[0-9A-Za-z]+`、`commit_sign_in` 的错误包装、`fetch_sign_in_info` 的 check_response 与外部登录态注入(`sess`/`token` 参数,auto 模式请求内复用同一登录态)
- **2026-09-23**(早些):新增 **jm 漫画下载打包服务**(`source/jm/jm.py`,aiohttp + jmcomic,`127.0.0.1:9004`,端点 `GET /jm/get_comic?id=xxx[&range=起-止]`),移植自 qqbot 的 jm 模块(去掉 QQ/代理轮换/上传 cf 逻辑,改为 HTTP 直返 ZIP;hk 可直连源站无需代理)。ZIP 为临时产物:返回后 600 秒延时删除,TTL 窗口内同名请求直接复用。聚合层 `main.py` 注册路由(新增 `JM_BACKEND`,复用既有 `proxy()`),nginx 无需改动(`location /` 兜底)
- **2026-09-13**:新增 **dufe 银杏签到码查询服务**(`source/dufe/ginkgostu_sign_in/ginkgo_sign_in.py`,Flask + waitress 16 线程,`127.0.0.1:9005`,端点 `GET|POST /dufe/ginkgostu_sign_in`),并在聚合层 `main.py` 注册该路由——为此新增 `proxy_forward()`(保留请求方法与请求体,原 `proxy()` 保持固定 GET 不变,零改动既有行为)。同期修复该服务两处缺陷:base64 参数补 `urllib.parse.unquote`(兼容 iOS 快捷指令的 `%3D` 编码),`commit` 判定改为 fail-closed(须 `commit.success is True` 才算签到成功)
- **2026-09-12**
- 端口重排 `9888/9088/19990 → 9008/9007/9006`,全部仅绑 `127.0.0.1`;pixiv 由 `0.0.0.0:9088` 收口
- 主入口 `main.py` 从 `source/openapi/` 上移到 `source/`,systemd `WorkingDirectory` 同步为 `source/`
- pixiv 缓存路径由硬编码 `/home/pixiv/images` 改为相对 `../cache/pixiv/images`
- pixiv 改由 **waitress 线程池**承载(`--threads=8`),替代 Flask 开发服务器(thread-per-request 无上限)
- bilibili 新增 **BV 真缓存**(`../cache/bilibili`,2 GiB / 7 天淘汰),并修复视频暂存目录永不清理导致的 `/tmp` 泄漏
- 新增 `/bilibili/cache_info` 运维接口;缓存日志加 `flush=True`
- **2026-09-02**:新增 pixiv token 刷新守护;补 `/img-original/` 反代(App API 原图路径)
- **2026-07-31**:三服务迁移落位(Python 环境合并为单一 `general`)