Skip to content

Commit 05c8591

Browse files
committed
feat(api): add local HTTP API service with aria2 compatibility
Introduce the fluxdown_api crate: /ping, browser script takeover, an aria2 JSON-RPC shim, and a token-guarded management API bound to 127.0.0.1. Move HTTP takeover out of hub into the new crate and wire ApiHost through download_actor; publish the OpenAPI spec and an /api-docs page. - add eD2K server.met subscription support - add built-in "bolt" app icon choice and rebrand logo/icons - remove built-in analytics/telemetry (analytics_service, stats page, and PWA web/ assets)
1 parent 442abc0 commit 05c8591

85 files changed

Lines changed: 10213 additions & 6556 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 4 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,12 @@ on:
44
push:
55
tags:
66
- "v*"
7-
workflow_dispatch:
8-
inputs:
9-
tag:
10-
description: "同步到 R2 的版本 tag(如 v0.1.28)"
11-
required: true
12-
type: string
137

148
permissions:
159
contents: write
1610
actions: write # 允许在 release job 末尾删除旧 artifact(避免 500MB 配额耗尽)
1711

1812
jobs:
19-
# ── 手动触发时:仅同步指定版本文件到 R2,跳过构建 ──
20-
sync-r2-only:
21-
name: Sync Release to R2
22-
if: github.event_name == 'workflow_dispatch'
23-
runs-on: ubuntu-latest
24-
steps:
25-
- name: Download release assets from GitHub
26-
env:
27-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28-
run: |
29-
TAG="${{ github.event.inputs.tag }}"
30-
echo "Downloading assets for ${TAG}..."
31-
mkdir -p release-assets
32-
gh release download "${TAG}" \
33-
--repo ${{ github.repository }} \
34-
--dir release-assets
35-
echo "Downloaded files:"
36-
ls -lh release-assets/
37-
38-
- name: Sync to Cloudflare R2
39-
env:
40-
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
41-
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
42-
AWS_DEFAULT_REGION: auto
43-
R2_ENDPOINT: ${{ secrets.CF_R2_ENDPOINT }}
44-
R2_BUCKET: ${{ secrets.CF_R2_BUCKET }}
45-
run: |
46-
TAG="${{ github.event.inputs.tag }}"
47-
echo "Syncing to R2 under prefix: ${TAG}/"
48-
for file in release-assets/*; do
49-
filename=$(basename "$file")
50-
echo " Uploading: ${filename}"
51-
aws s3 cp "$file" \
52-
"s3://${R2_BUCKET}/${TAG}/${filename}" \
53-
--endpoint-url "${R2_ENDPOINT}" \
54-
--no-progress
55-
done
56-
echo "✅ R2 sync complete: ${{ secrets.CF_R2_PUBLIC_URL }}/${TAG}/"
57-
5813
# ── 并行 Job 1: 构建 Windows 安装包(x64 + ARM64)──
5914
build-windows:
6015
name: Build Windows (${{ matrix.arch }})
@@ -160,7 +115,7 @@ jobs:
160115
161116
- name: Build Flutter Windows app
162117
# Windows 使用 native-tls(Schannel),无需 aws-lc-sys,无需任何额外 TLS 环境变量
163-
run: flutter build windows --release --build-name=${{ steps.version.outputs.VERSION }} --build-number=1 --dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }} --dart-define=ANALYTICS_APP_KEY=${{ secrets.ANALYTICS_APP_KEY }}
118+
run: flutter build windows --release --build-name=${{ steps.version.outputs.VERSION }} --build-number=1 --dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }}
164119

165120
# ── ARM64: Flutter 在 ARM64 runner 上以 x64 仿真运行,输出到 build\windows\x64
166121
# 但后续步骤(Inno Setup / portable zip)需要 build\windows\arm64 ──
@@ -543,7 +498,7 @@ jobs:
543498

544499
# ── Build ──
545500
- name: Build Flutter Linux app
546-
run: flutter build linux --release --build-name=${{ steps.version.outputs.VERSION }} --build-number=1 --dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }} --dart-define=ANALYTICS_APP_KEY=${{ secrets.ANALYTICS_APP_KEY }}
501+
run: flutter build linux --release --build-name=${{ steps.version.outputs.VERSION }} --build-number=1 --dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }}
547502

548503
# NOTE: fluxdown_nmh and fluxdown_updater are built automatically
549504
# by CMake custom targets defined in linux/CMakeLists.txt during flutter build.
@@ -836,8 +791,7 @@ jobs:
836791
flutter build macos --release \
837792
--build-name=${{ steps.version.outputs.VERSION }} \
838793
--build-number=1 \
839-
--dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }} \
840-
--dart-define=ANALYTICS_APP_KEY=${{ secrets.ANALYTICS_APP_KEY }}
794+
--dart-define=APP_VERSION=${{ steps.version.outputs.VERSION }}
841795
842796
# NOTE: fluxdown_nmh and fluxdown_updater are built automatically
843797
# by Xcode shell script build phases defined in macos/Runner.xcodeproj during flutter build.
@@ -976,85 +930,9 @@ jobs:
976930
generate_release_notes: false
977931
draft: false
978932

979-
# ── 同步所有 Release 文件到 Cloudflare R2(改善中国大陆下载速度)──
980-
# 在 GitHub Release 发布成功后执行
981-
# 文件路径格式: {tag_name}/{filename},例如: v0.3.0/FluxDown-0.3.0-windows-x64-setup.exe
982-
# 公开访问 URL: https://dw-fluxdown.zerx.dev/v0.3.0/FluxDown-0.3.0-windows-x64-setup.exe
983-
- name: Sync release assets to Cloudflare R2
984-
env:
985-
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
986-
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
987-
AWS_DEFAULT_REGION: auto
988-
R2_ENDPOINT: ${{ secrets.CF_R2_ENDPOINT }}
989-
R2_BUCKET: ${{ secrets.CF_R2_BUCKET }}
990-
run: |
991-
TAG_NAME="${GITHUB_REF#refs/tags/}"
992-
echo "Syncing release assets to R2 under prefix: ${TAG_NAME}/"
993-
994-
# 安装 AWS CLI(ubuntu-latest 已预装,此处保险起见确认可用)
995-
aws --version
996-
997-
# 批量上传所有 release 文件到 R2(按 tag 分目录存放)
998-
for file in release-assets/*; do
999-
filename=$(basename "$file")
1000-
echo " Uploading: ${filename}"
1001-
aws s3 cp "$file" \
1002-
"s3://${R2_BUCKET}/${TAG_NAME}/${filename}" \
1003-
--endpoint-url "${R2_ENDPOINT}" \
1004-
--no-progress
1005-
done
1006-
1007-
echo "R2 sync complete. Files available at: ${{ secrets.CF_R2_PUBLIC_URL }}/${TAG_NAME}/"
1008-
1009-
# ── 清理 R2 旧版本,仅保留最近 20 个版本(免费额度 10GB)──
1010-
- name: Cleanup old versions from R2
1011-
env:
1012-
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
1013-
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}
1014-
AWS_DEFAULT_REGION: auto
1015-
R2_ENDPOINT: ${{ secrets.CF_R2_ENDPOINT }}
1016-
R2_BUCKET: ${{ secrets.CF_R2_BUCKET }}
1017-
run: |
1018-
KEEP_VERSIONS=20
1019-
echo "🧹 R2 cleanup: keeping latest ${KEEP_VERSIONS} versions"
1020-
1021-
# 列出 R2 中所有 v* 前缀目录,提取唯一的版本 tag
1022-
ALL_VERSIONS=$(aws s3 ls "s3://${R2_BUCKET}/" \
1023-
--endpoint-url "${R2_ENDPOINT}" \
1024-
| grep 'PRE v' \
1025-
| awk '{print $2}' \
1026-
| sed 's|/||' \
1027-
| sort -t. -k1,1n -k2,2n -k3,3n \
1028-
)
1029-
1030-
TOTAL=$(echo "$ALL_VERSIONS" | grep -c . || true)
1031-
echo "Found ${TOTAL} versions in R2"
1032-
1033-
if [ "$TOTAL" -le "$KEEP_VERSIONS" ]; then
1034-
echo "✅ Only ${TOTAL} versions exist, nothing to clean up"
1035-
exit 0
1036-
fi
1037-
1038-
# 计算需要删除的版本数
1039-
DELETE_COUNT=$((TOTAL - KEEP_VERSIONS))
1040-
TO_DELETE=$(echo "$ALL_VERSIONS" | head -n "$DELETE_COUNT")
1041-
1042-
echo "Will delete ${DELETE_COUNT} old version(s):"
1043-
echo "$TO_DELETE"
1044-
1045-
for ver in $TO_DELETE; do
1046-
echo " 🗑️ Deleting ${ver}/ ..."
1047-
aws s3 rm "s3://${R2_BUCKET}/${ver}/" \
1048-
--endpoint-url "${R2_ENDPOINT}" \
1049-
--recursive \
1050-
--no-progress
1051-
done
1052-
1053-
echo "✅ Cleanup complete. Kept latest ${KEEP_VERSIONS} versions."
1054-
1055933
# ── 清理 GitHub Actions Artifact,仅保留当前 tag 这一组 ──
1056934
# 原因:Free 账号 artifact 配额仅 500MB,每次 release 会上传 ~400MB
1057-
# Release 资产已独立存储在 GitHub Release + R2,artifact 只是 CI 中间产物可安全清理
935+
# Release 资产已独立存储在 GitHub Release,artifact 只是 CI 中间产物可安全清理
1058936
# 此 step 在 release 创建成功后执行,失败不阻断流程(continue-on-error)
1059937
- name: Cleanup GitHub Actions artifacts (keep current tag only)
1060938
if: always() && success()

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ skills-lock.json
9494
test/batch_delete_scale_test.dart
9595
test/resume_all_test.dart
9696
website/fluxdown-website-latest.tar
97-
design/mobile/
9897
docs/
9998

10099
# ── 敏感信息(开源清理,严禁入库)──
@@ -114,3 +113,4 @@ android/local.properties
114113
**/flutter/ephemeral/
115114
**/Flutter/ephemeral/
116115
website/.vscode/launch.json
116+
design/

