# funread-web **Repository Path**: farfarfun/funread-web ## Basic Information - **Project Name**: funread-web - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-09 - **Last Updated**: 2026-10-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # funread-web [funread-api](https://github.com/farfarfun/funread-api)(依赖 [funread](https://github.com/farfarfun/funread) 核心库)的采集源管理界面。

书架 正文 采集源管理

截图由 scripts/shots.mjs 在每次打 tag 时自动重截(shots.yml)。 里面的书名和正文都是脚本里写死的假数据 —— funread 不自带任何内容,读的是你自己配的书源。

| 组件 | 默认地址 | | --- | --- | | 前端(静态页面 + 内部 API 转发) | `http://127.0.0.1:8811` | | `funread-api` 后端 | `http://127.0.0.1:18811` | 生产态 Web 服务默认只监听本机。需要手机或局域网设备访问时,先在后端 `config.toml` 配置 `[auth] password` 并注册首个管理员账号,再在 Web 服务的 `config.toml` 设置 `[server] host = "0.0.0.0"`。 ## 快速开始 ```bash # 终端 A cd ../funread-api uv run funread-api server run # 库地址来自 ~/.farfarfun/funread/config.toml # 终端 B cd ../funread-web pnpm install pnpm dev ``` 浏览器只请求同源 `/api`;Vite 默认转发到 `127.0.0.1:18811`。后端地址不同时,`pnpm dev` 下在 `.env.local` 里设 vite 的构建期变量 `FUNREAD_API_BASE_URL`;生产态服务端 (`funread-web server run`)用 `--backend` 或 config.toml 的 `[server] backend`。页面支持登记、筛选、排序、分页、启停、立即采集、重置刷新时间、 单条/批量删除以及操作队列反馈。 ## 移动端:两个 Android 客户端 两套 Capacitor 工程、两个包名,**可以装在同一台手机上对比**。打 `v*` tag 时 CI 会把 两个 debug APK 都传到 [GitHub Release](https://github.com/farfarfun/funread-web/releases) 上,直接下载安装。 | | funread light(`mobile/`) | funread 打包版(`mobile-packaged/`) | | --- | --- | --- | | Web 资源 | 不打包,`location.replace` 到用户服务器 | **打进 app** | | 页面来源 | 用户的服务器 | `http://localhost` | | 认证 | 同源 cookie 会话 | **`Authorization: Bearer <设备 token>`** | | 离线启动 | 要网络 | 能 | | 包名 | `com.farfarfun.funread.light` | `com.farfarfun.funread` | | 服务端要求 | 任意版本 | 要有 `POST /auth/tokens/issue` | **light** 只带一个启动页,填完地址就把整个窗口交给那台服务器上的这同一个 `/web`。 页面来源是用户的服务器,`/api` 同源,cookie 会话原样生效 —— **后端一行都不用改**。 代价是启动必须有网络,这是预期行为。 **打包版**把 `dist` 装进 app,于是请求用户服务器成了跨站,cookie 发不出去 (后端 `allow_credentials=False`)。它改用设备 token:首次启动填地址 + 账号口令, 换一条长期凭据存在本机;「我的 → 我的设备」能看到所有设备并单条吊销。 ```bash cd mobile && npm install && npm run sync # light cd mobile-packaged && npm install && npm run build:www && npm run sync # 打包版 ``` 细节、踩过的坑、iOS 为什么未经验证,在各自的 README 里: [mobile/README.md](mobile/README.md)、[mobile-packaged/README.md](mobile-packaged/README.md)。 ## 桌面端:同样两个客户端 两套 [Tauri 2](https://v2.tauri.app/) 工程,形态和上面两个 Android 客户端一一对应, 应用标识也不同,**可以装在同一台机器上对比**。打 `v*` tag 时 CI 会把它们和 APK 一起 传到同一个 Release 上。 | | funread light(`desktop/`) | funread 打包版(`desktop-packaged/`) | | --- | --- | --- | | Web 资源 | 不打包,`navigate()` 到用户服务器 | **打进应用**(同一份 `dist-packaged/`) | | 页面来源 | 用户的服务器 | `tauri://localhost` | | 认证 | 同源 cookie 会话 | **`Authorization: Bearer <设备 token>`** | | 应用标识 | `com.farfarfun.funread.light` | `com.farfarfun.funread` | | 产物 | `funread-light--{linux,windows,macos}.*` | `funread_<版本>_<架构>.deb`、`funread-<版本>-macos-<架构>.dmg` 等 | **两套产物名**。打包版按 [farfarfun/packages](https://github.com/farfarfun/packages) 的接入规范拼(`docs/package-spec.md`):版本号不带 `v`,`.deb` 用下划线分隔、 macOS 分 `arm64` / `amd64` 两个 dmg,外加一个覆盖全部附件的 `SHA256SUMS`。组织的 APT 源和 Homebrew tap 是**按文件名**认版本和架构的,差一个字就收不进去。light 不进 包仓库 —— 它装完还要填服务器地址,不适合「装上就能用」的包管理器分发 —— 名字沿用 原来的,继续只在 Release 页面提供。 **Tauri 而不是 Electron**:Electron 每个 app 自带一份 Chromium(100MB 起步), Tauri 用系统 WebView,壳本身是个位数 MB。对一个自托管小工具这个差距没有道理。 代价是 Linux 上的渲染行为取决于用户装的 WebKitGTK 版本,所以产物固定在 ubuntu-22.04 上构建(标准源里带 webkit2gtk-4.1 的第一个 LTS,也是 AppImage 的 glibc 基线)。 两件桌面特有的事: - **light 版的地址是运行时的。** `tauri.conf.json` 里的 URL 和 Capacitor 的 `server.url` 一样是构建期配置,所以同样走「本地启动页 + 跳转」,只是跳转由 Rust 的 `WebviewWindow::navigate()` 发起,探活也挪进了 Rust。 - **打包版的 API 请求从 Rust 发出去。** Tauri 的页面来源不在 funread-api 的 CORS 白名单里,而且 WebKit 把 `tauri://` 当不透明来源、发出去的 `Origin` 是空的 —— 加白名单在 Linux / macOS 上也救不回来。换掉 `fetch` 是壳层的事,Vue 一个字都没改。 **所有桌面产物都没有签名**,也没有做 macOS 公证。第一次打开要绕过系统拦截,方法写在 Release 说明和各自的 README 里: [desktop/README.md](desktop/README.md)、[desktop-packaged/README.md](desktop-packaged/README.md)。 ```bash cd desktop && cargo tauri build # light pnpm run build:packaged && cd desktop-packaged && cargo tauri build # 打包版 ``` ### Vue 这边怎么区分两种形态 一份源码出两种产物,分界是构建模式: - `pnpm run build` → `dist/`,给浏览器、也给 light 套壳加载。同源 `/api`,cookie。 - `pnpm run build:packaged` → `dist-packaged/`,打进 app。跨站基址 + Bearer。 判定用的是 `vite build --mode packaged` 注入的构建期常量 `__FUNREAD_PACKAGED__`, 不是运行时探测 —— 这样浏览器那份产物里连 `Authorization` 这个词都不会出现, 浏览器和 light 的行为分毫不变。理由写在 [`src/api/packaged.ts`](src/api/packaged.ts) 顶部,两个方向的断言分别在 `tests/bundle.test.js` 和 CI 的 `android-packaged` job 里。 四套客户端工程都不进 npm 包:`package.json` 的 `files` 是白名单 (`bin`/`server`/`dist`),`tests/packaging.test.js` 盯着它别被改坏。 --- ## 关于 farfarfun [farfarfun](https://github.com/farfarfun) 是一个专注于实用工具库的开源组织, 涵盖云存储、数据处理、AI、多媒体与开发工具链等方向。 - 🏠 组织主页: - 📧 联系:farfarfun@qq.com 本项目基于 [MIT](LICENSE) 协议开源。