feat: 检索页聚合搜索,修复中文关键词导致主进程崩溃

一次查询所有已启用数据源并按源分组展示,每组最多 5 条,
超出时提供「查看更多」跳转到该源的单源检索。

同时修复聚合并发暴露出的网络层问题:

- 主进程崩溃:Memory of the World 会把搜索关键词原样回写进
  ETag 响应头,中文关键词下 Electron net.fetch 在内部 emit
  回调里抛 ByteString TypeError,await 无法捕获,主进程直接
  崩溃且 Promise 永不 settle。现拦截该类异常并回退到 undici。
- OpenLibrary / LibGen 要求关键词至少 3 字符,否则返回 422。
  改为前置校验,直接返回空结果加说明,耗时从 14s 降到 3ms。
- 重试只针对可自愈错误(超时/5xx/限流),不再为 4xx 白等;
  重试时收紧超时,避免最坏耗时翻倍。镜像轮询类源关闭内层
  重试,换镜像交给 tryMirrors/raceMirrors。
- 网络错误文案改为可读提示,不再把整条 URL 抛给用户。
- Sci-Hub 非 DOI 关键词返回空而非报错,避免聚合结果刷屏。

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-07-26 11:59:40 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent 1a1288ce18
commit de3e1d8a44
10 changed files with 484 additions and 72 deletions
+138
View File
@@ -0,0 +1,138 @@
# PeopleLib
开放获取文献与图书的桌面客户端(Electron)。在一个界面里检索多个公开文献源,查看详情,下载文件并归入本地书库。
## 功能
- **多源检索**:12 个数据源统一的搜索、详情、下载流程
- **本地书库**:收藏条目、下载文件、封面缓存、阅读状态管理
- **全局代理**:一处配置,对所有数据源与封面请求生效
- **镜像故障转移**:镜像失效自动切换,恢复后自动重新启用
- **Z-Library 登录**:凭据本地保存,会话过期自动重新登录
## 数据源
| 源 | ID | 说明 |
|---|---|---|
| arXiv 论文 | `arxiv` | 预印本,支持全文检索 |
| Gutenberg 公版书 | `gutenberg` | 公共领域图书 |
| Open Library 图书 | `openlibrary` | 图书元数据与借阅入口 |
| DOAJ 开放期刊 | `doaj` | 开放获取期刊论文 |
| PMC 生物医学 | `pmc` | PubMed Central 全文 |
| bioRxiv 预印本 | `biorxiv` | 仅浏览最新列表,不支持关键词搜索 |
| Standard Ebooks | `standardebooks` | 精校排版的公版电子书 |
| Semantic Scholar | `semanticscholar` | 学术论文检索 |
| Memory of the World | `motw` | 公共图书馆藏书 |
| Library Genesis | `libgen` | 图书检索,下载需站点账号 |
| Z-Library | `zlib` | 需登录,可获取下载直链 |
| Sci-Hub(按 DOI | `scihub` | 按 DOI 查询 |
数据源的可用性取决于站点自身状态。部分站点(如 Sci-Hub)启用了人机验证,程序会给出明确提示而非静默失败。
## 环境要求
- Node.js 18+
- Windows / macOS / Linux
## 快速开始
```bash
npm install
npm start
```
## 打包
生成 Windows 免安装版到 `dist/`
```bash
npm run portable
```
## 配置
### 代理
在应用内「设置」填写代理地址,例如 `http://127.0.0.1:7897`。留空表示直连。配置会持久化,重启后仍生效,并应用于所有数据源请求与封面加载。
网络受限环境下,多数数据源需要代理才能访问。
### Z-Library 账号
「设置」中填入邮箱与密码即可登录。登录后可获取下载直链(免费账号有每日下载额度限制)。
> 凭据以 base64 混淆后保存在本地 `zlib-auth.json`**这只是防止肉眼直读,不是加密**。请勿在不受信任的机器上使用。
## 数据位置
| 模式 | 路径 |
|---|---|
| 开发运行 | `%APPDATA%/PeopleLib`Windows |
| 打包运行 | 可执行文件同级的 `data/` 目录 |
该目录包含:
- `library.json` 书库索引
- `settings.json` 应用设置(代理等)
- `zlib-auth.json` Z-Library 凭据与会话
- `covers/` 封面缓存
## 项目结构
```
main.js 主进程:窗口、IPC、代理与证书处理、文件下载
preload.js 渲染进程 API 桥接
src/
settings.js 设置持久化
library/store.js 书库存储
sources/
index.js 数据源注册表
http.js 统一 HTTP 层:超时、Cookie、代理
mirror.js 镜像故障转移
zlib-auth.js Z-Library 凭据存储
<source>.js 各数据源实现
ui/ 渲染进程界面
```
### 数据源接口
每个数据源模块导出以下结构:
```js
module.exports = {
id: 'example',
name: '示例源',
supportsSearch: true,
async list(page) // 浏览:{ items, maxPage, page }
async search(keyword, page) // 搜索:{ items, maxPage, page }
async detail(postId) // 详情:{ title, authors, cover, tags, brief, links }
async download(postId) // 下载:{ files, links }
};
```
新增数据源只需实现该接口,并在 `src/sources/index.js` 中注册。
### 网络层说明
- 主进程使用 Electron 的 `net.fetch`(Chromium 网络栈),代理通过 `session.setProxy` 生效
- 纯 Node 环境回退到 undici `ProxyAgent`(Electron 下不启用,该组合存在兼容问题)
- 所有请求默认 15 秒超时,可按调用点覆盖
- 失效镜像进入 5 分钟冷却,之后自动重试,避免站点恢复后被永久跳过
- **应用启动时全局忽略 TLS 证书错误**。这是为了兼容大量使用自签名或过期证书的镜像站点,代价是失去对中间人攻击的防护,请仅在受信任的网络环境中使用。
## 测试
```bash
npx electron test-search.js --proxy http://127.0.0.1:7897
```
对主要数据源依次执行搜索、详情、下载链路,输出每一步耗时与结果。
## 免责声明
本项目仅是对公开网络接口的客户端封装,不托管、不分发任何内容。部分数据源所提供作品的版权状态因司法辖区而异,使用者需自行确保其使用方式符合当地法律与各站点的服务条款。
## 许可
MIT