跳转到内容
AsterDrive Developer Docs开发者

AsterDrive 架构概览

本文描述的是当前仓库已经落地的实现,不是早期设计草图。

如果你刚接手这个仓库,建议先看这页,再看 module-designs.md

  • AsterDrive 现在不是“只有一个运行模式的单体服务”,而是同一套代码支持两种节点模式:
    • primary:对外提供主 REST API、公开分享、WebDAV、前端页面,并负责运行时配置和后台任务
    • follower:只暴露健康检查和内部对象存储协议,给远端主节点当受管存储节点
  • 元数据主要在数据库里,文件内容主要在存储驱动里;两者通过 filesfile_blobsfile_revision_historiesfile_revisionsupload_sessions 等表关联。files 是 current revision 的物化投影,不是另一份版本事实
  • 个人空间和团队空间共用同一条文件主链路,只是在 route / service 层通过 WorkspaceStorageScope 切换作用域
  • 后端主线仍然是: src/api/routes/* -> src/services/* -> src/db/repository/* / src/storage/*
  • workspace crate 已拆出明确基础边界:aster_drive_model 负责领域类型和 Entity,aster_drive_migration 负责表结构演进,aster_drive_storage 负责存储 trait、descriptor、对象 key 与结构化错误;根包保留产品业务和具体运行时实现
  • WebDAV 不是普通 REST 路由的一个分支,而是独立挂载在 src/webdav/
  • 运行二进制默认启动 HTTP 服务;启用默认 cli feature 时,同一入口还提供 doctorconfigdatabase-migratenode enroll 等运维子命令
  • 前端代码在 frontend-panel/,生产产物由 primary 节点直接服务
  • 配置分两层:
    • 静态配置:data/config.toml + ASTER__... 环境变量
    • 运行时配置:数据库 system_config,产品定义单一数据源是 src/config/definitions.rs;共享类型、registry、DB store 和进程内 snapshot 由 aster_forge_config / aster_forge_db 提供
你想回答的问题先看哪里为什么
服务怎么启动、怎么区分 primary / followersrc/main.rssrc/config/node_mode.rssrc/runtime/startup/这里决定启动模式、运行时状态和节点职责
运维 CLI 怎么执行src/main.rssrc/cli/**cli feature 下的子命令在进入 HTTP 启动前分派
主节点挂了哪些路由src/api/primary.rssrc/api/routes/这里决定 /api/v1/health/d/pv、WebDAV 和前端兜底的注册顺序
从节点到底暴露什么src/api/follower.rssrc/api/routes/internal_storage.rsfollower 只负责内部存储协议和健康检查
远端节点 binding 与反向隧道怎么走src/api/routes/remote_node_control.rssrc/services/remote/binding_control.rssrc/api/routes/remote_tunnel.rssrc/storage/remote_protocol/tunnel/follower 通过独立控制面拉取 binding desired state,只在有效 transport 为 reverse tunnel 时启动数据面 worker
一个 REST 接口怎么实现对应 src/api/routes/** 文件route 层做参数解析、鉴权包装和响应适配
文件 / 团队 / 分享 / 上传的业务规则在哪src/services/**业务语义集中在 service 层,不应散落在 route 里
数据怎么查怎么写src/db/repository/**repo 层封装数据库访问和跨库兼容细节
文件内容怎么落盘 / 上对象存储 / 走 OneDrive 或远端节点crates/aster_drive_storage/**src/storage/**前者定义 trait、descriptor 和结构化错误,后者实现 connector、具体驱动、registry、策略快照和远端协议运行时
WebDAV 为什么和 REST 不一样src/webdav/**这是单独的协议接入层
团队空间为什么复用个人空间语义src/services/workspace/scope/src/services/workspace/storage/src/services/workspace/models.rssrc/services/workspace/storage_core/src/services/files/folder/src/services/files/file/scope 切换、上传编排和统一存储核心链路都在这里
表结构怎么演进crates/aster_drive_migration/crates/aster_drive_model/src/entities/**migration 和 entity 必须一起看

追一个具体功能时,最省时间的路径通常是:

  1. 先从对应 src/api/routes/** 找入口
  2. 再跳到 src/services/**
  3. 最后看 src/db/repository/**crates/aster_drive_storage/**src/storage/**src/webdav/**

primary 会注册这些入口:

  • REST API:/api/v1/*
  • 远端节点 binding 控制面:/api/v1/internal/remote-node-control/*
  • 远端节点反向隧道内部接口:/api/v1/internal/remote-tunnel/*
  • 健康检查:/health*
  • 公开分享与直链:
    • /api/v1/s/{token}*
    • /d/{token}/{filename}
    • /pv/{token}/{filename}
  • WebDAV:默认 /webdav
  • 前端页面与静态资源:由 src/api/routes/frontend.rs 兜底
  • 开发态 OpenAPI:
    • /swagger-ui
    • /api-docs/openapi.json

follower 不提供普通用户 API、WebDAV 或前端页面,只注册:

  • 健康检查:/health*
  • 内部对象存储协议:/api/v1/internal/storage/*

这条内部协议当前用于主节点和受管远端节点之间的对象写入、对象拼接、对象列举、绑定同步与远端存储目标控制面。当前协议版本为 v5、兼容下限为 v4,primary 与 follower 声明的支持区间必须有交集。0.4.0 起控制面只保留 /targets 路径,旧 /ingress-profiles 兼容路由已经移除。

follower 对所有 master binding 周期性请求 primary 的 /api/v1/internal/remote-node-control/*,独立收敛 primary 解析后的有效 transport。只有 binding 已启用且 primary 下发的 resolved_transportreverse_tunnel 时,follower 才启动 tunnel worker 主动连接 /api/v1/internal/remote-tunnel/*;direct 和 disabled binding 不启动 tunnel 数据面 worker,但仍保持控制面同步。

  1. src/main.rs 进入 run_primary_http_server()
  2. src/api/primary.rs 注册 /api/v1 下的各模块路由
  3. 请求先经过全局中间件:
    • 压缩
    • Request ID
    • 运行时 CORS
    • 安全响应头
  4. 命中对应 src/api/routes/** handler
  5. 受保护接口再经过路由级 JWT 鉴权和限流
  6. src/services/** 执行业务规则
  7. src/db/repository/** 负责数据库读写;涉及二进制内容时进入 src/storage/**
  8. route 层返回统一 JSON,或者直接返回文件流 / SSE / WebDAV / Prometheus 文本响应

例外要记住:

  • /d/.../pv/...、文件下载、缩略图、分享下载不走统一 JSON 包装
  • GET /api/v1/auth/events/storage 是 SSE
  • GET /health/metrics 是 Prometheus text exposition
  • 前端兜底路由最后注册,所以 API / WebDAV 必须先于它注册
  1. src/api/follower.rs 只注册 /api/v1/internal/storage/*
  2. src/api/routes/internal_storage.rs 校验内部签名或预签名访问
  3. remote::master_binding 解析主节点绑定关系,remote::storage_target 解析 follower 侧远端存储目标
  4. 通过 driver_registry 取得实际存储驱动
  5. 请求落到本地 / 对象存储 / 远端驱动能力接口

如果你在查远端节点写入问题,不要先去普通 files / upload 路由里找。

远端节点有两种传输方式:

  • direct:primary 直接向 follower 的 /api/v1/internal/storage/* 发 HTTP 请求。
  • reverse_tunnel:primary 把内部存储请求登记到 tunnel registry;follower 通过 /api/v1/internal/remote-tunnel/poll / /complete/connect WebSocket 主动取走请求并回传结果。

auto 会根据远端节点是否有非空 base_url 选择 direct 或 reverse tunnel。

WebDAV 不走 src/api/routes/**,而是:

  1. crate::webdav::configure() 在 primary 上挂到配置的 prefix
  2. 检查运行时开关 webdav_enabled
  3. 做 WebDAV 专用 Basic Auth 认证
  4. 为请求构造带用户上下文的 AsterDavFs
  5. 使用数据库锁系统和版本能力
  6. 进入 WebDAV handler
┌─────────────────────────────────────────────┐
│ 接入层 │
│ - React 前端 / 公开分享页 │
│ - REST API (primary) │
│ - Internal Storage API (follower) │
│ - WebDAV │
├─────────────────────────────────────────────┤
│ 应用层 │
│ - 路由、DTO、统一响应、错误码 │
│ - JWT / Admin / Rate Limit / CORS 等中间件 │
├─────────────────────────────────────────────┤
│ 业务层 │
│ - auth / profile / team / file / folder │
│ - upload / batch / share / trash / task │
│ - policy / config / audit / webdav / wopi │
│ - workspace scope / storage core │
├─────────────────────────────────────────────┤
│ 基础设施层 │
│ - SeaORM + migration │
│ - StorageConnector descriptor / action │
│ - StorageDriver(Local/S3/SFTP/Azure) │
│ - StorageDriver(Tencent COS/OneDrive) │
│ - StorageDriver(Remote) │
│ - CacheBackend(Memory / Redis) │
├─────────────────────────────────────────────┤
│ 数据层 │
│ - users / teams / team_members │
│ - folders / files / file_blobs / versions │
│ - shares / upload_sessions / tasks │
│ - webdav_accounts / system_config / locks │
└─────────────────────────────────────────────┘

仓库里的实用判断标准仍然是:

  • route 层处理 HTTP / 协议适配
  • service 层处理业务语义
  • repo 层处理数据库读写
  • storage 层处理对象内容
模块当前职责
src/main.rs进程和 CLI 入口,完成通用 bootstrap、选择节点模式并把 prepared state 交给 runtime assembly
src/runtime/assembly.rs组装 primary / follower 的完整 Forge component graph;直接使用 Forge factory,不为单个 component 增加改名转发层
src/runtime/components.rs构造 Drive 自有资源适配所需的 primary / follower HTTP 和 mail outbox component
src/runtime/startup/common.rs连接数据库、跑 migration、准备默认策略和运行时配置、加载 policy snapshot / driver registry / cache
src/runtime/startup/primary.rs构造 primary 运行时:RuntimeConfig、邮件发送器、SSE 广播、分享下载回滚队列和远端协议运行时
src/runtime/startup/follower.rs构造 follower 运行时:只保留 follower 需要的共享状态
src/runtime/tasks.rs声明 Drive 的 primary/follower worker 与周期任务执行体;生命周期、lease、scheduled claim 和关闭由 Forge 管理
crates/aster_drive_metrics/Drive 产品指标 trait、NoopMetricsaster_forge_metrics 适配;根包不提供旧 metrics 路径兼容导出
src/api/primary.rsprimary 路由注册
src/api/follower.rsfollower 路由注册
src/api/routes/auth/mod.rs认证、会话、偏好、头像、SSE
src/api/routes/files/文件读写、上传、缩略图、版本、WOPI 启动
src/api/routes/folders.rs文件夹接口和团队空间聚合入口;团队 files 路由挂在这里
src/api/routes/tags.rs个人和团队工作空间标签库、实体标签绑定与批量标签操作
src/api/routes/admin/管理后台接口,包括策略、远端节点、用户、团队、分享审计、后台任务、存储迁移、文件 / Blob 可观测、配置、锁、审计
src/api/routes/share_public.rs公开分享页 API、/d 直链、/pv 预览直链
src/api/routes/internal_storage.rsfollower 内部对象存储协议
src/api/routes/remote_tunnel.rsprimary 侧远端节点 reverse tunnel 内部入口
src/services/业务规则集中层
AsterForge aster_forge_http产品无关、严格限制大小的 reqwest 响应体读取;Drive 调用方负责产品错误码和上下文映射
crates/aster_drive_storage/存储 trait、能力扩展、connector descriptor、对象 key 与结构化错误;根包不提供旧路径兼容导出
src/storage/connectors/存储 connector:descriptor、字段、action、连接测试、上传工作流和凭据需求
src/storage/drivers/本地、S3-compatible、SFTP、Azure Blob、Tencent COS、OneDrive 和远端驱动
src/storage/remote_protocol/tunnel/reverse tunnel 传输运行时、鉴权、注册表和流式响应
src/webdav/WebDAV 产品适配、认证、锁持久化与文件系统接入
frontend-panel/React 19 + Vite 前端,构建产物由后端服务

src/main.rs 当前的大致顺序是:

  1. 安装 panic hook
  2. 加载 .env
  3. 如果启用了 cli feature 且传入了 CLI 子命令,先执行对应命令并直接退出
  4. 初始化静态配置
  5. 初始化日志
  6. 清理 runtime 临时目录
  7. 根据 config.server.start_mode 选择 primaryfollower
  8. 把 prepared primary / follower state 交给 src/runtime/assembly.rs,由它使用 aster_forge_runtime::AsterRuntime 组装 HTTP、后台任务、mail outbox、audit 和 database component;启动审计作为 required startup phase 执行

优雅关闭不再由 main.rs 手写调用顺序。src/runtime/assembly.rs 使用 Forge component factory 声明的依赖图是:

primary 的依赖图是:

background_tasks
mail_outbox -> depends_on background_tasks
audit_logs -> depends_on mail_outbox
audit_manager -> depends_on audit_logs
database -> depends_on audit_manager

follower 没有邮件发送器,因此不会注册假的 mail component;它保持 audit_logs -> background_tasks。关闭 primary 时会先停止后台任务,再 drain mail outbox、记录 server_shutdown、flush audit buffer,最后关闭全部 reader / writer DB pool。注册顺序不决定关闭顺序,component graph 会在服务启动前完成校验。

primary 的 audit assembly 直接调用 aster_forge_audit::audit_component_infallible(...)mail-outbox-dependency feature 负责声明 audit_logs -> mail_outbox,Drive 不再重复传递这个依赖,也不需要为内部自行处理错误的审计 hook 重复包装 Ok(())。follower 因为没有 mail outbox,直接调用 audit_component_after_infallible(...) 声明 audit_logs -> background_tasks。database assembly 同样直接调用 aster_forge_db::database_component_after(...),不保留只改名转发的产品 wrapper。

邮件 outbox 的共享边界也已收敛到 Forge:

  • MailTemplateCodeStoredMailPayloadMailOutboxStatus、dispatch stats 和 retry policy 使用 aster_forge_mail
  • mail_outbox SeaORM entity、enqueue、claim/retry/sent/failed 状态机和 active count 使用 aster_forge_db
  • shutdown drain component 使用 aster_forge_mail::mail_outbox_component
  • SMTP sender、message/recipient model、rendered mail 和配置 normalizer 使用 aster_forge_mail;Drive 只保留动态 runtime settings provider、产品模板 payload/渲染、测试邮件文案和发送审计 hook。
  • 历史 baseline 不回改;后续 migration 将 template_code 和 Forge schema contract 对齐,并使用 Forge table/index builder 重建 SQLite 表。

运行时配置的共享边界同样以 Forge 为准:

  • ConfigValueTypeConfigSourceConfigVisibilityConfigValue 使用 aster_forge_config
  • system_config SeaORM entity 和通用 CRUD/default seed 使用 aster_forge_db::system_config
  • RuntimeConfig 的进程内快照使用 aster_forge_config::SyncRuntimeConfig
  • 跨实例 reload 通知使用 aster_forge_config::ConfigSyncRuntime。静态 [config_sync] 只描述 notification backend、endpoint 和 topic;当前 redis-pubsub 只传递 reload hint,配置值仍以数据库为权威存储。
  • primary 和 follower 都在 Forge background_tasks component 内运行订阅 worker,并复用 AsterRuntime 的 root shutdown token。收到其他 runtime 的通知后,从 writer DB 连接重新加载完整快照,避免 reader replica 延迟造成通知后仍读取旧值。
  • 管理 API 在事务提交、本进程快照与派生缓存更新后发布 Api 通知;aster_drive config set/delete/import 在数据库提交后发布 Cli 通知。通知携带 changed keys,用于观测和派生缓存失效,但接收方仍全量加载权威快照。
  • 默认 backend 为 disabled,单实例部署不需要 Redis。启用多实例同步时使用 backend = "redis"、共享 Redis endpoint 和产品级 aster_drive.config_reload topic。
  • ConfigDefinition 上直接注册 normalizer 和 dependency validator,API/CLI 写入统一走 CONFIG_REGISTRY;不再维护 key 分发表或配置校验 facade。
  • Drive 保留具体 key、默认值、领域 normalizer、媒体处理环境引导、preview registry 修复、权限、audit 和 API 响应结构。
  • 已发布的历史 migration 不回改;新的 Drive migration 使用 Forge table/index builder 对齐共享 schema contract。

通用工具与加密实现也直接使用 Forge,不在 Drive 保留 re-export 或改名转发层:

  • UUID/token、checked numeric conversion、loopback 判断、临时文件清理、RAII guard、runtime/upload/task path builder 使用 aster_forge_utils
  • 密码 Argon2 hash/verify、SHA-256 和 hex 编码使用 aster_forge_crypto;Drive 只负责把 CryptoError 映射为产品内部错误。
  • Drive 的静态路径默认值保留在 src/config/paths.rs;相对路径和 SQLite URL 的通用解析由 Forge 完成,Drive adapter 只负责映射成 config error。
  • HTTP date、If-Match 强比较和 If-None-Match 弱比较使用 aster_forge_utils::http_validators,WebDAV / 文件路由继续负责协议状态码。
  • 资源 owner 检查属于 Drive 权限语义,集中在 crate-private src/ownership.rs,供 repo 和 service 直接使用,不复制判断,也不制造 repo 到 service 的反向依赖。AsterDrive/<version> outbound user-agent 是产品静态配置,保留在 src/config/mod.rs
  • src/utils/ 已删除。新增共享 helper 时先判断应进入具体 Forge crate、产品领域模块还是协议层,不再恢复通用杂物目录。

多实例部署的静态配置示例:

[config_sync]
backend = "redis"
endpoint = "redis://127.0.0.1:6379/"
topic = "aster_drive.config_reload"

所有实例必须连接同一份权威数据库,并使用相同 topic。Redis 不保存配置值,也不补偿丢失的历史通知;实例启动时总会从数据库加载完整 snapshot,运行期间通知只负责提示其他 runtime 再次全量 reload。配置同步相关回归至少运行:

Terminal window
cargo nextest run --profile ci --test-threads=1 --lib services::ops::config::
cargo nextest run --profile ci --test-threads=1 --lib cli::config::tests
cargo check --features cli -j 1
cargo check --tests -j 1

Prometheus 指标不在 main.rs 直接初始化,而是在 prepare_common() 中通过 aster_drive_metrics 创建产品 MetricsRecorder

  • 启用 metrics feature 时初始化 Prometheus registry,并注入 Prometheus recorder
  • 未启用时注入 NoopMetrics
  • 业务层、存储驱动 wrapper 和后台任务直接依赖 aster_drive_metrics::MetricsRecorder;需要接入 Forge middleware / DB runtime 时通过 forge_recorder() 暴露 aster_forge_metrics::MetricsRecorder

src/runtime/startup/common.rs 会做所有节点共享的准备:

  1. 创建 MetricsRecorder,让数据库连接和后续运行时状态都能共享同一个 recorder
  2. 连接 writer 数据库
  3. 执行全部 migration
  4. 准备 SQLite 搜索加速能力(若当前后端适用)
  5. 保持产品存储初始化与 deployment profile 无关;两种 profile 的启动流程都不会创建存储策略
  6. primary 模式仅在管理员已经配置默认策略时协调默认策略组;多个 Primary 使用数据库锁串行化同一次协调
  7. 初始化 auth_cookie_secure 引导值,写入 system_config 默认值并清理废弃配置键
  8. follower 模式按需执行环境变量 enrollment bootstrap
  9. primary 模式校验当前数据库中的部署拓扑
  10. 创建 reader 数据库句柄
  11. 重载 PolicySnapshot
  12. 根据节点模式重载 DriverRegistry
  13. 以显式 ReturnError 策略创建配置指定的 cache backend
  14. 构造 config sync runtime

运行态通过 DbHandles 同时保存 writer 和 reader:

  • state.db / state.writer_db() 是 writer。所有事务、写入、读后写、配额权威判断、登录签发 session、refresh token rotation、上传 init/chunk/complete/cancel、依赖 SQLite 单连接模拟锁语义的 repo helper,都必须继续走 writer。
  • state.reader_db() 是纯读入口。SQLite 文件数据库下它会在 writer 完成 migration 和默认数据初始化后打开独立 reader pool,使用 WAL、mode=roPRAGMA query_only=ON;PostgreSQL/MySQL 或内存 SQLite 下它和 writer 指向同一个池。
  • reader 查询允许 WAL 快照级别的短暂滞后。只能用于列表、详情、搜索、上传进度、recoverable sessions、presign 查询阶段、auth snapshot cache miss、public runtime snapshot、admin overview 统计这类不会马上做权威写入判断的路径。
  • 不要把通用校验 helper 偷偷改成 reader,除非已经确认所有调用方都是纯读。更推荐在 service 入口显式选择 reader_db()writer_db(),让调用语义能从代码上看出来。

src/runtime/startup/primary.rs 额外准备:

  • RuntimeConfig
  • 运行时邮件发送器
  • 存储变更广播通道
  • 分享下载回滚队列
  • RemoteProtocolRuntime,包括 reverse tunnel registry,并注入到 DriverRegistry

随后 src/api/primary.rs 注册主路由,并在 src/runtime/tasks.rs 启动 primary 周期任务。

src/runtime/startup/follower.rs 只保留 follower 需要的共享状态。

随后 src/api/follower.rs 仅注册:

  • /api/v1/internal/storage/*
  • /health*

spawn_follower_background_tasks(state) 当前启动 follower-safe 的通用指标后台任务,并启动 reverse tunnel follower worker;它不会启动 primary 的业务清理任务,也不会启动 background-task-dispatch

primary 后台工作由 src/runtime/tasks.rs 声明,通用运行机制使用 aster_forge_tasks

  • background_task_component_with_definitions_from_shutdown(...) 直接接入 AsterRuntime,注册任务定义并复用 root shutdown token。
  • LeasedScheduledRuntimeConfigruntime_leases 保证正常情况下只有一个实例运行 primary scheduler group。
  • ScheduledTaskDbStorescheduled_tasks 持久化每个周期任务的下一次触发与 claim owner。
  • scheduled firing 写入 background_tasks 时使用 nullable unique dedupe_key,leader 切换或重复触发不会产生重复历史记录。
  • Drive 只声明 task kind、运行时 interval、执行体和结果展示;BackgroundTasks、dispatcher backoff、panic recovery、jitter、claim 和 shutdown mechanics 归 Forge。

任务分成常驻 worker 和周期任务:

  • 常驻 worker:share-download-rollbackbackground-task-dispatch(后者运行在 lease-scoped scheduler group 内)
  • 周期任务:
    • mail-outbox-dispatch
    • upload-cleanup
    • completed-upload-cleanup
    • blob-reconcile
    • system-health-check(包含数据库、缓存和远端节点健康检查)
    • trash-cleanup
    • team-archive-cleanup
    • lock-cleanup
    • auth-session-cleanup
    • external-auth-flow-cleanup
    • mfa-flow-cleanup(MFA 登录 flow、TOTP setup flow 和邮箱验证码)
    • audit-cleanup
    • task-cleanup
    • wopi-session-cleanup

周期任务按运行时配置里的间隔执行。Forge 负责 scheduled catalog 与 claim,Drive 负责执行结果。它们只有在有实际结果或失败时才写 SystemRuntime 任务记录;空轮询使用 RuntimeTaskRunOutcome::quiet() 不灌历史表。system-health-check 在连续健康成功时会刷新最近一条成功记录,而不是每轮新增一条噪音记录。

用户可见的 background_tasks 记录由 background-task-dispatch 派发。当前 dispatcher 按任务类型分五条 lane:

  • Archivearchive_compressarchive_extractarchive_preview_generate
  • Thumbnailthumbnail_generateimage_preview_generatemedia_metadata_extract
  • OfflineDownloadoffline_download
  • StorageMigrationstorage_policy_migration
  • Fallbackstorage_policy_temp_cleanuptrash_purge_allblob_maintenancesystem_runtime

前四条业务 lane 分别有自己的运行时并发配置;Fallback 使用通用 background_task_max_concurrency。任务的 lane、payload/result 编解码、steps、重试和执行入口统一由 src/services/task/spec/src/services/task/registry.rs 声明,新增任务不要在 dispatcher、presentation 和创建路径各写一份 kind 分支。

dispatcher 认领任务后会为业务执行创建 TaskExecutionContext。它同时携带 processing-token lease 和 graceful-shutdown token;这个 token 由 AsterRuntime 持有,并通过 HTTP / background-task component 注入 HTTP server、SSE 和后台任务,所以 SIGINT / SIGTERM 到来时,几条链路会一起开始收尾。任务代码、下载轮询、压缩 / 解压的阻塞 worker 都应该通过这个 context 做活跃检查;只有进度写库、runtime metadata 写库和最终状态写库这类底层 helper 直接使用 TaskLeaseGuard。服务关闭时,context 会让执行流协作退出,dispatcher 再把仍匹配当前 processing token 的任务释放回 Retry,不消耗重试次数。

Cargo.toml 里默认 feature 包含 cli,所以默认构建出来的 aster_drive 既能直接启动服务,也能执行离线运维子命令。src/main.rs 会在 HTTP 服务启动前先解析这些子命令:

子命令代码入口当前职责
serve 或无子命令src/main.rs启动 primary / follower HTTP 服务
doctorsrc/cli/doctor/mod.rssrc/cli/doctor/**数据库、migration、运行时配置、存储策略和深度一致性审计
configsrc/cli/config.rs离线读取、设置、导入、导出、校验 system_config
database-migratesrc/cli/database_migration/mod.rssrc/cli/database_migration/**跨数据库后端迁移,支持 dry-run、verify-only 和断点续传
node enrollsrc/cli/node.rsfollower 用主节点签发的 enrollment token 写入本地 master binding

这些 CLI 通常直接连接数据库,不经过 HTTP route 层。改这类能力时先看 src/cli/** 和对应 service,而不是去 src/api/routes/** 里找。

静态配置来自:

  • data/config.toml
  • 环境变量 ASTER__...

主要控制:

  • 监听地址、端口、worker 数
  • 节点启动模式
  • 数据库连接
  • WebDAV 前缀
  • 缓存和日志
  • follower 受管 Local 远端存储目标根目录:server.follower.remote_storage_target_local_root,默认 remote-storage-targets

首次启动会自动创建 data/config.toml。配置文件里的相对路径默认相对于 data/ 解析;兼容旧值时,已经写成 data/... 的相对路径会避免二次拼出 data/data/...。根目录下的旧 config.toml 不再是默认读取位置。

运行时配置保存在数据库 system_config,由管理员接口热更新。

单一数据源在 src/config/definitions.rs,常见键包括:

  • webdav_enabled
  • webdav_block_system_files_enabled
  • webdav_block_system_file_patterns
  • default_storage_quota
  • trash_retention_days
  • team_archive_retention_days
  • max_versions_per_file
  • auth_cookie_secure
  • auth_*_ttl_secs
  • auth_email_code_login_*
  • public_site_url
  • cors_*
  • mail_outbox_dispatch_interval_secs
  • background_task_dispatch_interval_secs
  • background_task_dispatch_idle_max_interval_secs
  • background_task_max_concurrency
  • background_task_archive_max_concurrency
  • background_task_thumbnail_max_concurrency
  • background_task_storage_migration_max_concurrency
  • background_task_max_attempts
  • share_download_rollback_queue_capacity
  • share_stream_session_ttl_secs
  • maintenance_cleanup_interval_secs
  • blob_reconcile_interval_secs
  • remote_node_health_test_interval_secs
  • task_retention_hours
  • archive_extract_*
  • archive_build_*
  • archive_preview_*
  • archive_extract_max_staging_bytes
  • thumbnail_max_source_bytes
  • thumbnail_max_dimension
  • image_preview_max_dimension
  • media_metadata_enabled
  • media_metadata_max_source_bytes
  • media_processing_registry_json
  • wopi_*

system_config.category 只使用 src/config/definitions.rs 里登记的分区常量。当前分区口径是:

  • site / site.preview:站点公开入口、品牌和预览应用
  • user.registration_and_login / user.avatar:注册登录和头像
  • auth:认证 Cookie 和 token TTL
  • mail.config / mail.template:发信配置和邮件模板
  • network:CORS 等网络访问规则
  • runtime.mail / runtime.background_task / runtime.maintenance / runtime.limits / runtime.share_stream:运行时派发、维护和限制
  • storage:版本、回收站、团队归档和默认配额等存储保留策略
  • file_processing.archive_extract / file_processing.archive_preview / file_processing.archive_build / file_processing.media:压缩包和媒体处理
  • webdav / audit:WebDAV 和审计日志

新增分区时必须同步更新允许列表和前端 zh/en i18n。ALL_CONFIGS 的单元测试会拒绝未登记分区,也会检查二级分区是否有前端标题和描述文案。

public_site_url 是一个历史上保持单数 key 的列表配置。配置类型是 string_array,管理 API 暴露为字符串数组,数据库值保存为规范化后的 JSON 数组字符串。生成绝对 URL 时,有请求上下文的路径会优先用当前请求 scheme/Host 在列表里做精确匹配;没有请求上下文或未命中时使用第一项作为回退。这个配置也参与 Cookie 认证写操作的 same-site CSRF 来源判断,但不参与 CORS 放行。

你要改的东西优先落点
新增主节点 REST 接口src/api/routes/**
新增 follower 内部协议能力src/api/routes/internal_storage.rssrc/storage/remote_protocol/
权限、配额、锁、版本、分享范围、团队语义src/services/**
新增查询、分页、过滤条件src/db/repository/**
存储 connector descriptor、连接测试、驱动 action、上传策略和对象读写规则src/storage/**
WebDAV 协议行为src/webdav/**
表字段、索引、默认值crates/aster_drive_migration/ + crates/aster_drive_model/src/entities/**
前端页面、状态管理、SDK 调用frontend-panel/src/**

如果你发现复杂业务判断写在 route 层,基本就是代码气味。