之前的问题:
// ❌ 硬编码目录列表
let directories = [
"config",
"logs",
"logs/mysql",
"app",
"upload",
"backups",
"data",
"data/mysql",
];现在的方案:
// ✅ 自动从 docker-compose.yml 检测
docker_manager.ensure_host_volumes_exist().await?;| 组件 | 职责 | 原因 |
|---|---|---|
| DockerManager | 解析 docker-compose.yml 并创建挂载目录 | 自动化,不需要硬编码 |
| DirectoryPermissionManager | 只设置 MySQL 配置文件权限(644) | MySQL 安全要求 |
| Docker init 容器 | 修改数据目录所有权(chown 999:999) | 需要 root 权限 |
- 900 行 → 176 行(-80%)
- 8 个公开方法 → 2 个(-75%)
- 删除硬编码目录列表 ✅
- 删除 20+ 个未使用方法 ✅
// 1. 自动检测 docker-compose.yml 并创建所有挂载目录
docker_manager.ensure_host_volumes_exist().await?;
// 2. 设置 MySQL 配置文件权限(644)
directory_permission_manager.ensure_mysql_config_safe()?;
// 3. 启动容器(Docker init 容器会 chown 999:999)
docker_manager.start_services().await?;// 只在 MySQL 启动失败时调用
directory_permission_manager.fix_mysql_permissions_on_failure()?;services:
mysql:
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/var/log/mysql
- ./config/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro// DockerManager 会自动:
// 1. 解析所有 volumes 配置
// 2. 识别 bind mount(以 ./ 或 / 开头)
// 3. 提取宿主机路径
// 4. 判断是文件还是目录
// 5. 创建必要的目录
// 对于上面的配置,会创建:
// - data/mysql/ (目录)
// - logs/mysql/ (目录)
// - config/ (mysql.cnf 的父目录)// client-core/src/container/volumes.rs
impl DockerManager {
pub async fn ensure_host_volumes_exist(&self) -> Result<()> {
// 1. 加载 docker-compose.yml
let compose_config = self.load_compose_config()?;
// 2. 提取所有挂载目录
let mount_directories = self.extract_mount_directories(&compose_config)?;
// 3. 创建不存在的目录
for mount_info in mount_directories {
if let Some(host_path) = &mount_info.host_path {
if mount_info.is_bind_mount {
self.create_host_directory_if_not_exists(host_path)?;
}
}
}
Ok(())
}
}之前:
- 每次修改 docker-compose.yml 都要同步更新 Rust 代码
- 容易遗漏或不一致
- 维护成本高
现在:
- 自动从 docker-compose.yml 读取
- 修改配置文件后自动生效
- 零维护成本 ✅
# 用户可以使用自定义的 docker-compose.yml
nuwax-cli auto-upgrade-deploy run --config custom-compose.yml
# DockerManager 会自动解析自定义配置
# 不需要修改任何代码# 用户可以随意修改挂载路径
services:
mysql:
volumes:
- ./my-custom-data:/var/lib/mysql # 自定义路径
- ./my-logs:/var/log/mysql # 自定义路径DockerManager 会自动创建 my-custom-data/ 和 my-logs/,不需要修改代码。
impl DirectoryPermissionManager {
/// 确保 MySQL 配置文件权限为 644
pub fn ensure_mysql_config_safe(&self) -> DockerServiceResult<()> {
// 1. 检查 config/mysql.cnf 是否存在
// 2. 检查权限是否为 group-writable 或 world-writable
// 3. 如果不安全,设置为 644
// 4. 验证设置成功
}
/// MySQL 失败时的修复逻辑
pub fn fix_mysql_permissions_on_failure(&self) -> DockerServiceResult<()> {
// 1. 检查目录是否存在(应该已经由 ensure_host_volumes_exist 创建)
// 2. 设置最宽松权限(777)尝试修复
// 3. 确保配置文件权限正确(644)
}
}- ❌ 创建目录(由 DockerManager 处理)
- ❌ 硬编码目录列表
- ❌ 复杂的权限检查逻辑
- ❌ 数据清理逻辑
- ❌ WSL2 特殊处理
- ❌ 容器启动后维护
cargo run -- auto-upgrade-deploy run
# 应该自动创建所有需要的目录
# 应该设置 mysql.cnf 为 644# 修改 docker-compose.yml,添加新的挂载
volumes:
- ./new-directory:/app/new
# 启动服务
cargo run -- auto-upgrade-deploy run
# 验证 new-directory 被自动创建
ls -la new-directory# 设置错误权限
chmod 777 docker/config/mysql.cnf
# 启动服务
cargo run -- auto-upgrade-deploy run
# 验证权限被自动修复
ls -la docker/config/mysql.cnf # 应该是 644- 删除硬编码 - 不再维护目录列表
- 自动化 - 从 docker-compose.yml 自动检测
- 灵活性 - 支持自定义配置
- 简化 - 代码减少 80%
- 职责清晰 - 每个组件只做一件事
不要重复配置
docker-compose.yml 已经定义了挂载目录,不要在 Rust 代码中再定义一遍。
自动化优于手动
让代码自动检测和处理,而不是手动维护列表。
单一职责
每个组件只做一件事,做好一件事。
- ✅ 代码更简洁(-80%)
- ✅ 更易维护(无硬编码)
- ✅ 更灵活(支持自定义)
- ✅ 更可靠(自动化)
- ✅ 功能完整(所有测试通过)