rssany 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +28 -50
  2. package/dist/index.js +17 -5
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/webui/build/200.html +6 -6
  6. package/webui/build/_app/immutable/assets/0.DjU2hdCQ.css +1 -0
  7. package/webui/build/_app/immutable/chunks/{DcAshVxe.js → C85CNwD2.js} +1 -1
  8. package/webui/build/_app/immutable/chunks/{BXCWEhUd.js → CdMsRjxJ.js} +1 -1
  9. package/webui/build/_app/immutable/chunks/{EIZIMsXK.js → CllQAdvt.js} +1 -1
  10. package/webui/build/_app/immutable/chunks/{CkUAV0m0.js → Dv1VCsiB.js} +1 -1
  11. package/webui/build/_app/immutable/entry/{app.DdgnooOk.js → app.BcD2eSsQ.js} +2 -2
  12. package/webui/build/_app/immutable/entry/start.CbkdJdz1.js +1 -0
  13. package/webui/build/_app/immutable/nodes/{0.BE05Cuc4.js → 0.DSUDmOx2.js} +1 -1
  14. package/webui/build/_app/immutable/nodes/{1.5DFDaT4c.js → 1.DU9aYGAb.js} +1 -1
  15. package/webui/build/_app/immutable/nodes/{10.OVK4i9XE.js → 10.Db6vw7Ih.js} +1 -1
  16. package/webui/build/_app/immutable/nodes/{11.Dhn_rO4A.js → 11.BaAcorz3.js} +1 -1
  17. package/webui/build/_app/immutable/nodes/{14.B_KpJLxn.js → 14.DqT4pcrQ.js} +1 -1
  18. package/webui/build/_app/immutable/nodes/{15.RaWaA-0I.js → 15.CCLbjxnH.js} +1 -1
  19. package/webui/build/_app/immutable/nodes/{16.DSUgqolV.js → 16.DiigpVdP.js} +1 -1
  20. package/webui/build/_app/immutable/nodes/{3.wQvGs9w-.js → 3.DEcYOQc-.js} +1 -1
  21. package/webui/build/_app/immutable/nodes/{5.CCtn90c0.js → 5.CvM1TkLG.js} +1 -1
  22. package/webui/build/_app/immutable/nodes/{6.C2_mjW1u.js → 6.Dscr6LkS.js} +1 -1
  23. package/webui/build/_app/immutable/nodes/{7.Dwz6W7A1.js → 7.Bp60MobD.js} +1 -1
  24. package/webui/build/_app/immutable/nodes/{8.DzkEw6rx.js → 8.DwSg0MHh.js} +1 -1
  25. package/webui/build/_app/immutable/nodes/{9.DtlXEwe1.js → 9.BeYOUjxR.js} +1 -1
  26. package/webui/build/_app/version.json +1 -1
  27. package/webui/build/_app/immutable/assets/0.C6Q_nuW9.css +0 -1
  28. package/webui/build/_app/immutable/entry/start.DhJaJZhR.js +0 -1
package/README.md CHANGED
@@ -14,13 +14,12 @@
14
14
  ## 功能概览
15
15
 
16
16
  - **统一订阅**:在 `.rssany/sources.json` 中配置网站列表、标准 RSS、IMAP 邮件等,由调度器按 `refresh` 策略拉取。
17
- - **可插拔信源**:`app/plugins/builtin/` 与 `.rssany/plugins/` 中的 **Site** 插件(`.rssany.js` / `.rssany.ts`),自定义列表解析与详情规则。
17
+ - **可插拔信源**:**Site / Source** 插件(`.rssany.js` / `.rssany.ts`),见 **[插件配置说明](./docs/plugins.md)**。
18
18
  - **正文与解析**:在信源 `fetchItems`(及需要的 `ctx.extractItem` 等)内完成;入库后跑 pipeline。
19
19
  - **固定 pipeline**:`app/pipeline/` 中打标签、翻译等,由 `.rssany/config.json` 的 `pipeline.steps` 开关(**不是**用户目录下的 pipeline 插件)。
20
20
  - **LLM 辅助**:解析、提取、标签、翻译等可按配置走 OpenAI 兼容接口。
