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"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,3 +59,34 @@ func (d *PluginDatabase) Save() error {
|
||||||
|
|
||||||
return nil
|
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
|
return plugins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RpcUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
func RpcPluginUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
||||||
pluginInstall, ok := pluginDatabase.Plugins[name]
|
pluginInstall, ok := pluginDatabase.Plugins[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("plugin not found: %s", name)
|
return nil, fmt.Errorf("plugin not found: %s", name)
|
||||||
|
@ -193,6 +193,32 @@ func RpcUpdateConfig(name string, enabled bool) (*PluginStatus, error) {
|
||||||
return status, nil
|
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) {
|
func readManifest(extractFolder string) (*PluginManifest, error) {
|
||||||
manifestPath := path.Join(extractFolder, "manifest.json")
|
manifestPath := path.Join(extractFolder, "manifest.json")
|
||||||
manifestFile, err := os.Open(manifestPath)
|
manifestFile, err := os.Open(manifestPath)
|
||||||
|
|
|
@ -559,5 +559,6 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"pluginExtract": {Func: plugin.RpcPluginExtract, Params: []string{"filename"}},
|
"pluginExtract": {Func: plugin.RpcPluginExtract, Params: []string{"filename"}},
|
||||||
"pluginInstall": {Func: plugin.RpcPluginInstall, Params: []string{"name", "version"}},
|
"pluginInstall": {Func: plugin.RpcPluginInstall, Params: []string{"name", "version"}},
|
||||||
"pluginList": {Func: plugin.RpcPluginList},
|
"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);
|
setLoading(true);
|
||||||
send("pluginUpdateConfig", { name: plugin.name, enabled }, resp => {
|
send("pluginUpdateConfig", { name: plugin.name, enabled }, resp => {
|
||||||
if ("error" in 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);
|
setError(resp.error.message);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -104,6 +122,15 @@ function Dialog({ plugin, setOpen }: { plugin: PluginStatus | null, setOpen: (op
|
||||||
animationDelay: "0.1s",
|
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">
|
<div className="flex justify-end w-full space-x-2">
|
||||||
<Button
|
<Button
|
||||||
size="MD"
|
size="MD"
|
||||||
|
|
|
@ -629,6 +629,7 @@ function InstallPluginView({
|
||||||
size="MD"
|
size="MD"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
text="Install"
|
text="Install"
|
||||||
|
disabled={!manifest || installing}
|
||||||
onClick={handleInstall}
|
onClick={handleInstall}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue