fix(scum): gate plugin features by companion support

This commit is contained in:
npc0-hue
2026-07-29 11:28:19 +08:00
parent 63022baa18
commit 47738acfd6
8 changed files with 38 additions and 25 deletions
@@ -35,8 +35,8 @@ const plugin: GamePluginResponse = {
title: "文件与配置",
path: "/files-config",
bundleKey: "scum-server-plugin",
bundleVersion: "1.0.0",
bundleIntegritySha256: "sha256:8a4216107e1d7773d42a7e13b6466fd4fcf6e6bb2dc5f5af398fb7f4ea4f623b",
bundleVersion: "1.0.1",
bundleIntegritySha256: "sha256:8de5ec67248be72a6fa47df5e6f8c98e10092ade99e6fc066ceeba678b119c64",
permissions: ["server.game-client.read", "server.game-client.command", "server.logs.read"],
bridgeActions: ["server.instances.read", "logs.query"]
}],
@@ -79,7 +79,7 @@ describe("PluginPageHostPage", () => {
const html = renderToStaticMarkup(<PluginPageHostPage {...props()} initialPlugin={plugin} />);
expect(html).toContain("文件与配置");
expect(html).toContain("平台托管上下文");
expect(html).toContain("scum-server-plugin@1.0.0");
expect(html).toContain("scum-server-plugin@1.0.1");
expect(html).toContain("完整性");
expect(html).toContain("返回服务器");
expect(html).not.toMatch(/sessionToken|componentKey|hostPath|dsn|runSocket|credential/i);
+4 -6
View File
@@ -9,7 +9,7 @@ import { EmptyState, ErrorState, LoadingState } from "../components/StateViews";
import type { PageComponentProps } from "../contracts/page";
import { pluginBridgeManifestContractFromResponse } from "../contracts/pluginBridge";
import { createPluginBridgeHostContext } from "../utils/pluginBridgeHost";
import { loadPluginPageBundle } from "../utils/pluginPageBundles";
import { loadPluginPageBundle, type PluginPageAvailability } from "../utils/pluginPageBundles";
type PluginPageState =
| { status: "loading" }
@@ -25,9 +25,9 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin }: Plugin
const routeKey = params.routeKey ?? "";
const serverId = params.serverId ?? "";
const [state, setState] = useState<PluginPageState>(() => initialPlugin ? { status: "ready", plugin: initialPlugin } : { status: "loading" });
const [bundle, setBundle] = useState<ComponentType<{ context: ReturnType<typeof createPluginBridgeHostContext>; workspace?: unknown; availability: { available: boolean; reason?: string } }> | null>(null);
const [bundle, setBundle] = useState<ComponentType<{ context: ReturnType<typeof createPluginBridgeHostContext>; workspace?: unknown; availability: PluginPageAvailability }> | null>(null);
const [bundleError, setBundleError] = useState("");
const [availability, setAvailability] = useState<{ available: boolean; reason?: string }>({ available: false, reason: "正在验证 Companion 可用性。" });
const [availability, setAvailability] = useState<PluginPageAvailability>({ available: false, reason: "正在验证 Companion 可用性。" });
const load = useCallback(async () => {
if (!pluginId || !routeKey) {
@@ -61,9 +61,7 @@ export function PluginPageHostPage({ params, onNavigate, initialPlugin }: Plugin
void loadPluginPageBundle(declaredBundlePage).then((loaded) => { if (active) setBundle(() => loaded); }).catch((error) => { if (active) setBundleError(error instanceof Error ? error.message : "插件页面 bundle 加载失败。"); });
if (!serverId) { setAvailability({ available: false, reason: "插件页面没有绑定服务器。" }); return () => { active = false; }; }
void platformApiClient.getGameClientBridgeStatus(serverId).then((status) => {
const requiredFeatures = declaredBundlePage.featureKeys ?? [];
const unavailable = requiredFeatures.map((key) => status.features?.find((feature) => feature.key === key)).find((feature) => !feature?.available);
if (active) setAvailability(unavailable ? { available: false, reason: unavailable.reason || `功能 ${unavailable.key} 没有兼容的 Companion 实现。` } : { available: status.available, reason: status.reason });
if (active) setAvailability({ available: status.available, reason: status.reason, features: status.features });
}).catch((error) => { if (active) setAvailability({ available: false, reason: error instanceof Error ? error.message : "无法验证 Companion 可用性。" }); });
return () => { active = false; };
}, [declaredBundlePage, serverId]);