import { PackageCheck, RotateCw } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { platformApiClient } from "../api/client"; import type { PluginLifecycleInstallationResponse, PluginLifecycleOperation, ServerInstanceResponse } from "../api/types"; import { ErrorState, LoadingState, ResultBadge } from "./StateViews"; interface PluginLifecycleWorkbenchProps { pluginId: string; pluginName: string; operations?: PluginLifecycleOperation[]; serverId?: string; disabled?: boolean; } export function PluginLifecycleWorkbench({ pluginId, pluginName, operations = lifecycleOperations, serverId, disabled = false }: PluginLifecycleWorkbenchProps) { const [servers, setServers] = useState([]); const [installations, setInstallations] = useState([]); const [selectedServerId, setSelectedServerId] = useState(serverId ?? ""); const [operation, setOperation] = useState(operations[0] ?? "install"); const [targetVersion, setTargetVersion] = useState(""); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [result, setResult] = useState<{ status: "succeeded" | "failed" | "pending"; label: string } | null>(null); const refresh = useCallback(async () => { setLoading(true); setError(""); try { const [serverResponse, lifecycleResponse] = await Promise.all([ platformApiClient.listServerInstances(), platformApiClient.listPluginLifecycles({ pluginId, ...(serverId ? { serverInstanceId: serverId } : {}) }) ]); const compatibleServers = serverResponse.items.filter((server) => server.pluginId === pluginId && (!serverId || server.id === serverId)); setServers(compatibleServers); setInstallations(lifecycleResponse.items); setSelectedServerId((current) => current || compatibleServers[0]?.id || ""); } catch (caught) { setError(caught instanceof Error ? caught.message : "插件生命周期加载失败"); } finally { setLoading(false); } }, [pluginId, serverId]); useEffect(() => { void refresh(); }, [refresh]); const installation = useMemo(() => installations.find((item) => item.serverInstanceId === selectedServerId), [installations, selectedServerId]); async function submit() { if (!selectedServerId || busy) return; setBusy(true); setResult({ status: "pending", label: `${lifecycleOperationLabel(operation)}提交中…` }); try { const response = await platformApiClient.runPluginLifecycle(pluginId, { serverInstanceId: selectedServerId, operation, targetVersion: targetVersion.trim() || undefined, idempotencyKey: `web:plugin.lifecycle:${pluginId}:${selectedServerId}:${operation}:${Date.now()}` }); const evidence = [response.job?.id && `任务 ${response.job.id}`].filter(Boolean).join(" · "); setResult({ status: response.status === "queued" || response.status === "accepted" ? "succeeded" : response.status === "deferred" ? "pending" : "failed", label: `${lifecycleOperationLabel(operation)}:${response.status}${evidence ? ` · ${evidence}` : ""}` }); await refresh(); } catch (caught) { setResult({ status: "failed", label: caught instanceof Error ? caught.message : "插件生命周期操作失败" }); } finally { setBusy(false); } } return (

生产生命周期

{result && } {loading && } {!loading && error && void refresh()} compact />} {!loading && !error && ( <>
{(operation === "install" || operation === "upgrade") && setTargetVersion(event.target.value)} />}
{installation ? (
{installation.currentState} → {installation.desiredState}{installation.compatibility || "pending"}
当前 {installation.currentVersion || "--"}目标 {installation.targetVersion || "--"}依赖 {installation.dependencyState || "unknown"} {installation.jobId && 任务 {installation.jobId}}
{installation.failureReason &&

{installation.failureReason}

}
) :

该服务器尚无插件生命周期记录。

} )}
); } const lifecycleOperations: PluginLifecycleOperation[] = ["install", "enable", "disable", "upgrade", "rollback", "retire", "dependency-check"]; function lifecycleOperationLabel(operation: PluginLifecycleOperation) { return ({ install: "安装", enable: "启用", disable: "停用", upgrade: "升级", rollback: "回滚", retire: "退役", "dependency-check": "依赖检查" } as Record)[operation]; }