- server/db: 拆 4929 行 db.rs 单体为 db/ 目录,migrations.rs 引入 PRAGMA user_version
版本化迁移运行器(M1~M13)
- 任务引擎 Phase 6/7b/7c 改名收敛:EngineStageConfig→PhaseConfig、StagePolicy→ResumePolicy、
Converged→Completed、删除 task_type 列、success_method 拆 tlusty_/synspec_ 双列、
新增 tlusty_status/synspec_status 半失败阶段守卫
- 科学正确性加固:conv_check 任意行 NaN/Inf/溢出判无效(0 行容忍)、新增 spec_is_valid
校验 SYNSPEC 脏谱、itek_history 逐次迭代全量保真、fmt_abn powf 溢出饱和
- 用户配置真正接通:tlusty_chain/tlusty_input 由死字段经 调度器→TaskSpec→executor→runner
透传生效;config 加载期 validate + deny_unknown_fields + 解析失败记 warn
- 调度修复:H1 活锁(pending_strategies 跳过已失败策略)、种子查找错误不再静默降级冷启动
- dashboard: 阶段配置面板 tlusty_stage/synspec_stage、"已完成"标签、迭代诊断展示
- docs: 新增 database_refactor_design.md,同步 database/api/PIPELINE/workflow_detail
48 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)应用了滑动窗口(sliding window)请求限流器(rate_limit.rs)。超出速率上限时返回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 seed_point_name: Option<String>, // 步进种子点名称(如适用) pub timeout_sec: u64, pub workflow_name: Option<String>, // 多工作流分区键 pub wave: i32, // 难度波次 pub tlusty_config: PhaseConfig, // TLUSTY 阶段配置(enabled/policy/strategies) pub synspec_config: PhaseConfig, // SYNSPEC 阶段配置 pub synspec_params: Option<serde_json::Value>, // SYNSPEC 数值参数(波长范围等) pub atmosphere_ref: Option<String>, // 显式大气来源点(仅 SYNSPEC-only 场景) }PhaseConfig(阶段独立配置,见task_engine_decoupling_design.md §3;Phase 7b 由EngineStageConfig改名):pub struct PhaseConfig { pub enabled: bool, pub policy: ResumePolicy, // skip_converged | force_recompute | skip_failed pub strategies: Vec<String>, // 策略链:["cold_run","seed_step"],失败回退弹首项 }Phase 6 起 无
task_type字段——执行链由tlusty_config.strategies[0]派生。 旧 payload 若仍携带task_type键会被 serde 忽略(未知字段)。 -
TypeScript 类型声明(Phase 6 起无
task_type):export interface PhaseConfig { enabled: boolean; policy: string; strategies: string[]; } export interface TaskSpec { task_id: string; point_name: string; params: GridPointParams; seed_point_name?: string | null; timeout_sec: number; workflow_name?: string | null; wave: number; tlusty_config: PhaseConfig; synspec_config: PhaseConfig; synspec_params?: Record<string, unknown> | null; atmosphere_ref?: string | null; }
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 result_valid: bool, // 7b 改名(原 converged):本次结果是否可用(大气收敛/管线成功双义) pub max_relc: Option<f64>, pub atmosphere_has_nan: bool, pub elapsed_sec: f64, pub error_message: Option<String>, pub summary_json: String, pub failed_stage: Option<String>, // "tlusty" | "synspec":失败阶段归因,决定弹哪条策略链 } #[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; result_valid: boolean; // 7b 改名(原 converged) max_relc?: number | null; atmosphere_has_nan: boolean; elapsed_sec: number; error_message?: string | null; summary_json: string; failed_stage?: 'tlusty' | 'synspec' | null; }
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' | 'pending_approval' | 'disabled' | 'rejected'; 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' | 'initializing' | 'running' | 'paused' | 'completed'; created_at: string; updated_at: string; stats?: WorkflowListStats | null; // 内联网格点聚合计数(未启动为 null,见 §8.11) } export interface WorkflowListStats { total: number; completed: number; // 7c:由 converged 改名(状态值 'completed') failed: number; running: number; cold_run_converged: number; seed_step_converged: number; synspec_converged: number; } 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, "registration_secret": "<一次性凭据>" }registration_secret:节点注册时下发的一次性凭据,节点须在后续/api/node/check_status时回传,才能取走待发 token(防止仅知 node_id 的攻击者抢先冒领)。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>, Extension(auth_node): Extension<AuthenticatedNode>, Json(req): Json<NodeHeartbeatRequest>, ) -> impl IntoResponse - 鉴权: 是(Node 角色必填;
req.node_id必须与鉴权身份一致,否则 403) - 请求 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", "admin_max_slots": 4 }admin_max_slots:管理员强制的并发槽位配额(null= 无限制,沿用物理max_slots)。 节点据此动态调整本地effective_max_slots(见dynamic_cpu_slots_design.md)。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", "admin_max_slots": 4 } ] }token_status取值:active(有效)/none(无凭据记录)。token 失效靠重发覆盖 hash 实现,不存在「已吊销」中间态。admin_max_slots:管理员强制并发槽位配额(null= 无限制),Dashboard 据此渲染节点配额状态。
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>"
7. 调整节点并发配额 (POST /api/admin/nodes/:node_id/quota)
- 权限: Admin 角色
- 说明: 动态调整节点并发槽位配额上限(不重启 Worker 进程,经下一次心跳响应下发生效)。传
null解除限制,恢复物理max_slots。详见dynamic_cpu_slots_design.md。 - 请求 Body:
{ "admin_max_slots": 4 } - 响应:
200 OK:{"success": true, "message": "节点配额已更新"}
- curl 示例:
curl -X POST http://localhost:8090/api/admin/nodes/node-worker-01/quota \ -H "Authorization: Bearer <ADMIN_TOKEN>" -H "Content-Type: application/json" \ -d '{"admin_max_slots": 4}'
4. 任务调度与结果上报 API (Task Processing)
支持 Worker 节点抢占式领用任务与计算结果(含种子 .7 文件)上传。
4.1 领用计算任务 (POST /api/task/claim)
- 处理函数:
claim_task - 函数签名:
pub async fn claim_task( State(state): State<AppState>, Extension(auth_node): Extension<AuthenticatedNode>, ) -> impl IntoResponse - 鉴权: 是(Node 角色必填;服务端据注入身份校验节点是否被停用)
- 请求 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 }, "seed_point_name": null, "timeout_sec": 7200, "tlusty_config": { "enabled": true, "policy": "skip_converged", "strategies": ["cold_run", "seed_step"] }, "synspec_config": { "enabled": true, "policy": "skip_converged", "strategies": ["standard"] } } }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>, Extension(auth_node): Extension<AuthenticatedNode>, mut multipart: Multipart, ) -> impl IntoResponse - 鉴权: 是(Node 角色必填;服务端据注入身份做任务归属校验
verify_task_claim,防跨节点伪造) - 请求格式:
multipart/form-data- Part
report: JSON 字符串 (映射为TaskReport) - Part
seed_file(可选): 二进制数据 (收敛网格点的.7大气结构种子文件)
- Part
- 响应 Schema:
200 OK(成功 / 幂等重放):{ "status": "ok", "message": "上报成功" }幂等重放:已结算任务(终态守卫已吸收)的重复/迟到上报仍返回 200,并补写种子,不重复结算。
400 Bad Request(缺少report字段):{ "status": "error", "message": "请求中缺少 report 字段" }400 Bad Request(参数格式错误):{ "status": "error", "message": "无法解析 params 或 summary_json" }409 Conflict(任务归属校验失败): 任务被其它节点重新领用、或队列中已无该 task_id(已被结算清理)。此时返回冲突,不写库。
- 说明:
- 当
converged == true且atmosphere_has_nan == false且包含seed_file时,服务端会将种子原子写入seeds_dir/<point_name>/<point_name>.7并记入seeds表(源精度point_name)。 - 失败上报(未收敛 / 失败 / 超时)且
state_changed == true时,服务端才触发策略链回退(trigger_strategy_fallback,按failed_stage弹 TLUSTY 或 SYNSPEC 策略链);被终态守卫吸收的重复失败(state_changed=false)不再触发,杜绝重复派发涡旋(2026-08-02 修复)。
- 当
- 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
tlusty_success_method(可选): 文本cold_run/seed_step。由tools/import_results依据旧conv.json的 stages 是否含seed_nc判定后设置,写入导入点大气归因tlusty_success_method(缺省按 seed_step 统计)
- 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, "queued": 60, "running": 32, "completed": 260, "failed": 10, "cold_run_converged": 200, "seed_step_converged": 60, "synspec_converged": 245 } }grid_stats为全部工作流的合计(跨工作流全局聚合)。多工作流并发运行时,此处展示所有 工作流 grid_points 的汇总进度;queued与pending分开计数(多工作流分区新语义),cold_run_converged/seed_step_converged为收敛手段归因;如需查看单个工作流的进度, 可读取该工作流各自的 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": { "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 - 说明: 以原子 CAS(
transition_workflow_to_initializing)抢占启动权,随后异步 spawn 网格初始化:解析 YAML 的 6 维网格点、按 policy 决定点状态(跳过收敛/打回失败)、展开并计算保序难度 Wave、把 pending 点推入 MQ 开启节点调度。接口先返回 200,后台推进为running。 - 响应 Schema:
200 OK(成功启动,后台初始化中):{ "success": true, "message": "工作流 'sdB_cno' 已进入后台异步建立与挂载流程", "data": null }400 Bad Request(重复启动):{ "success": false, "message": "工作流 'sdB_cno' 已处于运行状态,无需重复启动", "data": null }409 Conflict(并发启动,初始化抢占失败):{ "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, "completed": 261, "failed": 3, "cold_run_converged": 220, "seed_step_converged": 41, "waves": [ { "wave": 0, "total": 108, "completed": 108, "failed": 0 } ], "avg_point_sec": 740.5, "eta_sec": 9620.0 } }收敛手段归因仅
cold_run_converged/seed_step_converged两字段;无独立imported_converged——历史导入点统一按 seed_step 途径计入(大气归因tlusty_success_method语义见 §8.8)。avg_point_sec=AVG(COALESCE(tasks.elapsed_sec, created_at→completed_at 时间戳差))—— 优先用 Worker 回报的精确墙钟(不含排队等待),历史无elapsed_sec的行回退时间戳差近似;eta_sec=avg_point_sec × (total - completed - failed) ÷ 在线节点总槽位(并发感知; 无在线节点按串行兜底);无历史数据时二者为null。
8.8 工作流网格点列表 (GET /api/workflows/:name/points)
-
处理函数:
get_workflow_points(crates/server/src/api/workflow.rs) -
权限: Admin
-
用途: 详情页"网格点明细"表与"收敛性分析"(Parallel Sets 平行集合图)、概览"最近动态"流的数据源。 每行附带最近一次尝试信息(
tasks关联子查询取最新行,从未派发过的点last_*为 null)。 -
查询参数(全部可选,枚举值白名单校验,非法 →
400):参数 取值 默认 statuspending/queued/running/completed/failed(旧值converged仍作兼容别名接受,服务端归一化为completed)不过滤 methodcold_run/seed_step(映射tlusty_success_method)synspec_only(光谱专用点:tlusty_success_method IS NULL AND synspec_success_method IS NOT NULL)。白名单不含imported,传入即400不过滤 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": "completed", "tlusty_success_method": "seed_step", "synspec_success_method": "standard", "attempt_count": 2, "last_max_relc": 0.00043, "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", "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", "elapsed_sec": 1800.0, "failed_stage": "tlusty", "summary_json": "{... ModelSummary ...}" }, { "seed_point_name": "t60000_...", "status": "completed", "failed_stage": null, "...": "第二次尝试(救回)" } ], "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, "completed": 261, "failed": 3 } ], "rate_per_hour": 12.5, "now": "2026-07-31T09:00:00Z", "done_rate_per_hour": 11.0, "rate_span_hours": 23.5, "stalled_minutes": 0.0 } }series超 300 条自动降采样(首末点保留);rate_per_hour= 窗口首末 completed 增量 ÷ 时长(快照不足 2 条为 null);now= 服务端当前 UTC 时刻(前端锚定曲线右缘);done_rate_per_hour= 终态完成速率(用于 ETA);rate_span_hours= 速率统计实际时间跨度;stalled_minutes= 终态数(completed+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,
"completed": 261,
"failed": 3,
"running": 8,
"cold_run_converged": 220,
"seed_step_converged": 41,
"synspec_converged": 248
}
}
]
}
9. 错误处理与状态码汇总
| HTTP 状态码 | 触发场景说明 | 响应格式 | 核心原因与解决建议 |
|---|---|---|---|
200 OK |
请求正常处理 | JSON / Binary Stream | 操作成功执行。 |
400 Bad Request |
参数校验失败、缺失关键字段、YAML 格式错误或枚举白名单拒绝 | application/json / Plain Text |
检查请求 JSON 结构,验证 YAML 配置语法,确认 method/sort/order 等枚举在合法取值内。 |
401 Unauthorized |
鉴权失败或缺失 Authorization Header | application/json |
确认环境变量配置,并在 Request Header 中包含正确的 Token。 |
403 Forbidden |
身份校验失败(心跳 node_id 与鉴权身份不匹配、跨点上报拒绝、角色权限不足) |
application/json |
确认节点 token 与上报身份一致,检查 RBAC 角色。 |
404 Not Found |
资源、种子文件或工作流不存在 | application/json |
校验请求 URL 中的资源文件名或工作流 name 是否拼写无误。 |
409 Conflict |
状态冲突:节点停用/启用状态不支持、start 初始化抢占失败、report 任务归属校验失败 | application/json |
检查目标状态是否合法,或稍后重试。 |
429 Too Many Requests |
API 请求超出速率限制(滑动窗口限流生效) | application/json |
降低请求频率;限流为滑动窗口实现(rate_limit.rs),非漏桶。 |
500 Internal Server Error |
服务端数据库错误、I/O 打开失败或队列异常 | application/json |
检查服务端日志以进一步厘清 SQLite 锁冲突、磁盘空间或资源路径问题。 |
文档生成于 2026-07-27,2026-08-04 更新以对齐阶段独立配置 / 多工作流隔离 / 节点配额 / 失败阶段归因。