AsterDrive 架构概览
本文描述的是当前仓库已经落地的实现,不是早期设计草图。
如果你刚接手这个仓库,建议先看这页,再看 module-designs.md。
给新开发者的 60 秒版本
Section titled “给新开发者的 60 秒版本”- AsterDrive 现在不是“只有一个运行模式的单体服务”,而是同一套代码支持两种节点模式:
primary:对外提供主 REST API、公开分享、WebDAV、前端页面,并负责运行时配置和后台任务follower:只暴露健康检查和内部对象存储协议,给远端主节点当受管存储节点
- 元数据主要在数据库里,文件内容主要在存储驱动里;两者通过
files、file_blobs、file_versions、upload_sessions等表关联 - 个人空间和团队空间共用同一条文件主链路,只是在 route / service 层通过
WorkspaceStorageScope切换作用域 - 后端主线仍然是:
src/api/routes/*->src/services/*->src/db/repository/*/src/storage/* - WebDAV 不是普通 REST 路由的一个分支,而是独立挂载在
src/webdav/ - 运行二进制默认启动 HTTP 服务;启用默认
clifeature 时,同一入口还提供doctor、config、database-migrate、node 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 / follower | src/main.rs、src/config/node_mode.rs、src/runtime/startup/ | 这里决定启动模式、运行时状态和节点职责 |
| 运维 CLI 怎么执行 | src/main.rs、src/cli/** | cli feature 下的子命令在进入 HTTP 启动前分派 |
| 主节点挂了哪些路由 | src/api/primary.rs、src/api/routes/ | 这里决定 /api/v1、/health、/d、/pv、WebDAV 和前端兜底的注册顺序 |
| 从节点到底暴露什么 | src/api/follower.rs、src/api/routes/internal_storage.rs | follower 只负责内部存储协议和健康检查 |
| 远端节点反向隧道怎么走 | src/api/routes/remote_tunnel.rs、src/storage/remote_protocol/tunnel/ | primary 暴露 tunnel 控制面,follower 主动连回 primary |
| 一个 REST 接口怎么实现 | 对应 src/api/routes/** 文件 | route 层做参数解析、鉴权包装和响应适配 |
| 文件 / 团队 / 分享 / 上传的业务规则在哪 | src/services/** | 业务语义集中在 service 层,不应散落在 route 里 |
| 数据怎么查怎么写 | src/db/repository/** | repo 层封装数据库访问和跨库兼容细节 |
| 文件内容怎么落盘 / 上对象存储 / 走 OneDrive 或远端节点 | src/storage/** | connector descriptor、驱动抽象、具体驱动和远端协议都在这里 |
| WebDAV 为什么和 REST 不一样 | src/webdav/** | 这是单独的协议接入层 |
| 团队空间为什么复用个人空间语义 | src/services/workspace/scope/、src/services/workspace/storage/、src/services/workspace/models.rs、src/services/workspace/storage_core/、src/services/files/folder/、src/services/files/file/ | scope 切换、上传编排和统一存储核心链路都在这里 |
| 表结构怎么演进 | migration/、src/entities/** | migration 和 entity 必须一起看 |
追一个具体功能时,最省时间的路径通常是:
- 先从对应
src/api/routes/**找入口 - 再跳到
src/services/** - 最后看
src/db/repository/**、src/storage/**或src/webdav/**
运行模式与系统边界
Section titled “运行模式与系统边界”Primary 节点
Section titled “Primary 节点”primary 会注册这些入口:
- REST API:
/api/v1/* - 远端节点反向隧道内部接口:
/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 节点
Section titled “Follower 节点”follower 不提供普通用户 API、WebDAV 或前端页面,只注册:
- 健康检查:
/health* - 内部对象存储协议:
/api/v1/internal/storage/*
这条内部协议当前用于主节点和受管远端节点之间的对象写入、对象拼接、对象列举、绑定同步与远端存储目标控制面。当前协议版本为 v5、兼容下限为 v4,primary 与 follower 声明的支持区间必须有交集。0.4.0 起控制面只保留 /targets 路径,旧 /ingress-profiles 兼容路由已经移除。
如果远端节点使用 reverse_tunnel 或 auto 且没有可直连的 base_url,follower 不会额外暴露 primary 可直连的入口,而是由 follower 进程里的 tunnel worker 主动连接 primary 的 /api/v1/internal/remote-tunnel/*。
一个请求如何流转
Section titled “一个请求如何流转”Primary 上的普通 REST 请求
Section titled “Primary 上的普通 REST 请求”src/main.rs进入run_primary_http_server()src/api/primary.rs注册/api/v1下的各模块路由- 请求先经过全局中间件:
- 压缩
- Request ID
- 运行时 CORS
- 安全响应头
- 命中对应
src/api/routes/**handler - 受保护接口再经过路由级 JWT 鉴权和限流
src/services/**执行业务规则src/db/repository/**负责数据库读写;涉及二进制内容时进入src/storage/**- route 层返回统一 JSON,或者直接返回文件流 / SSE / WebDAV / Prometheus 文本响应
例外要记住:
/d/...、/pv/...、文件下载、缩略图、分享下载不走统一 JSON 包装GET /api/v1/auth/events/storage是 SSEGET /health/metrics是 Prometheus text exposition- 前端兜底路由最后注册,所以 API / WebDAV 必须先于它注册
Follower 上的内部存储请求
Section titled “Follower 上的内部存储请求”src/api/follower.rs只注册/api/v1/internal/storage/*src/api/routes/internal_storage.rs校验内部签名或预签名访问remote::master_binding解析主节点绑定关系,remote::storage_target解析 follower 侧远端存储目标- 通过
driver_registry取得实际存储驱动 - 请求落到本地 / 对象存储 / 远端驱动能力接口
如果你在查远端节点写入问题,不要先去普通 files / upload 路由里找。
远端节点有两种传输方式:
direct:primary 直接向 follower 的/api/v1/internal/storage/*发 HTTP 请求。reverse_tunnel:primary 把内部存储请求登记到 tunnel registry;follower 通过/api/v1/internal/remote-tunnel/poll//complete或/connectWebSocket 主动取走请求并回传结果。
auto 会根据远端节点是否有非空 base_url 选择 direct 或 reverse tunnel。
WebDAV 请求
Section titled “WebDAV 请求”WebDAV 不走 src/api/routes/**,而是:
- 由
crate::webdav::configure()在 primary 上挂到配置的 prefix - 检查运行时开关
webdav_enabled - 做 WebDAV 专用 Basic Auth 认证
- 为请求构造带用户上下文的
AsterDavFs - 使用数据库锁系统和版本能力
- 进入自研 WebDAV / DeltaV handler
┌─────────────────────────────────────────────┐│ 接入层 ││ - React 前端 / 公开分享页 ││ - REST API (primary) ││ - Internal Storage API (follower) ││ - WebDAV / DeltaV │├─────────────────────────────────────────────┤│ 应用层 ││ - 路由、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 管理 |
src/metrics.rs | Drive 产品指标 trait、NoopMetrics 和 aster_forge_metrics 适配;Prometheus recorder 仅在 metrics feature 启用时创建 |
src/api/primary.rs | primary 路由注册 |
src/api/follower.rs | follower 路由注册 |
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.rs | follower 内部对象存储协议 |
src/api/routes/remote_tunnel.rs | primary 侧远端节点 reverse tunnel 内部入口 |
src/services/ | 业务规则集中层 |
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 文件系统、认证、锁与 DeltaV 支持 |
frontend-panel/ | React 19 + Vite 前端,构建产物由后端服务 |
通用启动步骤
Section titled “通用启动步骤”src/main.rs 当前的大致顺序是:
- 安装 panic hook
- 加载
.env - 如果启用了
clifeature 且传入了 CLI 子命令,先执行对应命令并直接退出 - 初始化静态配置
- 初始化日志
- 清理 runtime 临时目录
- 根据
config.server.start_mode选择primary或follower - 把 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_tasksmail_outbox -> depends_on background_tasksaudit_logs -> depends_on mail_outboxaudit_manager -> depends_on audit_logsdatabase -> depends_on audit_managerfollower 没有邮件发送器,因此不会注册假的 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:
MailTemplateCode、StoredMailPayload、MailOutboxStatus、dispatch stats 和 retry policy 使用aster_forge_mail。mail_outboxSeaORM 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 为准:
ConfigValueType、ConfigSource、ConfigVisibility、ConfigValue使用aster_forge_config。system_configSeaORM 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_taskscomponent 内运行订阅 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_reloadtopic。 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/types/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。配置同步相关回归至少运行:
cargo test --lib services::ops::config:: -j 1cargo test --lib cli::config::tests -j 1cargo check --features cli -j 1cargo check --tests -j 1Prometheus 指标不在 main.rs 直接初始化,而是在 prepare_common() 中通过 src/metrics.rs 创建产品 MetricsRecorder:
- 启用
metricsfeature 时初始化 Prometheus registry,并注入 Prometheus recorder - 未启用时注入
NoopMetrics - 业务层、存储驱动 wrapper 和后台任务依赖
crate::metrics::MetricsRecorder;需要接入 Forge middleware / DB runtime 时通过forge_recorder()暴露aster_forge_metrics::MetricsRecorder
prepare_common()
Section titled “prepare_common()”src/runtime/startup/common.rs 会做所有节点共享的准备:
- 创建
MetricsRecorder,让数据库连接和后续运行时状态都能共享同一个 recorder - 连接数据库
- 执行全部 migration
- 准备 SQLite 搜索加速能力(若当前后端适用)
- 确保至少存在一个默认本地存储策略
- 仅 primary 模式下补种默认策略组
- 初始化
auth_cookie_secure引导值 - 写入
system_config默认值 - 清理废弃的
node_runtime_mode和旧 thumbnail 运行时配置键 - 重载
PolicySnapshot - 根据节点模式重载
DriverRegistry - 初始化缓存后端
数据库连接句柄
Section titled “数据库连接句柄”运行态通过 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=ro和PRAGMA 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(),让调用语义能从代码上看出来。
Primary 特有启动
Section titled “Primary 特有启动”src/runtime/startup/primary.rs 额外准备:
RuntimeConfig- 运行时邮件发送器
- 存储变更广播通道
- 分享下载回滚队列
RemoteProtocolRuntime,包括 reverse tunnel registry,并注入到DriverRegistry
随后 src/api/primary.rs 注册主路由,并在 src/runtime/tasks.rs 启动 primary 周期任务。
Follower 特有启动
Section titled “Follower 特有启动”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。LeasedScheduledRuntimeConfig用runtime_leases保证正常情况下只有一个实例运行 primary scheduler group。ScheduledTaskDbStore用scheduled_tasks持久化每个周期任务的下一次触发与 claim owner。- scheduled firing 写入
background_tasks时使用 nullable uniquededupe_key,leader 切换或重复触发不会产生重复历史记录。 - Drive 只声明 task kind、运行时 interval、执行体和结果展示;
BackgroundTasks、dispatcher backoff、panic recovery、jitter、claim 和 shutdown mechanics 归 Forge。
任务分成常驻 worker 和周期任务:
- 常驻 worker:
share-download-rollback、background-task-dispatch(后者运行在 lease-scoped scheduler group 内) - 周期任务:
mail-outbox-dispatchupload-cleanupcompleted-upload-cleanupblob-reconcilesystem-health-check(包含数据库、缓存和远端节点健康检查)trash-cleanupteam-archive-cleanuplock-cleanupauth-session-cleanupexternal-auth-flow-cleanupmfa-flow-cleanup(MFA 登录 flow、TOTP setup flow 和邮箱验证码)audit-cleanuptask-cleanupwopi-session-cleanup
周期任务按运行时配置里的间隔执行。Forge 负责 scheduled catalog 与 claim,Drive 负责执行结果。它们只有在有实际结果或失败时才写 SystemRuntime 任务记录;空轮询使用 RuntimeTaskRunOutcome::quiet() 不灌历史表。system-health-check 在连续健康成功时会刷新最近一条成功记录,而不是每轮新增一条噪音记录。
用户可见的 background_tasks 记录由 background-task-dispatch 派发。当前 dispatcher 按任务类型分五条 lane:
Archive:archive_compress、archive_extract、archive_preview_generateThumbnail:thumbnail_generate、image_preview_generate、media_metadata_extractOfflineDownload:offline_downloadStorageMigration:storage_policy_migrationFallback:storage_policy_temp_cleanup、trash_purge_all、blob_maintenance、system_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,不消耗重试次数。
CLI 与离线运维入口
Section titled “CLI 与离线运维入口”Cargo.toml 里默认 feature 包含 cli,所以默认构建出来的 aster_drive 既能直接启动服务,也能执行离线运维子命令。src/main.rs 会在 HTTP 服务启动前先解析这些子命令:
| 子命令 | 代码入口 | 当前职责 |
|---|---|---|
serve 或无子命令 | src/main.rs | 启动 primary / follower HTTP 服务 |
doctor | src/cli/doctor/mod.rs、src/cli/doctor/** | 数据库、migration、运行时配置、存储策略和深度一致性审计 |
config | src/cli/config.rs | 离线读取、设置、导入、导出、校验 system_config |
database-migrate | src/cli/database_migration/mod.rs、src/cli/database_migration/** | 跨数据库后端迁移,支持 dry-run、verify-only 和断点续传 |
node enroll | src/cli/node.rs | follower 用主节点签发的 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_enabledwebdav_block_system_files_enabledwebdav_block_system_file_patternsdefault_storage_quotatrash_retention_daysteam_archive_retention_daysmax_versions_per_fileauth_cookie_secureauth_*_ttl_secsauth_email_code_login_*public_site_urlcors_*mail_outbox_dispatch_interval_secsbackground_task_dispatch_interval_secsbackground_task_dispatch_idle_max_interval_secsbackground_task_max_concurrencybackground_task_archive_max_concurrencybackground_task_thumbnail_max_concurrencybackground_task_storage_migration_max_concurrencybackground_task_max_attemptsshare_download_rollback_queue_capacityshare_stream_session_ttl_secsmaintenance_cleanup_interval_secsblob_reconcile_interval_secsremote_node_health_test_interval_secstask_retention_hoursarchive_extract_*archive_build_*archive_preview_*archive_extract_max_staging_bytesthumbnail_max_source_bytesthumbnail_max_dimensionimage_preview_max_dimensionmedia_metadata_enabledmedia_metadata_max_source_bytesmedia_processing_registry_jsonwopi_*
system_config.category 只使用 src/config/definitions.rs 里登记的分区常量。当前分区口径是:
site/site.preview:站点公开入口、品牌和预览应用user.registration_and_login/user.avatar:注册登录和头像auth:认证 Cookie 和 token TTLmail.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 放行。
改动应该落在哪一层
Section titled “改动应该落在哪一层”| 你要改的东西 | 优先落点 |
|---|---|
| 新增主节点 REST 接口 | src/api/routes/** |
| 新增 follower 内部协议能力 | src/api/routes/internal_storage.rs、src/storage/remote_protocol/ |
| 权限、配额、锁、版本、分享范围、团队语义 | src/services/** |
| 新增查询、分页、过滤条件 | src/db/repository/** |
| 存储 connector descriptor、连接测试、驱动 action、上传策略和对象读写规则 | src/storage/** |
| WebDAV 协议行为 | src/webdav/** |
| 表字段、索引、默认值 | migration/ + src/entities/** |
| 前端页面、状态管理、SDK 调用 | frontend-panel/src/** |
如果你发现复杂业务判断写在 route 层,基本就是代码气味。