Complete SCUM controlled deployment lifecycle

This commit is contained in:
npc0-hue
2026-07-25 10:13:53 +08:00
parent 6cfd929f7f
commit 15789e15b4
28 changed files with 1349 additions and 123 deletions
+1 -1
View File
@@ -179,7 +179,7 @@ describe("first-party console pages", () => {
expect(serverDeploymentWorkflowSource).toContain("仅保存为草稿");
expect(serverDeploymentWorkflowSource).toContain("执行目录(可选)");
expect(serverDeploymentWorkflowSource).toContain("默认使用服务器目录");
expect(serverDeploymentWorkflowSource).toContain("安装目录(可选)");
expect(serverDeploymentWorkflowSource).toContain("安装目录{isScum ? \"(必填)\" : \"(可选)\"}");
expect(serverDeploymentWorkflowSource).toContain("已有服务器目录");
expect(serverDeploymentWorkflowSource).toContain("不会重装或覆盖现有游戏配置");
expect(serverDeploymentWorkflowSource).toContain("运行连接设置");
+16
View File
@@ -451,14 +451,30 @@ function ServerDeploymentSection({ instance, deployment, onEdit }: ServerDeploym
if (deployment.status === "loading") return <LoadingState label="正在加载部署定义…" compact />;
if (deployment.status === "error") return <ErrorState title="部署定义不可用" reason={deployment.reason} diagnosticId={`deployment:${instance.id}`} compact />;
const view = deployment.data;
const projection = view.projection;
const isScumTemplate = (instance.pluginId === "game.scum" && (view.mode === "guided-install" || view.mode === "existing-server")) || projection?.templateKey?.startsWith("scum-");
return <article className="console-panel" aria-label="server deployment">
<div className="panel-header"><h2><PackageOpen size={16} style={{ verticalAlign: "-2px" }} /> </h2><span className="page-status">{view.mode || "未配置"} · {view.revision}</span></div>
<p className="section-copy"></p>
<div className="console-row-list"><div className="console-row"><span></span><strong>{view.serverRootConfigured ? "已配置" : "未配置"}</strong></div><div className="console-row"><span></span><strong>{view.workingDirectoryConfigured ? "已配置" : "使用服务器目录"}</strong></div><div className="console-row"><span></span><strong>{view.startCommandConfigured ? "已配置" : view.mode === "custom-command" ? "未配置" : "插件引导"}</strong></div></div>
{isScumTemplate && <div className="console-row-list" style={{ marginTop: 12 }}><div className="console-row"><span>SCUM </span><strong>{projection?.templateVersion ? `${projection.templateKey ?? "已选择"} · v${projection.templateVersion}` : "等待 Run 预检"}</strong></div><div className="console-row"><span> / </span><strong>{deploymentProjectionLabel(projection?.preflightState)} / {deploymentProjectionLabel(projection?.discoveryState)}</strong></div><div className="console-row"><span> / </span><strong>{deploymentProjectionLabel(projection?.mappingState)} / {deploymentProjectionLabel(projection?.verificationState)}</strong></div>{projection?.failureCode && <div className="console-row"><span></span><strong>{projection.failureCode}</strong></div>}</div>}
<div className="action-strip" style={{ marginTop: 12 }}><button type="button" className="primary-command" disabled={instance.state === "running" || instance.state === "installing"} onClick={onEdit}><Pencil size={14} /><span></span></button>{(instance.state === "draft" || instance.state === "failed") && <span className="field-help"></span>}</div>
</article>;
}
function deploymentProjectionLabel(value?: string): string {
switch (value) {
case "queued": return "排队中";
case "running": return "执行中";
case "passed": return "已通过";
case "applied": return "已写入";
case "unchanged": return "未变化";
case "failed": return "失败";
case "skipped": return "已跳过";
default: return "待返回";
}
}
function deploymentWorkflowForm(instance: ServerInstanceResponse, deployment: ServerDeploymentResponse | undefined, plugins: GamePluginResponse[], endpoints: RunEndpointResponse[]): ServerCreateFormState {
const plugin = plugins.find((item) => item.id === instance.pluginId);
return { ...defaultServerCreateForm(plugins, endpoints), name: instance.name, pluginId: instance.pluginId, runEndpointId: instance.runEndpointId, profileKey: deployment?.profileKey ?? plugin?.runtimeProfiles?.lifecycleProfiles?.[0]?.key ?? "", createInputs: deployment?.createInputs ?? pluginCreateInputDefaults(plugin), deploymentMode: deployment?.mode ?? "guided-install", shell: deployment?.shell ?? "" };