diff --git a/packages/controller/src/ControlConnection.ts b/packages/controller/src/ControlConnection.ts index 410e1fd9..a2f7be4c 100644 --- a/packages/controller/src/ControlConnection.ts +++ b/packages/controller/src/ControlConnection.ts @@ -1075,7 +1075,13 @@ export default class ControlConnection extends BaseConnection { } async handlePluginListRequest(request: lib.PluginListRequest) { - return this._controller.pluginInfos.map(pluginInfo => lib.PluginDetails.fromNodeEnvInfo(pluginInfo)); + return this._controller.pluginInfos.map(pluginInfo => lib.PluginDetails.fromNodeEnvInfo( + pluginInfo, + this._controller.plugins.has(pluginInfo.name), + this._controller.config.get( + `${pluginInfo.name}.load_plugin` as keyof lib.ControllerConfigFields + ) as boolean, + )); } async handleDebugDumpWsRequest(request: lib.DebugDumpWsRequest) { diff --git a/packages/ctl/src/commands.ts b/packages/ctl/src/commands.ts index 23d60619..b634e3aa 100644 --- a/packages/ctl/src/commands.ts +++ b/packages/ctl/src/commands.ts @@ -537,7 +537,7 @@ hostPluginCommands.add(new lib.Command({ let hostId = await lib.resolveHost(control, args.host); const plugins = await control.sendTo({ hostId }, new lib.PluginListRequest()); print(asTable(plugins.map(p => ({ - title: p.title, version: p.version, npmPackage: p.npmPackage, + title: p.title, version: p.version, loaded: p.loaded, enabled: p.enabled, npmPackage: p.npmPackage, })))); }, })); diff --git a/packages/host/src/Host.ts b/packages/host/src/Host.ts index 4cf0df72..be6cb8e7 100644 --- a/packages/host/src/Host.ts +++ b/packages/host/src/Host.ts @@ -1071,7 +1071,11 @@ export default class Host extends lib.Link { } async handlePluginListRequest(request: lib.PluginListRequest) { - return this.pluginInfos.map(pluginInfo => lib.PluginDetails.fromNodeEnvInfo(pluginInfo)); + return this.pluginInfos.map(pluginInfo => lib.PluginDetails.fromNodeEnvInfo( + pluginInfo, + this.plugins.has(pluginInfo.name), + this.config.get(`${pluginInfo.name}.load_plugin` as keyof lib.HostConfigFields) as boolean, + )); } sendHostUpdate() { diff --git a/packages/lib/src/data/messages_plugin.ts b/packages/lib/src/data/messages_plugin.ts index 09bf687e..edbe7664 100644 --- a/packages/lib/src/data/messages_plugin.ts +++ b/packages/lib/src/data/messages_plugin.ts @@ -8,6 +8,8 @@ export class PluginDetails { public name: string, public title: string, public version: string, + public loaded: boolean, + public enabled: boolean, public description?: string, public npmPackage?: string, ) {} @@ -16,19 +18,23 @@ export class PluginDetails { name: Type.String(), title: Type.String(), version: Type.String(), + loaded: Type.Boolean(), + enabled: Type.Boolean(), description: Type.Optional(Type.String()), npmPackage: Type.Optional(Type.String()), }); static fromJSON(json: Static) { - return new this(json.name, json.title, json.version, json.description, json.npmPackage); + return new this( + json.name, json.title, json.version, json.loaded, json.enabled, + json.description, json.npmPackage + ); } - static fromNodeEnvInfo(pluginInfo: PluginNodeEnvInfo) { + static fromNodeEnvInfo(pluginInfo: PluginNodeEnvInfo, loaded: boolean, enabled: boolean) { return new this( - pluginInfo.name, pluginInfo.title, - pluginInfo.version, pluginInfo.description, - pluginInfo.npmPackage, + pluginInfo.name, pluginInfo.title, pluginInfo.version, loaded, enabled, + pluginInfo.description, pluginInfo.npmPackage, ); } } diff --git a/packages/web_ui/src/components/NpmButton.tsx b/packages/web_ui/src/components/NpmButton.tsx index 42d5be00..b02ac81a 100644 --- a/packages/web_ui/src/components/NpmButton.tsx +++ b/packages/web_ui/src/components/NpmButton.tsx @@ -89,6 +89,40 @@ export function NpmButton(props: { target: lib.AddressShorthand, canRestart?: bo const [formAction, setFormAction] = useState(undefined); const [form] = Form.useForm(); + const [allows, setAllows] = useState({ + remoteUpdates: true, pluginUpdates: true, pluginInstall: true, + }); + + useEffect(() => { + if (props.target === "controller") { + if (!account.hasPermission("core.controller.get_config")) { + setAllows({ remoteUpdates: true, pluginUpdates: true, pluginInstall: true }); + return; // If a user doesn't have config perms, then assume true to allow the sending the request + } + control.send(new lib.ControllerConfigGetRequest()).then(async (serializedConfig) => { + const config = lib.ControllerConfig.fromJSON(serializedConfig, "control"); + setAllows({ + remoteUpdates: config.get("controller.allow_remote_updates"), + pluginUpdates: config.get("controller.allow_plugin_updates"), + pluginInstall: config.get("controller.allow_plugin_install"), + }); + }).catch(notifyErrorHandler("Failed to fetch controller config for remote updates")); + } else { + if (!account.hasPermission("core.host.get_config")) { + setAllows({ remoteUpdates: true, pluginUpdates: true, pluginInstall: true }); + return; // If a user doesn't have config perms, then assume true to allow the sending the request + } + control.sendTo(props.target, new lib.HostConfigGetRequest()).then(async (serializedConfig) => { + const config = lib.HostConfig.fromJSON(serializedConfig, "control"); + setAllows({ + remoteUpdates: config.get("host.allow_remote_updates"), + pluginUpdates: config.get("host.allow_plugin_updates"), + pluginInstall: config.get("host.allow_plugin_install"), + }); + }).catch(notifyErrorHandler("Failed to fetch host config for remote updates")); + } + }, [props.target]); + function onValuesChange({ action } : { action?: string }) { if (action) { setFormAction(action); @@ -138,11 +172,17 @@ export function NpmButton(props: { target: lib.AddressShorthand, canRestart?: bo {account.hasPermission(`core.${props.target === "controller" ? "controller" : "host"}.update`) - ? Update Clusterio : undefined} + ? + Update Clusterio + : undefined} {account.hasPermission("core.plugin.update") - ? Update Plugin : undefined} + ? + Update Plugin + : undefined} {account.hasPermission("core.plugin.install") - ? Install Plugin : undefined} + ? + Install Plugin + : undefined} {