AGENTS.md

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ flutter test # 全部 Dart 测试
3333
flutter test test/widget_test.dart # 运行单个 Dart 测试文件
3434
cargo test -p fluxdown_engine # 运行下载引擎全部单元测试(native/engine,下载协议/分段/DB 等核心逻辑)
3535
cargo test -p hub # 运行 hub 适配层全部单元测试(native/hub,Rinf FFI/信号桥接)
36+
cargo test -p fluxdown_api # 运行本机 API 服务全部测试(native/api,axum HTTP API/aria2 兼容)
3637
cargo test -p fluxdown_engine -- segment_advisor # 运行特定 Rust 测试模块
3738
cargo test -p fluxdown_engine -- test_name # 运行单个 Rust 测试函数
39+
cargo run -p fluxdown_api --example gen_openapi > website/public/openapi.json # 改动 API 后重新生成 OpenAPI 规范(官网 /api-docs 渲染)
3840

3941
# 依赖
4042
flutter pub get # Dart 依赖安装
@@ -57,7 +59,7 @@ python scripts/release_tag.py v0.x.x --model opus --lang both --push --github-re
5759
python scripts/release_tag.py v0.x.x --dry-run # 仅预览
5860

5961
# 图标生成(修改 assets/logo/fluxdown_logo.svg 后执行)
60-
bun scripts/gen_icons.ts # 全平台图标一键生成(51 个文件,覆盖所有平台)
62+
bun scripts/gen_icons.ts # 全平台图标一键生成(50 个文件,覆盖所有平台)
6163
```
6264

6365
## 项目结构
@@ -84,7 +86,6 @@ x_down/
8486
│ │ ├── tray_service.dart # 系统托盘
8587
│ │ ├── notification_service.dart # 下载完成通知(800ms 防抖合批,Win: Win32 悬浮窗 / Linux/mac: 系统通知)
8688
│ │ ├── update_service.dart # 自动更新(GitHub Releases)
87-
│ │ ├── analytics_service.dart # 匿名数据分析(GA4)
8889
│ │ ├── feedback_service.dart # 反馈提交(GitHub Issues)
8990
│ │ ├── log_service.dart # 日志管理(2MB 分卷,总量默认 10MB 超量清理,保留 7 天)
9091
│ │ ├── open_folder.dart # 打开文件夹(跨平台)
@@ -118,6 +119,15 @@ x_down/
118119
│ └── tracker_subscription.rs # BT tracker 订阅列表抓取/去重
119120
│ ├── examples/headless_download.rs # CLI 式同进程直接调用证明(不依赖 hub/rinf)
120121
│ └── tests/ # realtest.rs / corruption_test.rs(迁移自 hub,确定性/真实网络回归)
122+
├── native/api/ # `fluxdown_api` crate(本机 HTTP API,axum,零 rinf 依赖)
123+
│ └── src/
124+
│ ├── types.rs # wire JSON 契约(TaskDto/CreateTaskRequest/DownloadRequest,camelCase)
125+
│ ├── routes.rs # 路径常量(/api/v1/*,server 与 Rust 客户端共用)
126+
│ ├── service.rs # `ApiHost` trait —— 宿主能力契约(桌面 App / 未来 server 各自实现)
127+
│ ├── server.rs # axum 服务器(/ping、脚本接管、aria2 兼容、管理 API;仅 127.0.0.1)
128+
│ ├── jsonrpc.rs # aria2 JSON-RPC 兼容垫片(addUri/getVersion/multicall/…)
129+
│ ├── takeover.rs # 脚本接管批量请求解析
130+
│ └── auth.rs # 鉴权(常量时间比较/Client 头门禁/管理 API 强制 token)
121131
├── native/hub/ # Rinf FFI 适配层 crate(`hub`,edition 2024,crate 名不可改)
122132
│ └── src/
123133
│ ├── lib.rs # 入口(tokio current_thread runtime)
@@ -129,7 +139,7 @@ x_down/
129139
│ ├── protocol_registry.rs # fluxdown:// 自定义协议注册(Windows)
130140
│ ├── file_association.rs # .torrent 文件关联注册(Windows)
131141
│ ├── native_messaging.rs # Windows: Named Pipe `\\.\pipe\fluxdown`;Linux: Unix socket 服务端
132-
│ ├── http_takeover.rs # 本地 HTTP 接管服务(Tampermonkey userscripts
142+
│ ├── api_host.rs # `fluxdown_api::ApiHost` 实现(读直查 Db,写经 ApiCommand+oneshot 进 actor
133143
│ ├── nmh_registry.rs # NMH 清单注册(Linux: 写入 Chrome/Firefox NMH JSON)
134144
│ ├── reveal_file.rs # 在文件管理器中定位文件/打开目录
135145
│ └── updater.rs # 自动更新器(GitHub Releases API)
@@ -190,7 +200,7 @@ x_down/
190200
| 文件 | 功能描述 |
191201
|------|---------|
192202
| `pages/home_page.dart` | 主页面。三栏布局(侧边栏 180-320px / 任务列表 / 详情面板 240-420px),全局快捷键(Ctrl+F/A/Esc/Del),Boost 优先下载 Banner |
193-
| `pages/settings_page.dart` | 设置页面。侧边栏导航 6 个分类:通用(开机启动/关闭到托盘/torrent关联/匿名分析)、外观(语言/主题/颜色)、下载(目录/线程/并发/速度/UA/队列)、BT(自定义 Tracker)、代理(无/系统/手动 + 代理测试)、关于(版本更新) |
203+
| `pages/settings_page.dart` | 设置页面。侧边栏导航 6 个分类:通用(开机启动/关闭到托盘/torrent关联)、外观(语言/主题/颜色)、下载(目录/线程/并发/速度/UA/队列)、BT(自定义 Tracker)、代理(无/系统/手动 + 代理测试)、关于(版本更新) |
194204

195205
### 核心布局组件
196206

@@ -298,7 +308,7 @@ CREATE TABLE queues (
298308

299309
> 以下模块均已迁移到 `native/engine``fluxdown_engine` crate)。`native/hub/src/logger.rs`
300310
> 是转发 `pub use fluxdown_engine::logger::*;` 的 shim,保留 `crate::logger::*` 路径供
301-
> hub 内 App-shell 专属文件(`native_messaging.rs`/`http_takeover.rs`/`updater.rs`/…)零改动继续使用。
311+
> hub 内 App-shell 专属文件(`native_messaging.rs`/`api_host.rs`/`updater.rs`/…)零改动继续使用。
302312
303313
### segment_advisor.rs — 动态分段计算
304314
- 文件 < 1MB → 1线程;1-10MB → 4;10-100MB → 8;100MB-1GB → 16;> 1GB → 32
@@ -338,6 +348,34 @@ CREATE TABLE queues (
338348
- 提供 `#[macro_export]``log_info!` / `log_error!` 宏(`$crate` 前缀保证跨 crate 调用正确解析),用法同 `format!()`
339349
- hub 侧使用前需在文件顶部 `use crate::logger::log_info;`(经 `native/hub/src/logger.rs` shim 转发)
340350

