From 1b2071d78bfe47891435501560bcb4bc8e2b4a47 Mon Sep 17 00:00:00 2001 From: RRRosmontis Date: Sun, 13 Sep 2026 02:26:35 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20README=20=E4=B8=8E?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E9=83=A8=E7=BD=B2=E8=BF=90=E7=BB=B4=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=EF=BC=8C=E6=96=B0=E5=A2=9E=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: 新架构图/端口表/目录结构/双缓存机制/waitress/部署与验证步骤/版本要点 - docs/部署与运维.md: 线上服务器的实际路径、缓存策略、迁移 8 步、日志与变更记录 - .gitignore: 忽略 source/cache/、tokens.json、cookies.txt、__pycache__ --- .gitignore | 14 +++++ README.md | 130 ++++++++++++++++++++++++++++++++++++++------- docs/部署与运维.md | 127 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 docs/部署与运维.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8860178 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# 运行时缓存(不入库) +source/cache/ + +# 凭据与密钥 +source/pixiv/tokens.json +source/bilibili/cookies.txt + +# Python +__pycache__/ +*.pyc + +# 杂项 +*.swp +.DS_Store diff --git a/README.md b/README.md index 3f43a2b..05f96f6 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,124 @@ 统一接口服务。线上入口:`https://openapi.rrrosmontis.icu` +聚合层把 **pixiv 图片服务**与 **bilibili 视频服务**收敛到单一对外入口。三个后端进程全部只监听 `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 + ├── /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) | 视频代理 | +| 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/ -├── openapi/main.py # 聚合层 (127.0.0.1:9888) /pixiv/get_image /bilibili/get_video +├── main.py # 聚合层 (127.0.0.1:9008),三条路由:/ /pixiv/get_image /bilibili/get_video ├── pixiv/ -│ ├── pixiv.py # Pixiv 图片服务 (9088) -│ ├── refresh_token.py # Pixiv OAuth token 刷新守护 -│ └── tokens.template.json # Pixiv token 模板 -└── bilibili/ - ├── bilibili.py # bilibili 视频服务 (19990) - ├── refresh_cookie.py # cookie 定时刷新守护 - ├── qr_login.py # 扫码登录获取 cookie - └── qr_login_fast.py +│ ├── 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 +└── cache/ # 运行时生成,不入库 + ├── pixiv/images # 图片缓存 + └── bilibili/ # 视频缓存 nginx/ -└── openapi.rrrosmontis.icu.conf # nginx 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/ 图片反代) +└── openapi.rrrosmontis.icu.conf # 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/) systemd/ -├── openapi.service # 聚合层 -├── pixiv.service # Pixiv 图片服务 -├── bilibili.service # bilibili 视频服务 -├── bilibili-refresh.service # cookie 刷新守护(每 50–70 分钟随机刷新) -└── pixiv-token-refresh.service # Pixiv token 刷新守护(每 30–60 分钟随机刷新) +├── openapi.service # 聚合层 +├── pixiv.service # Pixiv 图片服务(waitress --threads=8) +├── bilibili.service # bilibili 视频服务 +├── bilibili-refresh.service # cookie 刷新守护 +└── pixiv-token-refresh.service # Pixiv token 刷新守护 +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[]} ``` ## 部署 -1. token:复制 `source/pixiv/tokens.template.json` 为 `source/pixiv/tokens.json`,填入实际 pixiv token -2. bilibili cookie:放置 `source/bilibili/cookies.txt`(`name=value` 格式),或运行 `qr_login.py` 扫码生成 -3. 服务:将 `systemd/*.service` 安装到 `/etc/systemd/system/`,启用并启动 openapi / pixiv / bilibili / bilibili-refresh / pixiv-token-refresh(`systemctl enable --now`) -4. nginx:将 `nginx/openapi.rrrosmontis.icu.conf` 放入 `/etc/nginx/conf.d/`,`nginx -t && systemctl reload nginx` -5. 定时任务:无 cron/timer——bilibili-refresh(每 50–70 分钟刷 cookie)与 pixiv-token-refresh(每 30–60 分钟刷 Pixiv OAuth token,轮换后的 refresh_token 原子落盘)均为常驻守护,内部循环随机间隔,systemd `Restart=always` 兜底 +1. **Python 环境**:三服务共用一个环境,需含 `flask`、`waitress`、`aiohttp`、`httpx`、`yt-dlp` +2. **凭据**: + - 复制 `source/pixiv/tokens.template.json` 为 `source/pixiv/tokens.json`(`chmod 600`)并填入实际 pixiv token + - 放置 `source/bilibili/cookies.txt`(`name=value` 格式),或运行 `qr_login.py` 扫码生成 +3. **服务**:将 `systemd/*.service` 安装到 `/etc/systemd/system/`,然后 + `systemctl daemon-reload && systemctl enable --now openapi pixiv bilibili 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 -s 127.0.0.1:9006/bilibili/cache_info +``` + +端口暴露自检(三个后端都应"不可达",仅 443/80 通): + +```bash +for p in 9006 9007 9008; do timeout 3 bash -c "cat < /dev/null > /dev/tcp//$p" 2>/dev/null \ + && echo "$p 可连通(异常)" || echo "$p 不可达(正常)"; done +``` + +## 版本要点 + +- **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`) diff --git a/docs/部署与运维.md b/docs/部署与运维.md new file mode 100644 index 0000000..86befb8 --- /dev/null +++ b/docs/部署与运维.md @@ -0,0 +1,127 @@ +# 线上部署与运维(hk) + +> 记录线上服务器 `hk`(`103.24.218.119:10526`)的实际路径、缓存策略与迁移步骤。 +> 架构与代码结构见根目录 [README](../README.md)。 + +## 对外入口 + +统一入口:`https://openapi.rrrosmontis.icu`(nginx 监听 80/443) + +| 路径 | 链路 | +|---|---| +| `/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) | +| `/img-master/`、`/img-original/` | nginx 直接反代 `i.pximg.net`(无本地后端) | +| `/` | `127.0.0.1:9008`(302 跳转文章页) | + +- 其余在服站点:`sqy.bu4.ren`、`xxx.aeolianchi.me` +- 已删除的 vhost:`kim1.work`、`zxx.aeolianchi.me`(2026-09-12) +- 历史域名 `api.bu4.ren` / `pixiv.bu4.ren` / `apixiv.bu4.ren` 已合并进 `openapi.rrrosmontis.icu` + +## 进程与端口 + +三个服务**全部只绑 `127.0.0.1`**,对外仅 nginx 的 443/80。 + +| systemd 单元 | 源码 | 监听 | 说明 | +|---|---|---|---| +| `openapi.service` | `source/main.py` | `:9008` | 聚合层(aiohttp,纯转发,唯一入口) | +| `pixiv.service` | `source/pixiv/pixiv.py` | `:9007` | Flask,由 waitress(8 线程)承载 | +| `bilibili.service` | `source/bilibili/bilibili.py` | `:9006` | aiohttp + yt-dlp | +| `pixiv-token-refresh.service` | `source/pixiv/refresh_token.py` | — | 常驻刷新 Pixiv OAuth token | +| `bilibili-refresh.service` | `source/bilibili/refresh_cookie.py` | — | 常驻刷新 bilibili cookie | + +## 目录结构 + +``` +/home/openapi/ +├── source/ +│ ├── main.py 聚合层源码(2026-09-12 从 source/openapi/ 上移至此) +│ ├── pixiv/ +│ │ ├── pixiv.py 图像代理(Flask) +│ │ ├── refresh_token.py token 刷新守护 +│ │ └── tokens.json Pixiv OAuth token(敏感,权限 600,不入库) +│ ├── bilibili/ +│ │ ├── bilibili.py 视频代理(aiohttp + yt-dlp) +│ │ ├── cookies.txt bilibili cookie(敏感,不入库) +│ │ ├── refresh_cookie.py cookie 刷新守护 +│ │ └── qr_login*.py 扫码登录工具(cookie 失效时人工重登用) +│ └── cache/ 运行时缓存(不入库) +│ ├── pixiv/images pixiv 图片缓存(代码中为 ../cache/pixiv/images) +│ └── bilibili/ bilibili 视频缓存(上限 2 GiB / TTL 7 天) +├── systemd/ 5 个单元文件的归档副本(与 /etc/systemd/system/ 保持一致) +├── nginx/ 宝塔导出的历史存档,非生效路径 +├── cert/ SSL 证书 +├── env/ Python 环境(general + env_general.tar.gz 整包) +└── 备份说明.txt 本文件的历史版本(旧端口/旧路径,仅供参考) + +/home/images -> /home/openapi/source/cache/pixiv/images 便捷软链 +``` + +## 缓存机制 + +**pixiv(图片)**:`check_cache()` 以 glob 命中即直接回文件;未命中按 `官方 App API → pixiv.re → pixiv.cat` 三级回退抓取,落盘用 `.tmp` + `os.replace` 原子写。 + +**bilibili(视频)**:以 BV 号为键缓存到 `source/cache/bilibili/{BV}.mp4` + +- 命中即复用、不再下载 +- 未命中才调 yt-dlp,先下载到缓存目录内的隐藏暂存目录 `.dl-XXXX`,成功后原子 rename 落盘 +- 无论成功 / 失败 / 超时都 `rmtree` 该暂存目录 + (2026-09-12 修复:原实现从不清理 tmpdir,每次取视频都会把完整视频永久留在 `/tmp`,属无上限磁盘泄漏) +- 淘汰策略:单文件 7 天未访问过期 + 总量超过 2 GiB 时按 mtime 从最旧开始删 +- 运维接口:`curl -s 127.0.0.1:9006/bilibili/cache_info` + +## nginx + +| 文件 | 作用 | +|---|---| +| `/etc/nginx/conf.d/openapi.rrrosmontis.icu.conf` | 统一入口站点(`proxy_pass` → `127.0.0.1:9008`) | +| `/etc/nginx/conf.d/00-websocket.conf` | `$connection_upgrade` map 定义(**必须存在**) | +| `/home/openapi/nginx/**` | 宝塔导出的历史存档,非生效路径 | + +## Python 环境 + +- `/home/openapi/env/general` —— 三服务共用(flask / waitress / aiohttp / httpx / yt-dlp) +- `/home/openapi/env/env_general.tar.gz` —— 环境整包(迁移用) +- 注意:pixiv 现由 **waitress** 承载(`pip install waitress`,纯 Python 线程池),不再使用 Flask 开发服务器 + (开发服务器是 thread-per-request 无上限,受控线程池更适合长期运行) + +## 迁移到新服务器 + +1. **环境**:解压 `env/env_general.tar.gz`,直接使用其 `bin/python`(依赖齐全,含 waitress) +2. **源码**:恢复 `source/{main.py,pixiv/,bilibili/}` 到 `/home/openapi/source/` +3. **systemd**:恢复 `systemd/*.service` 到 `/etc/systemd/system/`,`daemon-reload` + `enable` + `start` +4. **nginx**:套用 `conf.d/openapi.rrrosmontis.icu.conf`(并确保 `00-websocket.conf` 的 map 存在) +5. **SSL**:恢复 `cert/<域名>/` 到 `/etc/nginx/cert/<域名>/` +6. **凭据**:放回 `source/pixiv/tokens.json`(600) 与 `source/bilibili/cookies.txt` +7. **软链**:`ln -s /home/openapi/source/cache/pixiv/images /home/images` +8. **验证**: + ```bash + curl -k "https://openapi.rrrosmontis.icu/pixiv/get_image?id=xxx" + curl -k "https://openapi.rrrosmontis.icu/bilibili/get_video?bv=BV16a41157Q4" + ``` + +## 日志 + +``` +/var/log/openapi.log /var/log/pixiv.log /var/log/bilibili.log +/var/log/bilibili_refresh.log /var/log/pixiv_token_refresh.log +``` + +缓存命中/落盘会在 `bilibili.log` 打印 `cache HIT / MISS / STORE`(已加 `flush`,实时可见)。 + +## 变更记录 + +**2026-09-12** + +- 端口重排 `9888 / 9088 / 19990 → 9008 / 9007 / 9006`(全部仅 `127.0.0.1`) +- pixiv 曾误绑 `0.0.0.0:9088` 对外裸奔(绕过 nginx 日志与限流),已收口为 `127.0.0.1` +- 主入口 `main.py` 由 `source/openapi/` 上移到 `source/`;systemd `WorkingDirectory` 同步 +- pixiv 的 `IMAGE_DIR` 由硬编码 `/home/pixiv/images` 改为相对 `../cache/pixiv/images`,并建 `/home/images` 软链 +- bilibili 新增真缓存(`../cache/bilibili`)并修复 `/tmp` 泄漏,新增 `cache_info` 接口 +- pixiv 改用 waitress 8 线程 +- 删除 `kim1.work` / `zxx.aeolianchi.me` 两个 vhost +- 清理历史 `.bak` / `__pycache__` / 空日志等冗余文件 + +**2026-09-02** 新增 `pixiv-token-refresh`;补 `/img-original/` 反代(App API 原图路径) + +**2026-07-31** 三服务迁移落位(Python 环境合并为单一 `general`)