feat(server,dashboard): 引入多工作流数据隔离、安全中间件与前端 ESM 模块化重构

- server: 实现按 workflow_name 的多工作流数据隔离与旧数据库平滑迁移机制
- server: 新增 API Key 认证(auth)、限流中间件(rate_limit)与运维备份接口(admin)
- server: 统一 AppError 错误处理体系,重构调度器 scheduler 支持工作流级重置与抢占
- node: 节点 ID 缺失时自动生成随机 UUID,原生支持 `docker compose --scale node=N` 动态扩容
- dashboard: 前端模块化重构(state/api/components),升级 CSS 变量设计系统与 Toast 通知
- docker/docs: 更新 /healthz 健康检查、部署脚本 IP 配置及数据库设计文档
This commit is contained in:
fmq
2026-07-28 21:54:02 +08:00
parent 4b4238d702
commit b91f1e4fa5
59 changed files with 7697 additions and 1490 deletions
+55 -19
View File
@@ -46,7 +46,10 @@ async fn main() -> Result<()> {
// 1. 自动兼容扫描旧版 run_grid.py 产物与新版 DCTS 目录结构
let seeds = scan_dir_for_seeds(&args.dir).await?;
info!("扫描完成,共找到 {} 个经校验无 NaN 且物理收敛的合格大气种子!", seeds.len());
info!(
"扫描完成,共找到 {} 个经校验无 NaN 且物理收敛的合格大气种子!",
seeds.len()
);
if seeds.is_empty() {
info!("未查找到符合条件的合格种子文件。");
@@ -69,7 +72,11 @@ async fn scan_dir_for_seeds(dir_path: &Path) -> Result<Vec<CandidateSeed>> {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with('.') || name.contains(".OLD") || name.contains(".FAILED") || name.contains(".coldfail") {
if name.starts_with('.')
|| name.contains(".OLD")
|| name.contains(".FAILED")
|| name.contains(".coldfail")
{
continue;
}
@@ -87,17 +94,21 @@ async fn scan_dir_for_seeds(dir_path: &Path) -> Result<Vec<CandidateSeed>> {
let atmo_7 = candidates_7.into_iter().find(|p| p.is_file());
if conv_json.is_file() && atmo_7.is_some() {
let seed_file = atmo_7.unwrap();
if let Ok(content) = std::fs::read_to_string(&conv_json) {
if let Ok(summary) = serde_json::from_str::<ModelSummary>(&content) {
if summary.converged && !summary.atmosphere_has_nan && !atmosphere_has_nan(&seed_file) {
results.push(CandidateSeed {
point_name: summary.name.clone(),
params: summary.params.clone(),
summary_json: content,
seed_path: seed_file,
});
if conv_json.is_file() {
if let Some(seed_file) = atmo_7 {
if let Ok(content) = std::fs::read_to_string(&conv_json) {
if let Ok(summary) = serde_json::from_str::<ModelSummary>(&content) {
if summary.converged
&& !summary.atmosphere_has_nan
&& !atmosphere_has_nan(&seed_file)
{
results.push(CandidateSeed {
point_name: summary.name.clone(),
params: summary.params.clone(),
summary_json: content,
seed_path: seed_file,
});
}
}
}
}
@@ -109,7 +120,11 @@ async fn scan_dir_for_seeds(dir_path: &Path) -> Result<Vec<CandidateSeed>> {
}
/// 向远程 Master 服务端逐个发送 HTTP POST /api/task/report 请求同步种子与元数据
async fn upload_seeds_to_remote_server(server_url: &str, seeds: &[CandidateSeed], token: Option<&str>) -> Result<()> {
async fn upload_seeds_to_remote_server(
server_url: &str,
seeds: &[CandidateSeed],
token: Option<&str>,
) -> Result<()> {
let client = Client::builder()
.timeout(std::time::Duration::from_secs(60))
.build()?;
@@ -119,7 +134,13 @@ async fn upload_seeds_to_remote_server(server_url: &str, seeds: &[CandidateSeed]
let mut success_count = 0;
for (idx, seed) in seeds.iter().enumerate() {
info!("正在上传种子 [{}/{}] 网格点: {} (路径: {})...", idx + 1, total, seed.point_name, seed.seed_path.display());
info!(
"正在上传种子 [{}/{}] 网格点: {} (路径: {})...",
idx + 1,
total,
seed.point_name,
seed.seed_path.display()
);
let seed_bytes = match tokio::fs::read(&seed.seed_path).await {
Ok(b) => b,
@@ -147,8 +168,16 @@ async fn upload_seeds_to_remote_server(server_url: &str, seeds: &[CandidateSeed]
let seed_file_name = format!("{}.7", seed.point_name);
let form = Form::new()
.part("report", Part::bytes(report_bytes).mime_str("application/json")?)
.part("seed_file", Part::bytes(seed_bytes).file_name(seed_file_name).mime_str("application/octet-stream")?);
.part(
"report",
Part::bytes(report_bytes).mime_str("application/json")?,
)
.part(
"seed_file",
Part::bytes(seed_bytes)
.file_name(seed_file_name)
.mime_str("application/octet-stream")?,
);
let mut req = client.post(&report_url);
if let Some(t) = token {
@@ -161,7 +190,11 @@ async fn upload_seeds_to_remote_server(server_url: &str, seeds: &[CandidateSeed]
info!("网格点 {} 上传成功!", seed.point_name);
}
Ok(resp) => {
warn!("网格点 {} 上传失败,服务端响应 HTTP {}", seed.point_name, resp.status());
warn!(
"网格点 {} 上传失败,服务端响应 HTTP {}",
seed.point_name,
resp.status()
);
}
Err(e) => {
warn!("网格点 {} 网络上传失败: {}", seed.point_name, e);
@@ -169,6 +202,9 @@ async fn upload_seeds_to_remote_server(server_url: &str, seeds: &[CandidateSeed]
}
}
info!("成功将 {}/{} 个有效大气种子上传至 Master 服务端!", success_count, total);
info!(
"成功将 {}/{} 个有效大气种子上传至 Master 服务端!",
success_count, total
);
Ok(())
}