核心变更:
1. GridAxisValue 源精度命名
- 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
- config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
- runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
修复 REAL 列回读丢精度导致的 model_name 错配
2. 工作流执行可观测台
- 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
停滞预警、逐点明细分页、收敛性热力图数据)
- 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
- runner 携带 last_iter/worst_depth/n_depths 进 conv.json
- 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器
3. 节点停用/启用管理
- 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
移除 host_name 字段
4. 白名单结果归档
- 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
- executor 原子写入归档 + 200 点 LRU 上限
5. 历史数据导入
- sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
按新版命名迁移产物树
6. 部署与目录重规划
- data/results→seeds、data/archive→result + migrate_data_dirs.sh
- deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身
7. 文档同步更新 api/database/architecture/deployment
42 KiB
DCTS (Distributed Computing TLUSTY/SYNSPEC) API 文档
本文档由源代码自动提取并整理,详细说明了 DCTS 分布式恒星大气网格计算系统 服务端 (dcts_server) 提供的所有 RESTful API 接口规范、数据结构定义、鉴权机制、错误码及 curl 调用示例。
目录 (Table of Contents)
- 通用说明与鉴权机制
- 数据结构与类型定义 (Rust & TypeScript Schema)
- 计算节点管理 API (Node Management)
- 任务调度与结果上报 API (Task Processing)
- 种子文件管理 API (Seed Management)
- 静态资源与数据下载 API (Data Assets)
- 系统状态监控 API (System Status)
- 工作流管理 API (Workflow CRUD & Execution)
- 错误处理与状态码汇总
1. 通用说明与鉴权机制
1.1 服务端信息
- 默认服务地址:
http://127.0.0.1:8090(端口可通过--port/DCTS_PORT环境变量配置) - 传输协议: HTTP / HTTPS
- 默认请求/响应格式:
application/json(部分文件下载接口为application/octet-stream,任务上报为multipart/form-data)
1.2 鉴权机制与权限控制矩阵 (auth_middleware & RBAC)
服务端基于角色访问控制 (RBAC) 划分三种请求鉴权级别:
- Admin 角色:具有系统管理权限(工作流 CRUD/起停、节点凭据审批/重发/停用/启用、系统恢复)。在 Request Header 中需携带:
Authorization: Bearer <DCTS_ADMIN_TOKEN> # 或 x-api-key: <DCTS_ADMIN_TOKEN> - Node 角色:仅限 Worker 节点运行态调用(心跳/抢占任务/汇报/下载数据)。携带管理员审批颁发的专属节点 Token:
Authorization: Bearer <NODE_SPECIFIC_TOKEN> - Public 角色:无需鉴权直接访问(如
/login管理登录、/node/register提交申请、/node/check_status轮询审批、/healthz健康检查)。
Tip
防侧信道保护:底层调用 Constant-Time 等时比较机制 (
Sha256摘要匹配),彻底规避侧信道攻击(Side-Channel Attack)。
1.3 限流机制与错误模型 (Rate Limiting & Unified Error)
- API 限流 (Rate Limiting):服务端对敏感接口(如
/login、/node/register)应用了基于 Governor / 漏桶算法的请求限流器。超出速率上限时返回429 Too Many Requests。 - 统一错误格式 (AppError):所有 RESTful API 的错误响应均格式化为标准 JSON:
{ "success": false, "message": "错误原因详细说明", "data": null }
2. 数据结构与类型定义 (Rust & TypeScript Schema)
2.1 6维网格点参数 (GridPointParams)
定义恒星大气模型的 6 维大气参数:温度 $T_{\text{eff}}$、重力加速度 $\log g$、以及元素丰度 \log(N_{\text{He}}/N_{\text{H}}), \log(N_{\text{C}}/N_{\text{H}}), \log(N_{\text{N}}/N_{\text{H}}), $\log(N_{\text{O}}/N_{\text{H}})$。
-
Rust 定义 (models.rs):
// 每个轴值携带数值与 YAML 源书写文本,使命名严格忠于源精度。 #[derive(Debug, Clone, PartialEq)] pub struct GridAxisValue { /* 内部:value: f64 + text: 源文本 */ } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct GridPointParams { pub teff: GridAxisValue, // 有效温度 (K), e.g. 35000.0 pub logg: GridAxisValue, // 表面重力加速度对数 (cgs), e.g. 5.5 pub loghe: GridAxisValue, // 氦丰度对数, e.g. -1.0 pub logc: GridAxisValue, // 碳丰度对数, e.g. -2.0 pub logn: GridAxisValue, // 氮丰度对数, e.g. -2.0 pub logo: GridAxisValue, // 氧丰度对数, e.g. -2.0 }serde 行为:
GridAxisValue序列化为纯数值(f64),下游 JSON 消费者无感; 反序列化时优先捕获 YAML/JSON 标量原文。命名精度:model_name()直接拼接各轴 源书写文本——配置里多少位小数就多少位(logg: 5.0→g5.0,teff: 20000→t20000), 与旧版 Pythongen_input5.model_name逐字符一致,保证历史数据可迁移。详见GridConfig::from_yaml_str(YAML 源文本捕获路径)。 -
TypeScript 类型声明:
export interface GridPointParams { teff: number; logg: number; loghe: number; logc: number; logn: number; logo: number; }
2.2 任务规格 (TaskSpec)
服务端派发给 Worker 节点的单个计算任务定义。
-
Rust 定义 (models.rs):
#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TaskSpec { pub task_id: Uuid, pub point_name: String, pub params: GridPointParams, pub task_type: TaskType, // ColdRun | SeedStep pub seed_point_name: Option<String>,// 步进种子点名称(如适用) pub timeout_sec: u64, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum TaskType { ColdRun, SeedStep, } -
TypeScript 类型声明:
export type TaskType = 'cold_run' | 'seed_step'; export interface TaskSpec { task_id: string; point_name: string; params: GridPointParams; task_type: TaskType; seed_point_name?: string | null; timeout_sec: number; }
2.3 任务上报报告 (TaskReport)
Worker 节点向服务端上报的任务计算结果。
-
Rust 定义 (models.rs):
#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TaskReport { pub task_id: Uuid, pub point_name: String, #[serde(default)] pub params: Option<GridPointParams>, pub node_id: String, pub status: TaskStatus, // Pending | Running | Completed | Failed | Timeout pub converged: bool, pub max_relc: Option<f64>, pub atmosphere_has_nan: bool, pub elapsed_sec: f64, pub error_message: Option<String>, pub summary_json: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum TaskStatus { Pending, Running, Completed, Failed, Timeout, } -
TypeScript 类型声明:
export type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' | 'timeout'; export interface TaskReport { task_id: string; point_name: string; params?: GridPointParams; node_id: string; status: TaskStatus; converged: boolean; max_relc?: number | null; atmosphere_has_nan: boolean; elapsed_sec: number; error_message?: string | null; summary_json: string; }
2.4 节点信息与请求模型 (NodeRegisterRequest / NodeHeartbeatRequest)
-
Rust 定义 (models.rs):
pub struct NodeRegisterRequest { pub node_id: String, pub max_slots: i32, } pub struct NodeHeartbeatRequest { pub node_id: String, pub active_slots: i32, pub cpu_usage: f32, pub memory_usage: f32, } pub struct NodeInfo { pub node_id: String, pub max_slots: i32, pub active_slots: i32, pub status: String, pub cpu_usage: f32, pub memory_usage: f32, pub last_heartbeat: DateTime<Utc>, } -
TypeScript 类型声明:
export interface NodeRegisterRequest { node_id: string; max_slots: number; } export interface NodeHeartbeatRequest { node_id: string; active_slots: number; cpu_usage: number; memory_usage: number; } export interface NodeInfo { node_id: string; max_slots: number; active_slots: number; status: 'online' | 'offline'; cpu_usage: number; memory_usage: number; last_heartbeat: string; }节点
status取值:'online'(在线分发中)、'offline'(心跳超时离线)、'pending_approval'(待管理员审批)、'disabled'(管理员手动停用——在线但不分发任务,详见/disable)。
2.5 工作流响应模型与请求体 (CreateWorkflowRequest / ApiResponse<T>)
-
Rust 定义 (workflow.rs):
pub struct CreateWorkflowRequest { pub name: String, pub description: Option<String>, pub config_yaml: String, } pub struct ApiResponse<T> { pub success: bool, pub message: String, pub data: Option<T>, } -
TypeScript 类型声明:
export interface CreateWorkflowRequest { name: string; description?: string | null; config_yaml: string; } export interface ApiResponse<T = unknown> { success: boolean; message: string; data?: T | null; } export interface WorkflowSummary { name: string; description?: string | null; status: 'idle' | 'running' | 'paused' | 'completed'; created_at: string; updated_at: string; } export interface WorkflowItem { name: string; description?: string | null; config_yaml: string; status: 'idle' | 'running' | 'paused' | 'completed'; created_at: string; updated_at: string; }
3. 计算节点管理 API (Node Management)
处理 Worker 节点的注册登录与定期心跳保活。
3.1 注册计算节点 (POST /api/node/register)
- 处理函数:
register_node - 函数签名:
pub async fn register_node( State(state): State<AppState>, auth_node: Option<Extension<AuthenticatedNode>>, Json(req): Json<NodeRegisterRequest> ) -> impl IntoResponse - 鉴权: 否(免凭据申请,提交后进入
pending_approval待管理员审批) - 请求 Header:
Content-Type: application/json - 请求 Body:
{ "node_id": "node-worker-01", "max_slots": 8 } - 响应 Schema:
200 OK(新节点申请提交成功,待审批):{ "status": "pending_approval", "message": "节点注册申请已成功提交!请在管理 Dashboard 控制台上点击【同意接入】授权该节点", "node_token": null }200 OK(已授权节点带专属 token 刷新配置):{ "status": "approved", "message": "节点配置更新成功", "node_token": null }
- curl 示例:
curl -X POST http://localhost:8090/api/node/register \ -H "Content-Type: application/json" \ -d '{ "node_id": "node-worker-01", "max_slots": 8 }'
3.2 节点心跳保活 (POST /api/node/heartbeat)
- 处理函数:
heartbeat_node - 函数签名:
pub async fn heartbeat_node( State(state): State<AppState>, Json(req): Json<NodeHeartbeatRequest>, ) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 请求 Header:
Content-Type: application/json - 请求 Body:
{ "node_id": "node-worker-01", "active_slots": 2, "cpu_usage": 45.2, "memory_usage": 30.8 } - 响应 Schema:
200 OK(成功):{ "status": "ok" }200 OK(失败):{ "status": "error", "message": "节点未找到或心跳更新失败" }
- curl 示例:
curl -X POST http://localhost:8090/api/node/heartbeat \ -H "Content-Type: application/json" \ -H "Authorization: Bearer secret_token" \ -d '{ "node_id": "node-worker-01", "active_slots": 2, "cpu_usage": 45.2, "memory_usage": 30.8 }'
3.3 节点凭据与审批管理 API (Admin 角色)
1. 列出全部节点及凭据状态 (GET /api/admin/nodes)
- 权限: Admin 角色
- 响应 (
200 OK):{ "success": true, "message": "成功获取节点列表", "data": [ { "node_id": "node-worker-01", "status": "online", "token_status": "active", "token_issued_at": "2026-07-28T12:00:00Z" } ] }token_status取值:active(有效)/none(无凭据记录)。token 失效靠重发覆盖 hash 实现,不存在「已吊销」中间态。
2. 同意节点接入申请 (POST /api/admin/nodes/:node_id/approve)
- 权限: Admin 角色
- 说明: 批准处于待审批状态的 Node,并生成该节点的专属 Token。
3. 拒绝节点接入申请 (POST /api/admin/nodes/:node_id/reject)
- 权限: Admin 角色
- 说明: 拒绝处于待审批状态的 Node 接入。
4. 重新颁发节点专属 Token (POST /api/admin/nodes/:node_id/reissue)
- 权限: Admin 角色
- 说明: 作废旧 Token(hash 被覆盖,立即失效)并重新生成新 Token 文本返回。新明文同时暂存到服务端,节点可通过
/node/check_status自动拉取(限 1 天内有效),或由管理员手动同步到节点本地.node_token。
5. 停用节点 (POST /api/admin/nodes/:node_id/disable)
- 权限: Admin 角色
- 说明: 手动停用一个处于
online/offline状态的节点。停用后节点保持在线心跳(Dashboard 仍可见其存活),但claim不再向其分发任务,Worker 收到{"status":"disabled"}后会拉长轮询、空闲待命,可随时调用enable恢复。 - 状态语义: 节点
status切为disabled。与「重发 Token」(凭据失效、Worker 强制退出重注册)不同——停用仅是运维意图,不退出 Worker 进程、不动凭据。 - 响应:
200 OK:{"success": true, "message": "节点 '...' 已停用,不再分发任务"}409 Conflict: 节点当前状态不支持停用(仅online/offline可停用,pending_approval/disabled返回此码)。
- curl 示例:
curl -X POST http://localhost:8090/api/admin/nodes/node-worker-01/disable \ -H "Authorization: Bearer <ADMIN_TOKEN>"
6. 重新启用节点 (POST /api/admin/nodes/:node_id/enable)
- 权限: Admin 角色
- 说明: 重新启用被手动停用(
disabled)的节点。状态切为offline,靠节点下一次心跳自然翻为online后即恢复分发任务——既能自愈,又不会对真实离线的节点虚报在线。 - 响应:
200 OK:{"success": true, "message": "节点 '...' 已重新启用,将在下一次心跳后恢复分发任务"}409 Conflict: 节点当前状态不支持启用(仅disabled可启用)。
- curl 示例:
curl -X POST http://localhost:8090/api/admin/nodes/node-worker-01/enable \ -H "Authorization: Bearer <ADMIN_TOKEN>"
4. 任务调度与结果上报 API (Task Processing)
支持 Worker 节点抢占式领用任务与计算结果(含种子 .7 文件)上传。
4.1 领用计算任务 (POST /api/task/claim)
- 处理函数:
claim_task - 函数签名:
pub async fn claim_task(State(state): State<AppState>) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 请求 Body: 无
- 响应 Schema:
200 OK(有可计算任务):{ "status": "ok", "task": { "task_id": "550e8400-e29b-41d4-a716-446655440000", "point_name": "t35000_g5.5_he-1_c-2_n-2_o-2", "params": { "teff": 35000.0, "logg": 5.5, "loghe": -1.0, "logc": -2.0, "logn": -2.0, "logo": -2.0 }, "task_type": "cold_run", "seed_point_name": null, "timeout_sec": 7200 } }200 OK(当前队列为空):{ "status": "empty", "task": null }200 OK(节点被管理员手动停用,不再分发任务):{ "status": "disabled", "task": null }Worker 收到此响应后保持存活、拉长轮询(每 60s)空闲待命,不会退出进程(区别于 401/403 的 Token 失效语义)。管理员调用
/enable后下一次轮询即恢复领用。500 Internal Server Error:{ "status": "error", "message": "领用任务失败: <error_details>" }
- curl 示例:
curl -X POST http://localhost:8090/api/task/claim \ -H "Authorization: Bearer secret_token"
4.2 上报任务结果与种子文件 (POST /api/task/report)
- 处理函数:
report_task - 函数签名:
pub async fn report_task( State(state): State<AppState>, mut multipart: Multipart, ) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 请求格式:
multipart/form-data- Part
report: JSON 字符串 (映射为TaskReport) - Part
seed_file(可选): 二进制数据 (收敛网格点的.7大气结构种子文件)
- Part
- 响应 Schema:
200 OK(成功):{ "status": "ok", "message": "上报成功" }400 Bad Request(缺少report字段):{ "status": "error", "message": "请求中缺少 report 字段" }400 Bad Request(参数格式错误):{ "status": "error", "message": "无法解析 params 或 summary_json" }
- 说明: 当
converged == true且atmosphere_has_nan == false且包含seed_file时,服务端会将种子保存至seeds_dir/<point_name>/<point_name>.7并记入seeds表。若冷启动任务失败,服务端会自动唤醒GridScheduler触发针对该网格点的步进回退算法 (Seed-step Fallback)。 - curl 示例:
curl -X POST http://localhost:8090/api/task/report \ -H "Authorization: Bearer secret_token" \ -F 'report={ "task_id": "550e8400-e29b-41d4-a716-446655440000", "point_name": "t35000_g5.5_he-1_c-2_n-2_o-2", "node_id": "node-worker-01", "status": "completed", "converged": true, "max_relc": 0.00008, "atmosphere_has_nan": false, "elapsed_sec": 142.5, "error_message": null, "summary_json": "{\"name\":\"t35000_g5.5_he-1_c-2_n-2_o-2\",\"params\":{\"teff\":35000.0,\"logg\":5.5,\"loghe\":-1.0,\"logc\":-2.0,\"logn\":-2.0,\"logo\":-2.0},\"stages\":[],\"converged\":true,\"elapsed_sec\":142.5,\"atmosphere_has_nan\":false}" };type=application/json' \ -F 'seed_file=@/path/to/t35000_g5.5_he-1_c-2_n-2_o-2.7'
4.3 历史种子批量导入 (POST /api/admin/import_seed)
把旧版单机 run_grid.py 产物(conv.json + .7 大气文件)批量回灌进 DCTS。与 /task/report 的关键区别:跳过任务归属校验(历史数据无领用语义),直接幂等落库。供 tools/import_results 调用。
- 处理函数:
import_seed - 鉴权: Admin 角色(admin token 或登录 session)
- 请求格式:
multipart/form-data- Part
report: 旧版conv.json的原文 JSON(映射为ModelSummary,服务端解析出name/params/converged/final_max_relc) - Part
seed_file: 二进制数据(.7大气种子文件;收敛点必传)
- Part
- Query 参数:
workflow(可选,默认imported):目标工作流名,种子导入到该工作流的grid_points。 - 命名保真:
point_name取旧conv.json的name字段(源精度真名,如t20000_g5.0_...),逐字符落库(磁盘目录、grid_points.name、seeds.point_name),与旧版 Pythongen_input5.model_name完全一致。 - 幂等:
ON CONFLICT DO NOTHINGupsertgrid_points、conv.json与.7原子覆盖写,可重复运行。 - 响应 Schema:
200 OK:{ "status": "ok", "point_name": "t20000_g5.0_he-2_c-4_n-4_o-4", "converged": true, "max_relc": 0.000321 }400 Bad Request: 缺少report/conv.json解析失败 / 非法网格点名称401 Unauthorized: 缺少或无效 admin 凭据
- curl 示例:
curl -X POST "http://localhost:8090/api/admin/import_seed?workflow=sdB_cno" \ -H "Authorization: Bearer <admin_token>" \ -F 'report=@/path/to/old_results/t20000_g5.0_he-2_c-4_n-4_o-4/conv.json;type=application/json' \ -F 'seed_file=@/path/to/old_results/t20000_g5.0_he-2_c-4_n-4_o-4/t20000_g5.0_he-2_c-4_n-4_o-4.7' - 批量导入工具:
import_results自动扫描结果目录、校验无 NaN 且收敛、逐点调用本端点(同时把完整产物树迁移到data/result/):cargo run -p import_results -- --dir <旧 results 根目录> \ --config workflows/sdB_cno.yaml \ --server http://127.0.0.1:8090 --workflow sdB_cno --token <admin_token>
5. 种子文件管理 API (Seed Management)
提供在网格计算过程中相近网格点间传递与下载 TLUSTY fort.7 大气结构二进制种子文件的功能。
5.1 下载网格点种子文件 (GET /api/seed/:name)
- 处理函数:
download_seed - 函数签名:
pub async fn download_seed( State(state): State<AppState>, AxumPath(name): AxumPath<String>, ) -> Response - 鉴权: 是 (若配置 Token)
- 路径参数:
name: 网格点名称 (例如:t35000_g5.5_he-1_c-2_n-2_o-2)
- 安全检查: 防止路径穿越攻击,校验参数中不可包含
..、/或\。 - 响应 Header:
Content-Type: application/octet-streamContent-Disposition: attachment; filename="<name>.7"
- 状态码与响应体:
200 OK: 返回文件二进制流400 Bad Request:"非法的种子名称参数"404 Not Found:"请求的种子文件不存在"500 Internal Server Error:"无法打开种子文件"
- curl 示例:
curl -X GET http://localhost:8090/api/seed/t35000_g5.5_he-1_c-2_n-2_o-2 \ -H "Authorization: Bearer secret_token" \ --output t35000_g5.5_he-1_c-2_n-2_o-2.7
6. 静态资源与数据下载 API (Data Assets)
供 Worker 节点下载执行 TLUSTY / SYNSPEC 所需的原子数据文件和线表。
6.1 下载任意数据资源文件 (GET /api/data/file/*filename)
- 处理函数:
download_single_data_file - 函数签名:
pub async fn download_single_data_file( AxumPath(filename): AxumPath<String> ) -> axum::response::Response - 鉴权: 是 (若配置 Token)
- 路径参数:
filename: 文件相对名称 (例如:he2.dat)
- 响应 Header:
Content-Type: application/octet-streamContent-Disposition: attachment; filename="<filename>"
- 状态码与响应体:
200 OK: 返回数据文件二进制流400 Bad Request:"无效的数据文件名"404 Not Found:"资源数据文件不存在"500 Internal Server Error:"无法读取资源数据文件"
- curl 示例:
curl -X GET http://localhost:8090/api/data/file/he2.dat \ -H "Authorization: Bearer secret_token" \ --output he2.dat
6.2 下载主光谱线表文件 (GET /api/data/linelist)
- 处理函数:
download_linelist - 函数签名:
pub async fn download_linelist() -> axum::response::Response - 鉴权: 是 (若配置 Token)
- 响应: 默认定位并流式返回
assets/gfVIS99.dat文件。 - 状态码:
200 OK(或404 Not Found/500 Internal Server Error) - curl 示例:
curl -X GET http://localhost:8090/api/data/linelist \ -H "Authorization: Bearer secret_token" \ --output gfVIS99.dat
7. 系统状态监控 API (System Status)
实时监控分布式计算集群节点活跃度与计算槽位利用率。
7.1 获取集群整体状态 (GET /api/status)
- 处理函数:
get_status - 函数签名:
pub async fn get_status(State(state): State<AppState>) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 响应 Schema (
200 OK):{ "status": "online", "nodes_online": 2, "total_active_slots": 4, "total_max_slots": 16, "nodes": [ { "node_id": "node-worker-01", "max_slots": 8, "active_slots": 2, "status": "online", "cpu_usage": 45.2, "memory_usage": 30.8, "last_heartbeat": "2026-07-27T16:55:00.000Z" } ], "grid_stats": { "total": 512, "pending": 210, "running": 32, "converged": 260, "failed": 10 } }grid_stats为全部工作流的合计(跨工作流全局聚合)。多工作流并发运行时,此处展示所有 工作流 grid_points 的汇总进度;如需查看单个工作流的进度,可读取该工作流各自的 grid_points 统计 (Database::get_grid_summary_stats(Some(workflow_name)))。 - curl 示例:
curl -X GET http://localhost:8090/api/status \ -H "Authorization: Bearer secret_token"
7.2 轻量级健康检查端点 (GET /healthz)
- 鉴权: 否 (Public 免鉴权,专用于 Docker / K8s / Caddy 探针)
- 响应 (
200 OK):{ "status": "ok" } - curl 示例:
curl -i http://localhost:8090/healthz
8. 工作流管理 API (Workflow CRUD & Execution)
管理恒星大气网格计算工作流 YAML 配置的增删改查、启动与暂停控制。
8.1 获取工作流列表 (GET /api/workflows)
- 处理函数:
list_workflows - 函数签名:
pub async fn list_workflows(State(state): State<AppState>) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 说明: 返回工作流轻量级元数据列表(包含
name、description、status、created_at、updated_at)。如需获取具体工作流的 YAML 配置详情,请调用GET /api/workflows/:name。 - 响应 Schema (
200 OK):{ "success": true, "message": "成功获取工作流列表", "data": [ { "name": "sdB_cno", "description": "sdB CNO 6D Stellar Atmosphere Grid", "status": "idle", "created_at": "2026-07-27T08:00:00Z", "updated_at": "2026-07-27T08:00:00Z" } ] } - curl 示例:
curl -X GET http://localhost:8090/api/workflows \ -H "Authorization: Bearer secret_token"
8.2 创建或保存工作流 (POST /api/workflows / PUT /api/workflows/:name)
- 处理函数:
save_workflow - 函数签名:
pub async fn save_workflow( State(state): State<AppState>, Json(req): Json<CreateWorkflowRequest>, ) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 请求 Body:
{ "name": "sdB_cno_custom", "description": "自定义 6维 网格计算工作流", "config_yaml": "grid:\n teff: [35000, 36000]\n logg: [5.5, 6.0]\n loghe: [-1.0]\n logc: [-2.0]\n logn: [-2.0]\n logo: [-2.0]\nchain:\n - label: LTE_START\n lte: T\n niter: 30\n" } - 响应 Schema:
200 OK(成功保存):{ "success": true, "message": "工作流 'sdB_cno_custom' 保存成功", "data": null }400 Bad Request(YAML 格式不合法):{ "success": false, "message": "无效的 YAML 配置: invalid syntax at line 2...", "data": null }
- curl 示例:
curl -X POST http://localhost:8090/api/workflows \ -H "Content-Type: application/json" \ -H "Authorization: Bearer secret_token" \ -d '{ "name": "sdB_cno_custom", "description": "自定义 6维 网格计算工作流", "config_yaml": "grid:\n teff: [35000, 36000]\n logg: [5.5, 6.0]\n loghe: [-1.0]\n logc: [-2.0]\n logn: [-2.0]\n logo: [-2.0]\nchain:\n - label: LTE_START\n lte: T\n niter: 30\n" }'
8.3 获取特定工作流详情 (GET /api/workflows/:name)
- 处理函数:
get_workflow - 函数签名:
pub async fn get_workflow( State(state): State<AppState>, AxumPath(name): AxumPath<String>, ) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 路径参数:
name(工作流唯一名称,如sdB_cno) - 响应 Schema:
200 OK(成功):{ "success": true, "message": "成功获取工作流详情", "data": { "id": 1, "name": "sdB_cno", "description": "sdB CNO 6D Stellar Atmosphere Grid", "config_yaml": "...", "status": "idle", "created_at": "2026-07-27T08:00:00Z", "updated_at": "2026-07-27T08:00:00Z" } }404 Not Found(不存在):{ "success": false, "message": "工作流 'unknown_wf' 未找到", "data": null }
- curl 示例:
curl -X GET http://localhost:8090/api/workflows/sdB_cno \ -H "Authorization: Bearer secret_token"
8.4 删除工作流 (DELETE /api/workflows/:name)
- 处理函数:
delete_workflow - 函数签名:
pub async fn delete_workflow( State(state): State<AppState>, AxumPath(name): AxumPath<String>, ) -> impl IntoResponse - 鉴权: 是 (若配置 Token)
- 响应 Schema (
200 OK):{ "success": true, "message": "工作流 'sdB_cno_custom' 已删除", "data": null } - curl 示例:
curl -X DELETE http://localhost:8090/api/workflows/sdB_cno_custom \ -H "Authorization: Bearer secret_token"
8.5 启动工作流 (POST /api/workflows/:name/start)
- 处理函数:
start_workflow - 函数签名:
pub async fn start_workflow( State(state): State<AppState>, AxumPath(name): AxumPath<String>, ) -> impl IntoResponse - 说明: 校验工作流,解析 YAML 中定义的所有 6 维网格坐标点,通过
GridScheduler::initialize_grid展开网格点并计算保序难度 Wave,写入 SQLite 任务队列并开启节点调度。 - 响应 Schema:
200 OK(成功启动):{ "success": true, "message": "工作流 'sdB_cno' 已成功启动并安排计算任务", "data": null }400 Bad Request(重复启动):{ "success": false, "message": "工作流 'sdB_cno' 已处于运行状态,无需重复启动", "data": null }404 Not Found:{ "success": false, "message": "工作流 'sdB_cno' 未找到", "data": null }
- curl 示例:
curl -X POST http://localhost:8090/api/workflows/sdB_cno/start \ -H "Authorization: Bearer secret_token"
8.6 暂停工作流 (POST /api/workflows/:name/stop)
- 处理函数:
stop_workflow - 函数签名:
pub async fn stop_workflow( State(state): State<AppState>, AxumPath(name): AxumPath<String>, ) -> impl IntoResponse - 响应 Schema (
200 OK):{ "success": true, "message": "工作流 'sdB_cno' 已暂停", "data": null } - curl 示例:
curl -X POST http://localhost:8090/api/workflows/sdB_cno/stop \ -H "Authorization: Bearer secret_token"
8.7 工作流执行统计 (GET /api/workflows/:name/stats)
- 处理函数:
get_workflow_stats(crates/server/src/api/workflow.rs) - 权限: Admin(
/workflows/*前缀统一映射) - 用途: 详情页"任务控制条 + 执行概览"数据源。在
get_grid_summary_stats(Some(wf))之上追加难度波次分布与近似 ETA。pending与queued分开计数(与/api/status的全局口径不同,后者由前端合并展示)。 - 路径参数:
:name— 工作流名(白名单[A-Za-z0-9._-]{1,64});未知工作流 →404。 - 响应 Schema (
200 OK):{ "success": true, "message": "成功获取工作流统计", "data": { "name": "sdB_cno", "status": "running", "total": 432, "pending": 120, "queued": 40, "running": 8, "converged": 261, "failed": 3, "cold_run_converged": 220, "seed_step_converged": 41, "imported_converged": 0, "waves": [ { "wave": 0, "total": 108, "converged": 108, "failed": 0 } ], "avg_point_sec": 740.5, "eta_sec": 9620.0 } }avg_point_sec=AVG(COALESCE(tasks.elapsed_sec, created_at→completed_at 时间戳差))—— 优先用 Worker 回报的精确墙钟(不含排队等待),历史无elapsed_sec的行回退时间戳差近似;eta_sec=avg_point_sec × (total - converged - failed) ÷ 在线节点总槽位(并发感知; 无在线节点按串行兜底);无历史数据时二者为null。
8.8 工作流网格点列表 (GET /api/workflows/:name/points)
-
处理函数:
get_workflow_points(crates/server/src/api/workflow.rs) -
权限: Admin
-
用途: 详情页"网格点明细"表与"收敛性分析"热力图、概览"最近动态"流的数据源。 每行附带最近一次尝试信息(
tasks关联子查询取最新行,从未派发过的点last_*为 null)。 -
查询参数(全部可选,枚举值白名单校验,非法 →
400):参数 取值 默认 statuspending/queued/running/converged/failed不过滤 methodcold_run/seed_step/imported不过滤 wave整数波次 不过滤 q点名子串(LIKE 通配符已转义) 不过滤 sortwave/teff/max_relc/attempts/last_completed_atwaveorderasc/descasclimit1–500(超出钳位) 100 offset≥0 0 -
响应 Schema (
200 OK):{ "success": true, "message": "成功获取工作流网格点列表", "data": { "total": 432, "points": [ { "name": "t60000_g5.0_he-2_c-4_n-4_o-4", "teff": 60000.0, "logg": 5.0, "loghe": -2.0, "logc": -4.0, "logn": -4.0, "logo": -4.0, "cno_sum": -12.0, "wave": 0, "status": "converged", "success_method": "seed_step", "attempt_count": 2, "last_max_relc": 0.00043, "last_task_type": "seed_step", "seed_point_name": "t60000_g5.0_he2_c-4_n-4_o-4", "node_id": "node-a1b2", "last_completed_at": "2026-07-30 11:12:00", "last_error": null } ] } }
8.9 网格点详情 (GET /api/workflows/:name/points/:point)
- 处理函数:
get_workflow_point_detail(crates/server/src/api/workflow.rs) - 权限: Admin
- 用途: 点详情滑入面板——尝试历史(还原"冷启动失败 → 种子步进救回"剧情)
conv.json逐阶段诊断。
- 路径参数:
:point— 点名白名单[A-Za-z0-9._-+@](非空、拒前导.、≤128, 即路径穿越前置闸门);conv.json 读盘前再经 canonicalize 归属校验。非法点名 →400; 点不存在 →404。 - 响应 Schema (
200 OK):{ "success": true, "message": "成功获取网格点详情", "data": { "point": { "...": "同 8.8 单行" }, "attempts": [ { "task_id": "uuid", "task_type": "cold_run", "seed_point_name": null, "status": "failed", "max_relc": 954000.0, "atmosphere_has_nan": false, "node_id": "node-a1b2", "error_message": "nl stage diverged", "created_at": "2026-07-30 09:00:00", "completed_at": "2026-07-30 09:30:00" }, { "task_type": "seed_step", "status": "completed", "...": "第二次尝试(救回)" } ], "conv": { "converged": true, "final_max_relc": 0.00043, "final_chmax": 0.001, "seed": "data/seeds/<seed_name>/<seed_name>.7", "atmosphere_has_nan": false, "synspec_rc": 0, "synspec_sec": 3.1, "elapsed_sec": 126.4, "stages": [ { "label": "seed_nc", "chmax": 0.001, "lte": false, "converged": false, "best_max_relc": 0.02, "elapsed_sec": 62.4, "note": null }, { "label": "nl", "chmax": 0.001, "lte": false, "converged": true, "best_max_relc": 0.00043, "elapsed_sec": 64.0, "note": null } ] } } }conv来自seeds_dir/<point>/conv.json(ModelSummary结构,见crates/common/src/models.rs)。文件缺失/解析失败时为null(仍返回 200, 前端降级显示"诊断文件不可用")。
8.10 工作流进度时间序列 (GET /api/workflows/:name/progress)
- 处理函数:
get_workflow_progress(crates/server/src/api/workflow.rs) - 权限: Admin
- 用途: 详情页概览的进度曲线(sparkline)、经验速率 ETA 与停滞预警数据源。
快照由服务端后台循环(~30s)对每个运行中工作流写入
workflow_progress_snapshots表,计数无变化不落库(去重防膨胀),保留期 7 天自动清理。 - 查询参数:
hours(时间窗口,默认 24,钳位 1–168)。 - 响应 Schema (
200 OK):{ "success": true, "message": "成功获取进度时间序列", "data": { "hours": 24, "series": [ { "ts": "2026-07-31 08:00:00", "total": 432, "pending": 120, "queued": 40, "running": 8, "converged": 261, "failed": 3 } ], "rate_per_hour": 12.5, "stalled_minutes": 0.0 } }series超 300 条自动降采样(首末点保留);rate_per_hour= 窗口首末 converged 增量 ÷ 时长(快照不足 2 条为 null);stalled_minutes= 终态数 (converged+failed)最后一次增长至窗口末端的分钟数(前端 >10 分钟触发停滞预警)。
8.11 工作流列表内联统计(GET /api/workflows 响应增强)
WorkflowSummary 新增 stats 字段(单条 GROUP BY 聚合回填,无 N+1):
工作流尚无网格点(未启动)时为 null,前端据此不渲染卡片进度条。
{
"success": true,
"message": "成功获取工作流列表",
"data": [
{
"name": "sdB_cno",
"description": "sdB 6维恒星大气模型计算网格",
"status": "running",
"created_at": "2026-07-30 10:00:00",
"updated_at": "2026-07-30 11:00:00",
"stats": {
"total": 432,
"converged": 261,
"failed": 3,
"running": 8,
"cold_run_converged": 220,
"seed_step_converged": 41
}
}
]
}
9. 错误处理与状态码汇总
| HTTP 状态码 | 触发场景说明 | 响应格式 | 核心原因与解决建议 |
|---|---|---|---|
200 OK |
请求正常处理 | JSON / Binary Stream | 操作成功执行。 |
400 Bad Request |
参数校验失败、缺失关键字段或 YAML 格式错误 | application/json / Plain Text |
检查请求 JSON 结构,验证 YAML 配置语法是否正确。 |
401 Unauthorized |
鉴权失败或缺失 Authorization Header | application/json |
确认环境变量配置,并在 Request Header 中包含正确的 Token。 |
404 Not Found |
资源、种子文件或工作流不存在 | application/json |
校验请求 URL 中的资源文件名或工作流 name 是否拼写无误。 |
429 Too Many Requests |
API 请求超出速率限制(限流生效) | application/json |
降低请求频率或配置漏桶/令牌桶容量参数。 |
500 Internal Server Error |
服务端数据库错误、I/O 打开失败或队列异常 | application/json |
检查服务端日志以进一步厘清 SQLite 锁冲突、磁盘空间或资源路径问题。 |
文档生成于 2026-07-27 | DCTS Server 0.1.0