351+
## 本机 API 服务(native/api,`fluxdown_api` crate)
352+
353+
一个端口(默认 17800,仅监听 127.0.0.1)、一个 axum 服务器,三组按配置独立启停的路由;
354+
`local_server_*` 配置变更时 actor 热重启监听(优雅停机 + 重绑,无需重启应用)。
355+
356+
|路由组|端点|开关|鉴权|
357+
|---|---|---|---|
358+
|探活|`GET /ping`|总开关||
359+
|脚本接管|`POST /download``/download/batch`|`local_server_takeover_enabled`|`X-FluxDown-Client` 头 + 可选 token|
360+
|aria2 兼容|`POST /jsonrpc`(addUri/getVersion/getGlobalStat/multicall/listMethods)|`local_server_jsonrpc_enabled`|可选 token(`X-FluxDown-Token` 头或 `params[0]="token:xxx"`|
361+
|管理 API|`GET /api/v1/info``GET/POST /api/v1/tasks``GET/DELETE /api/v1/tasks/{id}``PUT /api/v1/tasks/{id}/pause\|continue``PUT /api/v1/tasks/pause\|continue``GET /api/v1/queues`|`local_server_api_enabled`|**强制** token(`Authorization: Bearer``X-FluxDown-Token`|
362+
|API 文档|`GET /api/v1/openapi.json`(OpenAPI 3.1)|`local_server_api_enabled`|无(纯接口描述,不含数据)|
363+
364+
**架构**`fluxdown_api` 零 rinf 依赖,只定义 wire 契约(`types.rs`,camelCase JSON)+ 路径常量
365+
`routes.rs`)+ 宿主契约(`service.rs``ApiHost` trait)+ axum 服务器(`server.rs`)。
366+
桌面 App 在 `native/hub/src/api_host.rs` 实现 `ApiHost`:读操作直查 `Db`(Clone),
367+
写操作打包 `ApiCommand` + oneshot 经 mpsc 进 `download_actor` 事件循环串行执行。
368+
未来 headless server / 手机端复用同一 crate,只需另写一个 `ApiHost` 实现;
369+
MCP server 等 Rust 客户端直接 import `types` + `routes`
370+
371+
**语义区分**:接管/aria2 入口 → 外部下载流程(弹快速下载确认框);管理 API `POST /api/v1/tasks`
372+
→ 直接创建任务(自动化客户端受信任,无弹框)。
373+
374+
**OpenAPI 文档**:spec 由 utoipa 从 handler 注解(`#[utoipa::path]`)与 `ToSchema` 派生
375+
`openapi.rs`,含漂移守卫测试——路由常量与注解不同步会跑挂)。改动 API 后执行
376+
`cargo run -p fluxdown_api --example gen_openapi > website/public/openapi.json` 重新生成,
377+
官网 `/api-docs` 页用 Scalar(CDN)渲染该文件。
378+
341379
## 浏览器扩展(fluxDown/)
342380

