Files
DCTS/dashboard/index.html
T
fmq d16b3d3cdc feat(all): 数据库模块化拆分与版本化迁移、任务引擎命名体系收敛、物理输出校验加固与用户配置接通
- 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
2026-08-06 20:51:21 +08:00

325 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DCTS — 分布式恒星大气网格计算控制台</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- 字重精简:Inter 去掉未使用的 300Space Grotesk 去掉未使用的 500(仅 600/700 用于标题),
减少 2 个字体文件下载。display=swap 避免 FOIT。 -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Space+Grotesk:wght@600;700&display=swap" rel="stylesheet">
<!-- 主题初始化移为外链(public/theme-init.js):使 CSP 可移除 script-src 'unsafe-inline'。
同步脚本在首帧前执行,保持防 FOUC 语义。 -->
<script src="/theme-init.js"></script>
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<a href="#view-root" class="skip-link">跳到主内容</a>
<div id="app">
<!-- ===== 登录遮罩(鉴权) ===== -->
<div id="login-overlay" class="login-overlay" role="dialog" aria-modal="true" aria-labelledby="login-title" aria-describedby="login-subtitle">
<div class="login-card card">
<div class="login-header">
<h2 id="login-title">DCTS 控制台登录</h2>
<p id="login-subtitle">请输入管理员密码以访问管理控制台</p>
</div>
<form id="form-login" class="stack-col stack-col-md">
<div class="form-group">
<label for="input-admin-token">管理员密码</label>
<div class="input-with-toggle">
<input type="password" id="input-admin-token" class="form-input" placeholder="输入管理员密码..." required autocomplete="current-password" />
<button type="button" class="btn-toggle-password" aria-label="显示密码" title="显示/隐藏密码">
<svg class="icon-eye-open" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="icon-eye-closed hidden" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
</button>
</div>
</div>
<div id="login-error-msg" class="login-error-msg alert-error hidden"></div>
<button type="submit" id="btn-submit-login" class="btn btn-primary">登录</button>
</form>
</div>
</div>
<!-- 顶部导航栏 (Header) -->
<header class="header">
<div class="logo-container">
<div class="logo-icon">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<!-- 恒星大气星核 (TLUSTY Stellar Core) -->
<path d="M12 3c0 4.5-4.5 9-9 9 4.5 0 9 4.5 9 9 0-4.5 4.5-9 9-9-4.5 0-9-4.5-9-9z" fill="currentColor" fill-opacity="0.35" stroke-width="1.5" />
<!-- 分布式计算集群网格轨道与算力节点 (Distributed Computing Grid Nodes) -->
<circle cx="12" cy="12" r="9.5" stroke-width="1.4" stroke-dasharray="4 2.5" />
<circle cx="12" cy="2.5" r="1.5" fill="currentColor" />
<circle cx="21.5" cy="12" r="1.5" fill="currentColor" />
<circle cx="12" cy="21.5" r="1.5" fill="currentColor" />
<circle cx="2.5" cy="12" r="1.5" fill="currentColor" />
</svg>
</div>
<div>
<h1 class="logo-title">DCTS 集群控制台</h1>
<p class="logo-subtitle">Distributed Computing TLUSTY / SYNSPEC Grid System</p>
</div>
</div>
<div class="header-status">
<div class="status-indicator online" id="status-indicator-box" role="status" aria-live="polite">
<span class="status-dot"></span>
<span id="server-status-text">服务端正常运行</span>
</div>
<button id="btn-create-wf" class="btn btn-primary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
创建工作流
</button>
<button id="refresh-btn" class="btn btn-secondary">
<svg class="icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M23 4v6h-6M1 20v-6h6"/>
<path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/>
</svg>
刷新
</button>
<button id="btn-theme-toggle" class="btn btn-secondary" title="切换主题模式" aria-label="切换主题模式">
<svg class="icon-theme-sun hidden" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
<svg class="icon-theme-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
<button id="btn-logout" class="btn btn-secondary hidden" title="登出并清除 Token">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
<polyline points="16 17 21 12 16 7"></polyline>
<line x1="21" y1="12" x2="9" y2="12"></line>
</svg>
登出
</button>
</div>
</header>
<!-- 主内容区(视图由 src/router.js 挂载到 #view-root:首页 / 工作流详情页) -->
<main class="main-container">
<div id="view-root"></div>
</main>
<!-- 创建 / 编辑工作流 模态弹窗 -->
<div id="modal-create-wf" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal-card">
<div class="modal-header">
<h3 id="modal-title">注册新的计算工作流</h3>
<button id="close-create-wf" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<form id="form-create-wf" class="stack-col stack-col-md">
<div class="form-group">
<label for="input-wf-name">工作流名称 (Identifier) <span class="req-mark" aria-hidden="true">*</span></label>
<input type="text" id="input-wf-name" class="form-input" placeholder="例如: sdB_cno" required
pattern="[A-Za-z0-9._\-]+" maxlength="64" autocomplete="off"
aria-describedby="wf-name-hint" />
<small id="wf-name-hint" class="field-hint">仅支持字母、数字、点、下划线与连字符,最长 64 字符</small>
<small id="wf-name-error" class="field-error hidden" role="alert"></small>
</div>
<div class="form-group">
<label for="input-wf-desc">描述信息</label>
<input type="text" id="input-wf-desc" class="form-input" placeholder="例如: sdB 6维恒星大气模型计算网格" />
</div>
<div class="form-group">
<div class="row-wrap" style="justify-content: space-between; align-items: center;">
<label for="input-wf-yaml">YAML 配置内容 (GridConfig) <span class="req-mark" aria-hidden="true">*</span></label>
<button type="button" id="btn-load-preset-yaml" class="btn btn-secondary btn-sm">填入默认示例</button>
</div>
<textarea id="input-wf-yaml" class="form-textarea" required placeholder="请粘贴工作流 YAML 参数网格定义..." aria-describedby="wf-yaml-hint"></textarea>
<small id="wf-yaml-hint" class="field-hint">定义 grid 参数网格与 chain 计算链,保存前服务端会校验 YAML 结构</small>
</div>
<div class="modal-footer">
<button type="button" id="cancel-create-wf" class="btn btn-secondary">取消</button>
<button type="submit" class="btn btn-primary">提交保存</button>
</div>
</form>
</div>
</div>
<!-- 工作流 YAML 配置编辑器(查看/编辑双模式,逻辑见 src/components/yamlEditor.js -->
<div id="modal-view-wf" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="view-wf-title">
<div class="modal-card modal-wide yaml-editor">
<div class="modal-header">
<div class="yaml-editor-head">
<h3 id="view-wf-title">工作流配置</h3>
<span id="yaml-editor-status"></span>
<span id="yaml-editor-dirty" class="dirty-dot hidden" title="有未保存的修改" aria-label="有未保存的修改"></span>
</div>
<button id="close-view-wf" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<!-- 查看模式:语法高亮 + 行号 -->
<div id="yaml-view-pane" class="yaml-view-pane">
<div class="yaml-scroll"><pre id="view-wf-yaml" class="yaml-highlight"></pre></div>
</div>
<!-- 编辑模式:等宽编辑区 + 校验错误/状态重置提示 -->
<div id="yaml-edit-pane" class="yaml-edit-pane hidden">
<div id="yaml-edit-error" class="yaml-edit-error hidden" role="alert"></div>
<div id="yaml-reset-warn" class="yaml-reset-warn hidden">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<span>保存后工作流状态将重置为「闲置」,需重新启动才能使新配置生效</span>
</div>
<textarea id="yaml-edit-area" class="yaml-edit-area" spellcheck="false" aria-label="YAML 配置编辑区"></textarea>
</div>
<div class="modal-footer yaml-editor-footer">
<span id="yaml-editor-meta" class="yaml-editor-meta tabular-num"></span>
<span id="yaml-editor-lock" class="yaml-editor-lock hidden"></span>
<button type="button" id="btn-export-yaml" class="btn btn-secondary btn-sm" title="下载导出当前 YAML 配置文件">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
导出配置
</button>
<button type="button" id="btn-edit-yaml" class="btn btn-primary btn-sm">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
编辑
</button>
<button type="button" id="btn-cancel-edit-yaml" class="btn btn-secondary btn-sm hidden">取消</button>
<button type="button" id="btn-save-yaml" class="btn btn-primary btn-sm hidden">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
保存配置
</button>
<button type="button" id="btn-close-view-wf" class="btn btn-secondary btn-sm">关闭</button>
</div>
</div>
</div>
<!-- 重发 Token 结果展示模态弹窗 -->
<div id="modal-reissue-token" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="reissue-modal-title">
<div class="modal-card">
<div class="modal-header">
<h3 id="reissue-modal-title">节点专属 Token 已重新颁发</h3>
<button id="reissue-close-btn" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div class="modal-body">
<p class="text-hint-mb">
旧 Token 已立即失效。请复制下方新 Token 并同步到该节点的本地 <code>.node_token</code> 文件后重启节点:
</p>
<pre id="reissue-token-content" class="code-pre-wrap"></pre>
</div>
<div class="modal-footer">
<button type="button" id="btn-copy-token" class="btn btn-primary">复制 Token</button>
<button type="button" id="btn-close-reissue-modal" class="btn btn-secondary">关闭</button>
</div>
</div>
</div>
<!-- 节点详情与管理模态弹窗(三点菜单入口:配额调整 / 启停 / 重发 Token -->
<div id="modal-node-manage" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="node-manage-title">
<div class="modal-card">
<div class="modal-header">
<h3 id="node-manage-title">节点详情与管理</h3>
<button id="node-manage-close-btn" class="modal-close" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div class="modal-body">
<!-- 基础信息 + 槽位展示 -->
<div id="node-manage-overview" class="node-manage-section">
<div class="form-group">
<label>节点 ID</label>
<div id="node-manage-id" class="node-manage-value mono"></div>
</div>
<div class="node-manage-grid">
<div class="form-group">
<label>节点状态</label>
<div id="node-manage-status"></div>
</div>
<div class="form-group">
<label>CPU 槽位 (占用/配额/最大)</label>
<div id="node-manage-slots" class="node-manage-value tabular-num"></div>
</div>
<div class="form-group">
<label>CPU / 内存</label>
<div id="node-manage-usage" class="node-manage-value tabular-num"></div>
</div>
<div class="form-group">
<label>心跳时间</label>
<div id="node-manage-heartbeat" class="node-manage-value"></div>
</div>
</div>
</div>
<!-- 修改配额 (CPU 核数) -->
<div class="node-manage-section">
<h4 class="node-manage-section-title">并发槽位配额</h4>
<p class="text-hint-mb">限制该节点最大并发领用的任务数(CPU 核数)。正在运行的任务不会被中断,等其结束后自然回落到新配额以下;设为 0 可暂停接收新任务,留空清除限制恢复物理上限。</p>
<div class="row-wrap" style="gap: 0.5rem; align-items: flex-end;">
<div class="form-group" style="flex: 1; min-width: 120px;">
<label for="node-manage-quota-input">配额上限</label>
<input type="number" id="node-manage-quota-input" class="form-input" min="0" step="1" placeholder="留空 = 无限制" />
</div>
<button type="button" id="btn-node-save-quota" class="btn btn-primary btn-sm">保存配额</button>
<button type="button" id="btn-node-clear-quota" class="btn btn-secondary btn-sm">清除配额</button>
</div>
</div>
<!-- 节点调度控制 -->
<div class="node-manage-section">
<h4 class="node-manage-section-title">调度控制</h4>
<div class="row-wrap" style="gap: 0.5rem;">
<button type="button" id="btn-node-disable" class="btn btn-secondary btn-sm">停用节点(暂停分发)</button>
<button type="button" id="btn-node-enable" class="btn btn-primary btn-sm">启用节点(恢复分发)</button>
</div>
</div>
<!-- 安全管理 -->
<div class="node-manage-section">
<h4 class="node-manage-section-title">安全管理</h4>
<p class="text-hint-mb">重发后旧 Token 立即失效,该节点下次心跳/领用将 401 退出。请随后把新 Token 同步到节点。</p>
<button type="button" id="btn-node-reissue" class="btn btn-secondary btn-sm">重新颁发专属 Token</button>
</div>
</div>
<div class="modal-footer">
<button type="button" id="btn-close-node-manage" class="btn btn-secondary">关闭</button>
</div>
</div>
</div>
<!-- 页脚 -->
<footer class="footer">
<p>DCTS 分布式恒星大气计算系统 &copy; 2026 TLUSTY/SYNSPEC Project</p>
</footer>
<!-- ===== 全局 Toast 提示容器 ===== -->
<div id="toast-container" class="toast-container" aria-live="polite" aria-atomic="true"></div>
<!-- ===== 通用自定义 Confirm 模态弹窗 ===== -->
<div id="modal-confirm" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="confirm-modal-title" aria-describedby="confirm-modal-msg">
<div class="login-card card modal-card">
<div class="login-header">
<h3 id="confirm-modal-title" class="confirm-modal-title">确认操作</h3>
<p id="confirm-modal-msg" class="confirm-modal-msg">您确定要执行此操作吗?</p>
</div>
<div class="modal-footer confirm-modal-footer">
<button type="button" id="btn-confirm-cancel" class="btn btn-secondary">取消</button>
<button type="button" id="btn-confirm-ok" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>