21
21
  - **站点登录**:需登录的站点通过 Puppeteer 管理 Cookie(与产品用户账号无关)。
22
22
  - **可选远端投递**:若 `config.json` 中 `**deliver.url`** 非空,在写库与 pipeline 完成后将条目以 `**{ sourceRef, items }**` JSON **POST** 到该 URL(由 `app/deliver/post.ts` 发送);留空则仅本地消费。
23
- - **MCP**:条目检索等能力以 MCP 暴露,供 Cursor、Claude 等使用。
24
23
  - **Web 界面**:SvelteKit 构建产物由后端托管;**Feeds** 等需 **邮箱校验**;`**/admin`** 需 `**users.role === 'admin'**`(可从 `**/me**` 进入)。
25
24
 
26
25
  ---
@@ -42,82 +41,62 @@
42
41
 
43
42
  ## 快速开始
44
43
 
45
- ### 环境要求
44
+ 日常使用只需 **Node.js 20.x–23.x**(与 `package.json` 的 `engines` 一致):
46
45
 
47
- - Node.js **20.x–23.x**(与 `package.json` 的 `engines` 字段一致)
48
- - **pnpm**
49
-
50
- ### 安装依赖
46
+ ### 全局安装(推荐)
51
47
 
52
48
  ```bash
53
- pnpm install
54
- pnpm run webui:install
49
+ npm install -g rssany # 与 npm i -g rssany 相同
50
+ rssany
55
51
  ```
56
52
 