343381
### 通信架构
@@ -388,7 +426,6 @@ NMH 注册:
388426
| 行为 | `closeToTray` | 关闭到系统托盘 |
389427
| 行为 | `autoStartup` | 开机启动 |
390428
| 行为 | `autoCheckUpdate` | 自动检查更新 |
391-
| 行为 | `analyticsEnabled` | 匿名数据分析(GA4) |
392429
| BT | `btEnableDht` | 启用 DHT |
393430
| BT | `btEnableUpnp` | 启用 UPnP |
394431
| BT | `btPortStart/End` | 端口范围 |
@@ -400,6 +437,12 @@ NMH 注册:
400437
| 代理 | `proxyNoList` | 排除列表 |
401438
| 文件关联 | `torrentAssocPrompted` | 是否已提示过 torrent 关联 |
402439
| 文件关联 | `torrentAssociated` | 是否已关联 .torrent 文件 |
440+
| API 服务 | `local_server_enabled` | 本机 API 服务总开关(默认开) |
441+
| API 服务 | `local_server_port` | 监听端口(默认 17800,仅 127.0.0.1) |
442+
| API 服务 | `local_server_token` | 访问令牌(管理 API 强制要求非空) |
443+
| API 服务 | `local_server_takeover_enabled` | 浏览器脚本接管子开关(默认开) |
444+
| API 服务 | `local_server_jsonrpc_enabled` | aria2 RPC 兼容子开关(默认开) |
445+
| API 服务 | `local_server_api_enabled` | 管理 API 子开关(默认关,开启时 Dart 侧自动生成 token) |
403446

