mirror of https://github.com/jetkvm/kvm.git
Add ability to uninstall a plugin
This commit is contained in:
parent
5652e8f95a
commit
562f6c406c
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -58,3 +59,34 @@ func (d *PluginDatabase) Save() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find all extract directories that are not referenced in the Plugins map and remove them
|
||||
func (d *PluginDatabase) CleanupExtractDirectories() error {
|
||||
extractDirectories, err := os.ReadDir(pluginsExtractsFolder)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read extract directories: %v", err)
|
||||
}
|
||||
|
||||
for _, extractDir := range extractDirectories {
|
||||
found := false
|
||||
for _, pluginInstall := range d.Plugins {
|
||||
for _, extractedFolder := range pluginInstall.ExtractedVersions {
|
||||
if extractDir.Name() == extractedFolder {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
if err := os.RemoveAll(path.Join(pluginsExtractsFolder, extractDir.Name())); err != nil {
|
||||
return fmt.Errorf("failed to remove extract directory: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ func RpcPluginList() ([]PluginStatus, error) {
|
|||
return plugins, nil
|
||||
}
|
||||
|
||||
func RpcUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
||||
func RpcPluginUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
||||
pluginInstall, ok := pluginDatabase.Plugins[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin not found: %s", name)
|
||||
|
@ -193,6 +193,32 @@ func RpcUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
|||
return status, nil
|
||||
}
|
||||
|
||||
func RpcPluginUninstall(name string) error {
|
||||
pluginInstall, ok := pluginDatabase.Plugins[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("plugin not found: %s", name)
|
||||
}
|
||||
|
||||
pluginInstall.Enabled = false
|
||||
|
||||
err := pluginInstall.ReconcileSubprocess()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop plugin %s: %v", name, err)
|
||||
}
|
||||
|
||||
delete(pluginDatabase.Plugins, name)
|
||||
if err := pluginDatabase.Save(); err != nil {
|
||||
return fmt.Errorf("failed to save plugin database: %v", err)
|
||||
}
|
||||
|
||||
err = pluginDatabase.CleanupExtractDirectories()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to cleanup extract directories: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readManifest(extractFolder string) (*PluginManifest, error) {
|
||||
manifestPath := path.Join(extractFolder, "manifest.json")
|
||||
manifestFile, err := os.Open(manifestPath)
|
||||
|
|
|
@ -559,5 +559,6 @@ var rpcHandlers = map[string]RPCHandler{
|
|||
"pluginExtract": {Func: plugin.RpcPluginExtract, Params: []string{"filename"}},
|
||||
"pluginInstall": {Func: plugin.RpcPluginInstall, Params: []string{"name", "version"}},
|
||||
"pluginList": {Func: plugin.RpcPluginList},
|
||||
"pluginUpdateConfig": {Func: plugin.RpcUpdateConfig, Params: []string{"name", "enabled"}},
|
||||
"pluginUpdateConfig": {Func: plugin.RpcPluginUpdateConfig, Params: []string{"name", "enabled"}},
|
||||
"pluginUninstall": {Func: plugin.RpcPluginUninstall, Params: []string{"name"}},
|
||||
}
|
||||
|
|
|
@ -46,6 +46,24 @@ function Dialog({ plugin, setOpen }: { plugin: PluginStatus | null, setOpen: (op
|
|||
setLoading(true);
|
||||
send("pluginUpdateConfig", { name: plugin.name, enabled }, resp => {
|
||||
if ("error" in resp) {
|
||||
setLoading(false);
|
||||
setError(resp.error.message);
|
||||
return
|
||||
}
|
||||
setOpen(false);
|
||||
});
|
||||
}, [send, plugin, setOpen])
|
||||
|
||||
const uninstallPlugin = useCallback(() => {
|
||||
if (!plugin) return;
|
||||
if (!window.confirm("Are you sure you want to uninstall this plugin? This will not delete any data.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
send("pluginUninstall", { name: plugin.name }, resp => {
|
||||
if ("error" in resp) {
|
||||
setLoading(false);
|
||||
setError(resp.error.message);
|
||||
return
|
||||
}
|
||||
|
@ -104,6 +122,15 @@ function Dialog({ plugin, setOpen }: { plugin: PluginStatus | null, setOpen: (op
|
|||
animationDelay: "0.1s",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center w-full space-x-2">
|
||||
<Button
|
||||
size="MD"
|
||||
theme="blank"
|
||||
text="Uninstall Plugin"
|
||||
disabled={loading}
|
||||
onClick={uninstallPlugin}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end w-full space-x-2">
|
||||
<Button
|
||||
size="MD"
|
||||
|
|
|
@ -629,6 +629,7 @@ function InstallPluginView({
|
|||
size="MD"
|
||||
theme="primary"
|
||||
text="Install"
|
||||
disabled={!manifest || installing}
|
||||
onClick={handleInstall}
|
||||
/>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue