背景
- 早期的 S3 配置是零散的:
- 端点、AccessKey、SecretKey、Bucket 可能通过多个入口修改。
- 没有“确认后一次性生效”的概念。
- 实际上,S3 是 Go 核心的依赖,Flutter 只是前端配置界面:
- 改 S3 配置本质上是“改核心进程的启动参数/环境”。
- 从使用心智上,更符合“改完之后重启核心服务”的模型。
- 同时,后续还计划用 gomobile 把核心跑在同一进程内,需要预留“重启核心”的能力。
本次调整的目标:
- 把 S3 相关设置收敛到一个弹窗中,一次填完再确认。
- 预置一个
_restartCore空实现,将来对接 gomobile 时直接填充。
实现总览
- 主要改动集中在设置页:
- 统一入口:
client/lib/ui/settings_page.dart:544-581 - 弹窗逻辑:
_showS3ConfigDialog,client/lib/ui/settings_page.dart:169-301 - 预留的重启方法:
_restartCore,client/lib/ui/settings_page.dart:169
- 统一入口:
- 状态与持久化由
AppState提供:- S3 偏好加载:
_loadPreferences,client/lib/core/state/app_state.dart:133-151 - S3 相关 setter:
setS3Endpoint/setS3AccessKey/setS3SecretKey/setS3Bucket/setS3UseSsl,client/lib/core/state/app_state.dart:286-337
- S3 偏好加载:
统一 S3 配置弹窗设计
入口展示
- 在设置页的“存储”区域新增“
S3 配置”入口:- 左侧图标使用
MdiIcons.cloud。 - 副标题根据当前配置智能展示:
- 若端点和桶均为空:显示“使用默认配置”。
- 若只有桶:显示“默认端点 · 桶:{bucket}”。
- 若只有端点:显示“{endpoint}”。
- 若两者都有:显示“{endpoint} · 桶:{bucket}”。
- 代码位置:
client/lib/ui/settings_page.dart:544-581。
- 左侧图标使用
弹窗内容
- 调用入口:点击
ListTile后执行_showS3ConfigDialog。 - 弹窗内部字段:
S3 端点:- 文本框,默认填入
appState.s3Endpoint ?? ''。 - 提示:
例如:http://127.0.0.1:9000,留空使用默认。
- 文本框,默认填入
AccessKey:- 文本框,默认填入
appState.s3AccessKey ?? ''。 - 提示:
访问密钥,留空表示不设置。
- 文本框,默认填入
SecretKey:- 文本框,
obscureText: true。 - 默认填入
appState.s3SecretKey ?? ''。 - 提示:
访问密钥,留空表示不设置。
- 文本框,
存储桶:- 文本框,默认填入
appState.s3Bucket ?? ''。 - 提示:
例如:e2e-pan,留空表示使用默认。
- 文本框,默认填入
使用 HTTPS开关:- 绑定
useSsl,初始值为appState.s3UseSsl。 - 提示文案:
默认关闭,以提升传输速度。
- 绑定
- 弹窗的确认逻辑:
取消:Navigator.pop(ctx, false),不保存。确认:Navigator.pop(ctx, true),继续后续保存流程。- 位置:
client/lib/ui/settings_page.dart:169-243。
保存行为
- 弹窗关闭后,如果选择确认:
- 分别从四个
TextEditingController和useSsl中读取最终值。 - 将空字符串视为“恢复默认/不设置”:
- endpoint 为空:
setS3Endpoint(null)。 - accessKey 为空:
setS3AccessKey(null)。 - secretKey 为空:
setS3SecretKey(null)。 - bucket 为空:
setS3Bucket(null)。
- endpoint 为空:
- 非空时按原样保存。
- 分别从四个
- TLS 选项:
- 直接通过
setS3UseSsl(useSsl)写入偏好。
- 直接通过
- 所有 setter 统一更新
SharedPreferences并notifyListeners:- 参考:
client/lib/core/state/app_state.dart:286-339。
- 参考:
- 保存结束后:
- 调用
_restartCore()。 - 弹出
SnackBar:“已更新 S3 配置”。
- 调用
_restartCore 空实现的意义
当前行为
- 方法定义:
client/lib/ui/settings_page.dart:169- 签名:
Future<void> _restartCore() async {}。 - 目前不做任何事,只是一个占位。
- 签名:
- 原因:
- 当前架构下,Go 核心是一个独立进程,通过 HTTP 在本地/局域网访问。
- Flutter 客户端实际上无法直接“重启核心”,只能提示手动重启。
- 提前在 UI 里把“改配置之后要重启核心”的意图明确下来。
未来对接思路
- 当后续切换到 gomobile / FFI 方案时,这个函数将成为对接点:
- 可能的行为:
- 优雅停止当前核心实例(关闭所有连接、flush 持久化状态)。
- 携带新的配置重新初始化核心。
- 重新触发健康检查和 bootstrap 流程。
- 可能的行为:
- 相比于到处散落的“改配置后自己想办法”的代码,一个统一的
_restartCore:- 有利于集中处理错误和状态迁移。
- 可以在这里统一处理“重启后 token、元数据缓存、传输队列”等衔接逻辑。
收获
- 交互层面:
- S3 设置从“分散、多入口”收敛成一个弹窗,认知开销更低。
- 一次填完、一次确认,避免某个字段忘记点“保存”的情况。
- 工程层面:
- 用
AppState封装所有 S3 偏好,UI 只关心展示与收集。 - 通过
setS3*系列方法保证:- 内存状态更新。
SharedPreferences中的持久化同步。- 下游依赖者收到变更通知。
- 用
- 架构演进层面:
_restartCore提前站好坑,未来接 gomobile 时不会反过来找 UI 里的“重启核心”逻辑。- 当前阶段保持空实现,不增加多余复杂度。