404447
## 主题系统
405448

@@ -421,7 +464,6 @@ NMH 注册:
421464
| `tray_service.dart` | 系统托盘图标+菜单(多语言),菜单项:显示窗口/新建下载/暂停恢复/退出 |
422465
| `notification_service.dart` | 下载完成通知。800ms 防抖合批(3s 最长等待),Windows → Win32ToastWindow 主显示器右下角悬浮窗(无论主窗口可见性),Linux/macOS → 系统通知带"打开文件夹/打开文件"动作按钮(Linux D-Bus actions / macOS UNNotificationCategory;Wayland 禁止全局坐标定位,D-Bus 通知是唯一正确做法) |
423466
| `update_service.dart` | GitHub Releases 检查,启动后 5s 静默检查,弹窗展示 changelog |
424-
| `analytics_service.dart` | GA4 匿名埋点:启动/退出/创建/完成/失败/视图切换 |
425467
| `feedback_service.dart` | POST GitHub Issues API 提交反馈(含 OS/版本/语言系统信息) |
426468
| `log_service.dart` | 按日期写入 `fluxdown_YYYY-MM-DD.log`,启动时清理 7 天前日志,提供 `logInfo()`/`logError()` 全局函数 |
427469
| `open_folder.dart` | 跨平台打开文件夹(调用系统文件管理器) |

0 commit comments

Comments
 (0)