57
- ### 配置
58
-
59
- 1. 复制环境变量示例并按需填写(JWT、OAuth、SMTP、LLM 等):
60
- ```bash
61
- cp .env.example .env
62
- ```
63
- 2. 信源与全局配置:首次启动会在 **`~/.rssany/`**(Windows:`%USERPROFILE%\.rssany\`)下自动从包内 **`init/`** 目录中的默认数据复制生成 `sources.json`、`config.json`(若已存在则不会覆盖)。也可手动复制仓库里的 `init/sources.json`、`init/config.json`。
64
- 3. (可选)LLM:在 `.env` 中设置 `OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL` 等。
53
+ 安装包内已包含构建好的后端与 Web 界面;启动后用浏览器打开终端里提示的地址(默认 **`http://127.0.0.1:18473/`**,端口可在**运行命令时当前目录**下的 `.env` 里设置 `PORT`)。
65
54
 
66
- ### 运行
55
+ - **数据目录**:首次运行会在 **`~/.rssany/`**(Windows:`%USERPROFILE%\.rssany\`)自动从包内 **`init/`** 生成 `sources.json`、`config.json` 等(已存在则不会覆盖)。
56
+ - **可选配置**:在启动 `rssany` 时的**当前目录**放置 `.env`(可参考仓库里的 `.env.example`),用于 JWT、OAuth、SMTP、LLM(如 `OPENAI_API_KEY` / `OPENAI_BASE_URL` / `OPENAI_MODEL`)等。
57
+ - **重置全部本地数据**(结束占用 `PORT` 的进程并删除用户目录,慎用):执行 **`rssany reset`**;在含 `.env` 的目录下运行可读取 `PORT` / `RSSANY_USER_DIR`,或事先在环境里导出这些变量。
67
58
 
68
- **开发**(后端根路径托管 `webui` 构建产物,改前端需重新构建或 watch):
69
-
70
- ```bash
71
- # 推荐:API + 前端 watch(修改 Svelte 后自动写入构建目录,刷新浏览器即可)
72
- pnpm run dev:all
73
-
74
- # 或分步:先打一次前端再起后端
75
- pnpm run webui:build
76
- pnpm dev
77
- ```
59
+ 等价于在项目里执行 `node node_modules/rssany/dist/index.js`;CLI 名为 **`rssany`**。
78
60
 
79
- 默认监听 `**http://127.0.0.1:18473/**`(端口见 `.env.example` 中 `PORT`,避免与常见开发端口冲突)。
61
+ ### 从源码运行(开发 / 贡献)
80
62
 
81
- **重置本地数据**(结束占用 `PORT` 的监听进程,并删除 **`~/.rssany/`**,或 `RSSANY_USER_DIR` 所设目录):
63
+ 需要 **pnpm**:
82
64
 
83
65
  ```bash
84
- pnpm reset
66
+ pnpm install
67
+ pnpm run webui:install
68
+ cp .env.example .env # 按需修改
85
69
  ```
86
70
 
87
- **仅调试 WebUI 热更新**(可选):`cd webui && pnpm dev`(Vite 代理到本机后端,见 `webui/vite.config.ts`)。
88
-
89
- **生产**:
71
+ **开发**(后端托管 `webui` 构建目录;改前端可 watch):
90
72
 
91
73
  ```bash
92
- pnpm run webui:build && pnpm start
74
+ pnpm run dev:all
93
75
  ```
94
76
 
95
- ### npm 全局安装(或 `npx`)
77
+ 或分步:`pnpm run webui:build` `pnpm dev`。
96
78
 
97
- 发布包时 `prepublishOnly` 会执行 `build:all`(后端 `vite build` + `webui:build`)。安装后:
79
+ **仅调试 WebUI 热更新**(可选):`cd webui && pnpm dev`(Vite 代理到本机后端,见 `webui/vite.config.ts`)。
98
80
 
99
- ```bash
100
- npm install -g rssany
101
- rssany
102
- ```
81
+ **生产**(本仓库):`pnpm run webui:build && pnpm start`。
103
82
 
104
- 重置数据(结束 `PORT` 监听进程并删除用户目录):**`rssany reset`**(与仓库内 **`pnpm reset`** 相同逻辑;可在含 `.env` 的目录下执行以读取 `PORT` / `RSSANY_USER_DIR`)。
83
+ **重置本地数据**(与全局安装的 `rssany reset` 逻辑相同):`pnpm reset`。
105
84
 
106
- 用户数据在 **`~/.rssany/`**(Windows:`%USERPROFILE%\.rssany`),与工作目录无关。可选环境变量 **`RSSANY_USER_DIR`** 可指定其它路径。等价于 `node node_modules/rssany/dist/index.js`;CLI 名称为 `rssany`。内置 `app/plugins/builtin/`、`statics/`、`webui/build` 随包安装路径解析。
85
+ 发布到 npm `prepublishOnly` 会执行 `build:all`(后端 `vite build` + `webui:build`)。
107
86
 
108
87
  ---
109
88
 
110
89
  ## 数据流(简图)
111
90
 
112
91
  ```
113
- sources.json / Site 插件
92
+ sources.json / 信源插件
114
93
  → 调度器触发 fetchItems
115
94
  → upsertItems
116
95
  → pipeline(每条一次)
117
96
  → [可选] deliver.url POST(出站,非入站 API)
118
97
  ```
119
98
 
120
- 消费侧:**RSS/XML**、`**/api/*`**、**MCP**、Web UI。
99
+ 消费侧:**RSS/XML**、`**/api/*`**、Web UI。
121
100
 
122
101
  ---
123
102
 
@@ -130,11 +109,9 @@ sources.json / Site 插件
130
109
 
131
110
  ---
132
111
 
133
- ## 插件与配置
134
-
135
- ### 信源插件(Site)
112
+ ## 配置
136
113
 
137
- 放置于 `**app/plugins/builtin/**` `**.rssany/plugins/**`(扁平),用户插件可与内置插件同 `id` 覆盖。最小约定包括 `id`、`listUrlPattern` 等(详见 `app/scraper/sources/web/site.ts`)。
114
+ **信源插件(Site / Source)**:目录约定、`listUrlPattern` / `pattern`、`fetchItems`、与 `sources.json` 的关系等,见 **[docs/plugins.md](./docs/plugins.md)**。
138
115
 
139
116
  ### Pipeline(固定代码)
140
117
 
@@ -173,8 +150,9 @@ sources.json / Site 插件
173
150
  ## 仓库目录(摘要)
174
151
 
175
152
  ```
176
- ├── app/ # 后端:路由、feeder、scraper、pipeline、mcp、db、auth…
153
+ ├── app/ # 后端:路由、feeder、scraper、pipeline、db、auth…
177
154
  │ └── plugins/builtin/ # 内置信源 *.rssany.js
155
+ ├── docs/ # 用户文档(如 plugins.md)
178
156
  └── webui/ # SvelteKit 前端
179
157
 
180
158
  ~/.rssany/ # 运行时用户数据(首次启动创建;或 RSSANY_USER_DIR)
package/dist/index.js CHANGED
@@ -134,6 +134,18 @@ function getEffectiveItemFields(item, lng) {
134
134
  content: (t?.content != null && t.content !== "" ? t.content : item.content) ?? ""
135
135
  };
136
136
  }
137
+ function pubDateToIsoOrNull(pubDate) {
138
+ if (pubDate == null) return null;
139
+ if (pubDate instanceof Date) {
140
+ const ms = pubDate.getTime();
141
+ return Number.isNaN(ms) ? null : pubDate.toISOString();
142
+ }
143
+ if (typeof pubDate === "string") {
144
+ const s = pubDate.trim();
145
+ return s || null;
146
+ }
147
+ return null;
148
+ }
137
149
  function normalizeAuthor(author) {
138
150
  if (author == null) return void 0;
139
151
  if (Array.isArray(author)) return author.filter((s2) => typeof s2 === "string" && s2.trim()).map((s2) => s2.trim());
@@ -619,7 +631,7 @@ async function upsertItems(items, sourceUrlOverride) {
619
631
  const nextSummary = normalizeText(item.summary) || null;
620
632
  const nextAuthorArr = normalizeAuthor(item.author);
621
633
  const nextAuthor = nextAuthorArr?.length ? JSON.stringify(nextAuthorArr) : null;
622
- const nextPubDate = item.pubDate instanceof Date ? item.pubDate.toISOString() : item.pubDate ?? null;
634
+ const nextPubDate = pubDateToIsoOrNull(item.pubDate);
623
635
  const nextTags = item.tags?.length ? JSON.stringify(item.tags) : null;
624
636
  const nextImageUrl = typeof item.imageUrl === "string" && item.imageUrl.trim() ? item.imageUrl.trim() : null;
625
637
  const info = stmt.run({
@@ -687,7 +699,7 @@ async function updateItemContent(item) {
687
699
  const arr = normalizeAuthor(item.author);
688
700
  return arr?.length ? JSON.stringify(arr) : null;
689
701
  })(),
690
- pubDate: item.pubDate instanceof Date ? item.pubDate.toISOString() : item.pubDate ?? null,
702
+ pubDate: pubDateToIsoOrNull(item.pubDate),
691
703
  tags: item.tags?.length ? JSON.stringify(item.tags) : null,
692
704
  translations: item.translations && Object.keys(item.translations).length > 0 ? JSON.stringify(item.translations) : null
693
705
  });
@@ -2514,7 +2526,7 @@ function feedItemsToPayload(items) {
2514
2526
  guid: i.guid,
2515
2527
  title: i.title,
2516
2528
  link: i.link,
2517
- pubDate: i.pubDate instanceof Date ? i.pubDate.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
2529
+ pubDate: pubDateToIsoOrNull(i.pubDate) ?? (/* @__PURE__ */ new Date()).toISOString(),
2518
2530
  author: i.author,
2519
2531
  summary: i.summary,
2520
2532
  content: i.content,
@@ -2575,7 +2587,7 @@ function toRssEntry(item, lng) {
2575
2587
  link: item.link,
2576
2588
  description: desc,
2577
2589
  guid: item.guid,
2578
- published: item.pubDate?.toISOString?.() ?? void 0,
2590
+ published: pubDateToIsoOrNull(item.pubDate) ?? void 0,
2579
2591
  imageUrl: item.imageUrl
2580
2592
  };
2581
2593
  }
@@ -2957,7 +2969,7 @@ function registerRssApiRoutes(app) {
2957
2969
  link: item.link,
2958
2970
  summary,
2959
2971
  author: item.author,
2960
- pubDate: item.pubDate instanceof Date ? item.pubDate.toISOString() : item.pubDate
2972
+ pubDate: pubDateToIsoOrNull(item.pubDate)
2961
2973
  };
2962
2974
  })
2963
2975
  });