diff --git a/packages/lib/src/plugin.ts b/packages/lib/src/plugin.ts index ca878706..98953b63 100644 --- a/packages/lib/src/plugin.ts +++ b/packages/lib/src/plugin.ts @@ -43,9 +43,10 @@ export type PluginNodeEnvInfo = PluginDeclaration & { }; export type PluginWebpackEnvInfo = PluginDeclaration & { - container: any; - package: any; + container?: any; + package?: any; enabled?: boolean; + error?: string; }; /** diff --git a/packages/web_ui/src/bootstrap.tsx b/packages/web_ui/src/bootstrap.tsx index 9c018e7d..be280ecf 100644 --- a/packages/web_ui/src/bootstrap.tsx +++ b/packages/web_ui/src/bootstrap.tsx @@ -38,7 +38,7 @@ async function loadPluginInfos(): Promise { pluginList = []; } - let pluginInfos = []; + let pluginInfos: lib.PluginWebpackEnvInfo[] = []; await __webpack_init_sharing__("default"); for (let meta of pluginList) { if (meta.web.error) { @@ -59,6 +59,12 @@ async function loadPluginInfos(): Promise { pluginInfos.push(pluginInfo); } catch (err: any) { + pluginInfos.push({ + name: meta.name, + title: meta.name, + enabled: false, + error: err.message, + }); logger.error(`Failed to load plugin info for ${meta.name}`); if (err.stack) { logger.error(err.stack); @@ -79,7 +85,8 @@ async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[], control: Con if (pluginInfo.webEntrypoint) { let webModule = (await pluginInfo.container.get(pluginInfo.webEntrypoint))(); if (!webModule.WebPlugin) { - throw new Error("Plugin webEntrypoint does not export WebPlugin class"); + pluginInfo.error = "Plugin webEntrypoint does not export WebPlugin class"; + throw new Error(pluginInfo.error); } WebPluginClass = webModule.WebPlugin; } @@ -89,6 +96,7 @@ async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[], control: Con plugins.set(pluginInfo.name, plugin); } catch (err: any) { + pluginInfo.error = `Error loading plugin: ${err.message}`; logger.error(`Failed to load plugin ${pluginInfo.name}`); if (err.stack) { logger.error(err.stack); @@ -124,7 +132,7 @@ export default async function bootstrap() { let wsUrl = new URL(webRoot, document.location.href); let controlConnector = new ControlConnector(wsUrl.href, 120, undefined); - let control = new Control(controlConnector); + let control = new Control(controlConnector, new Map(pluginInfos.map(p => [p.name, p]))); control.plugins = await loadPlugins(pluginInfos, control); control.inputComponents = inputComponentsFromPlugins(control.plugins); diff --git a/packages/web_ui/src/components/PluginViewPage.tsx b/packages/web_ui/src/components/PluginViewPage.tsx index 01aef936..8b9281fd 100644 --- a/packages/web_ui/src/components/PluginViewPage.tsx +++ b/packages/web_ui/src/components/PluginViewPage.tsx @@ -10,7 +10,7 @@ import notify from "../util/notify"; export default function PluginViewPage() { let params = useParams(); - let plugins = useContext(ControlContext).plugins; + const control = useContext(ControlContext); let [pluginList, setPluginList] = useState(); let pluginName = String(params.name); @@ -25,8 +25,9 @@ export default function PluginViewPage() { })(); }, []); - let plugin = plugins.get(pluginName); - let pluginTitle = plugin ? plugin.info.title : pluginName; + const plugin = control.plugins.get(pluginName); + const pluginInfo = control.pluginInfos.get(pluginName); + const pluginTitle = pluginInfo ? pluginInfo.title : pluginName; let nav = [{ name: "Plugins", path: "/plugins" }, { name: pluginTitle }]; if (!pluginList) { return @@ -43,33 +44,42 @@ export default function PluginViewPage() { ; } + const pluginError = pluginMeta.web.error ?? pluginInfo?.error; if (!plugin) { return {pluginMeta.version} - {pluginMeta.loaded ? "Yes" : "No"} + No + {pluginMeta.loaded ? "Yes" : "No"} + + {pluginMeta.enabled ? "Yes" : "No"} + - + />} ; } return {pluginMeta.version} - {pluginMeta.loaded ? "Yes" : "No"} + Yes + {pluginMeta.loaded ? "Yes" : "No"} + + {pluginMeta.enabled ? "Yes" : "No"} + {plugin.info.description} {plugin.package.homepage ? {plugin.package.homepage} diff --git a/packages/web_ui/src/components/PluginsPage.tsx b/packages/web_ui/src/components/PluginsPage.tsx index bd976f66..f12b3d00 100644 --- a/packages/web_ui/src/components/PluginsPage.tsx +++ b/packages/web_ui/src/components/PluginsPage.tsx @@ -1,6 +1,8 @@ import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Table } from "antd"; +import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled"; +import InfoCircleFilled from "@ant-design/icons/InfoCircleFilled"; import type { PluginWebApi } from "@clusterio/lib"; @@ -12,7 +14,7 @@ const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" export default function PluginsPage() { - let plugins = useContext(ControlContext).plugins; + const control = useContext(ControlContext); let navigate = useNavigate(); let [pluginList, setPluginList] = useState([]); @@ -29,8 +31,8 @@ export default function PluginsPage() { let tableContents = []; for (let meta of pluginList) { - if (plugins.has(meta.name)) { - let plugin = plugins.get(meta.name)!; + if (control.plugins.has(meta.name)) { + let plugin = control.plugins.get(meta.name)!; tableContents.push({ meta, info: plugin.info, @@ -39,6 +41,7 @@ export default function PluginsPage() { } else { tableContents.push({ meta, + info: control.pluginInfos.get(meta.name), }); } } @@ -58,8 +61,11 @@ export default function PluginsPage() { title: "Version", key: "version", render: (_, plugin) => { + if (!plugin.meta.enabled) { + return <> Disabled on controller; + } if (!plugin.package) { - return "Error loading module"; + return <> Error loading module; } if (plugin.package.version !== plugin.meta.version) { return "Version missmatched"; @@ -70,9 +76,8 @@ export default function PluginsPage() { }, { title: "Loaded", - dataIndex: ["meta", "loaded"], - render: loaded => (loaded ? "Yes" : null), - sorter: (a, b) => Number(a.meta.loaded) - Number(b.meta.loaded), + render: (_, plugin) => (plugin.package ? "Yes" : null), + sorter: (a, b) => Number(Boolean(a.package)) - Number(Boolean(b.package)), responsive: ["sm"], }, ]} diff --git a/packages/web_ui/src/util/websocket.ts b/packages/web_ui/src/util/websocket.ts index dee94dac..49573b22 100644 --- a/packages/web_ui/src/util/websocket.ts +++ b/packages/web_ui/src/util/websocket.ts @@ -72,6 +72,7 @@ export class Control extends lib.Link { constructor( connector: ControlConnector, + public pluginInfos = new Map(), ) { super(connector);