https://github.com/ckaznable/towa
https://github.com/ckaznable/towa
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/ckaznable/towa
- Owner: ckaznable
- Created: 2026-03-30T14:40:07.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-05-15T06:26:00.000Z (2 months ago)
- Last Synced: 2026-05-15T08:38:22.679Z (2 months ago)
- Language: Rust
- Size: 303 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Towa RSS Reader
Towa 是一個單機自架的 RSS / Atom reader,包含:
- Axum 後端 API 與背景 worker
- Vite 8 + Vue 前端
- SQLite 文章儲存
- 來源排程抓取、HTTP cache-aware freshness 判斷
- Gemini Batch API 文章後處理
- 永久收藏與 30 天保留策略
- 同時保存 feed `summary` 與 `content`
## Requirements
- Rust toolchain
- Node.js 25+
- npm 11+
## Project Layout
- `src/`: Axum API、scheduler、LLM worker、SQLite access
- `migrations/`: SQLite schema migrations
- `web/`: Vite 8 + Vue frontend
- `scripts/dev.sh`: 本地同時啟動後端與前端的開發腳本
- `openspec/changes/build-rss-reader-platform/`: OpenSpec artifacts
## OpenSpec Coverage
目前主 `openspec/specs/` 已涵蓋這些核心能力:
- `feed-source-management`
- `feed-fetch-scheduling`
- `article-storage-retention`
- `llm-agent-post-processing`
- `reader-api-and-web-ui`
- `article-favorites`
## Runtime Paths
預設使用 XDG 目錄:
- Config: `~/.config/towa/config.toml`
- Database: `~/.local/share/towa/towa.db`
可用環境變數覆蓋:
- `TOWA_CONFIG`
- `TOWA_DB_PATH`
- `GEMINI_API_KEY`
- `PORT`
## Config Example
```toml
[llm]
api_key = "optional-inline-key"
batch_poll_interval_seconds = 300
batch_submit_size = 16
retry_limit = 3
[[llm.agents]]
id = "gemini-brief"
label = "Gemini Brief"
provider = "gemini"
model = "gemini-2.5-flash"
system_prompt = "Write a concise 3-5 sentence summary for a reader."
batch_enabled = true
[[llm.agents]]
id = "gemini-deep-tech"
label = "Gemini Deep Tech"
provider = "gemini"
model = "gemini-2.5-flash"
system_prompt = "Explain technical points, risks, and practical next actions."
batch_enabled = true
```
## Development
安裝前端依賴:
```bash
cd web
npm install
```
只跑後端:
```bash
cargo run
```
只跑前端:
```bash
cd web
npm run dev
```
一起跑:
```bash
./scripts/dev.sh
```
開發時:
- Axum API 預設在 `http://127.0.0.1:3000`
- Vite dev server 預設在 `http://127.0.0.1:5173`
- `web/vite.config.ts` 已把 `/api` proxy 到 Axum
## Build
後端:
```bash
cargo build --release
```
前端:
```bash
cd web
npm run build
```
## Test
後端測試:
```bash
cargo test
```
前端型別檢查與 production build:
```bash
cd web
npm run build
```
## Scheduler Behavior
每個來源會保存:
- `ETag`
- `Last-Modified`
- `last_fetch_at`
- `next_fetch_at`
下一次抓取時間計算順序:
1. `Cache-Control: s-maxage` / `max-age`
2. `Expires`
3. fallback interval
這讓 scheduler 優先尊重來源的 cache / expiry header,而不是固定頻率暴力抓取。
fallback interval 目前邊界是:
- 最短 `1` 小時
- 最長 `6` 小時
- fetch 失敗時以最短間隔重試
## Article Retention
- 一般文章:預設保留 30 天
- 收藏文章:永久保留
背景 scheduler 每輪會先執行 retention cleanup,再抓取到期來源。
## Gemini Post-Processing
- 來源可指派單一 agent
- 新文章若有 agent 指派,會進入 `pending`
- worker 預設走 Gemini Batch API
- 狀態模型:`pending` / `processing` / `done` / `failed`
- 成功結果寫入 `llm_summary`
- 錯誤寫入 `llm_error`
- 依 `retry_limit` 自動重試
- 原始文章內容會同時保存 `summary` 與 `content`;若 feed 有完整 `content.body`,系統會優先保存到 `content`
若未設定 `GEMINI_API_KEY`,HTTP API 與前端仍可使用,但 LLM worker 不會啟動。
## Frontend Scope
目前前端已包含:
- dark mode dashboard
- 全來源 stream 與 favorites 視圖
- 來源管理:新增、編輯、啟用/停用、刪除、agent 指派
- 文章清單與文章詳情閱讀
- LLM 狀態、摘要、錯誤顯示
- 收藏切換與永久收藏檢視
前端文章流目前只顯示 `llm_status = done` 的項目;若文章需經過 LLM,排序與顯示時間以 `available_at` 為準,也就是 batch 完成回寫時間,而不是原始抓取時間。
## Favorites API
後端已把收藏提升成正式能力,前端與其他 client 應優先使用:
- `GET /api/favorites`
- `PUT /api/articles/{id}/favorite`
- `GET /api/articles?favorited=true`
相容性考量下,舊的 bookmark alias 仍保留:
- `GET /api/bookmarks`
- `PUT /api/articles/{id}/bookmark`
- `GET /api/articles?bookmarked=true`
response 目前同時回傳:
- `favorited`: 正式收藏語意
- `bookmarked`: 舊欄位 alias
## Deployment Notes
單機部署最簡單:
1. 在 `web/` 執行 `npm run build`
2. 啟動 Axum binary
3. Axum 會直接提供 `web/dist`,且所有非 `/api` 路徑都會 fallback 到前端 `index.html`
4. 提供 `~/.config/towa/config.toml` 與 `GEMINI_API_KEY`
如果只想先驗證抓取與 API,可不提供 `GEMINI_API_KEY`。