fix: only plugins with on_install are managed - skip others entirely
- Discover(): skip plugins without on_install hook (not shown in UI) - Enable(): simplified - just check Installed flag - SyncConfig(): simplified - no HasInstall special case - Frontend: simplified toggle logic (only installed/uninstalled states) - Tests: updated all test fixtures to include on_install hook
This commit is contained in:
parent
5c769c92a0
commit
3f787ec66d
|
|
@ -107,11 +107,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="plugin-toggle">
|
||||
{#if p.hasInstall && !p.installed}
|
||||
{#if !p.installed}
|
||||
<button class="install-btn" on:click={() => install(p)}>
|
||||
📦 Установить
|
||||
</button>
|
||||
{:else if p.hasInstall && p.installed}
|
||||
{:else}
|
||||
<div class="toggle-group">
|
||||
<button
|
||||
class="toggle-btn"
|
||||
|
|
@ -126,16 +126,6 @@
|
|||
🗑
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class="toggle-btn"
|
||||
class:active={p.active}
|
||||
on:click={() => toggle(p)}
|
||||
role="switch"
|
||||
aria-checked={p.active}
|
||||
>
|
||||
<span class="toggle-knob"></span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ func (m *Manager) Discover() {
|
|||
os.MkdirAll(dataDir, 0o750)
|
||||
|
||||
hasInstall := meta.Hooks["on_install"] != ""
|
||||
if !hasInstall {
|
||||
log.Printf("[plugins] %s: skipping — no on_install hook (not a managed plugin)", e.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
m.plugins = append(m.plugins, Plugin{
|
||||
Meta: meta,
|
||||
|
|
@ -137,7 +141,7 @@ func (m *Manager) Discover() {
|
|||
DataDir: dataDir,
|
||||
Active: false,
|
||||
Installed: false,
|
||||
HasInstall: hasInstall,
|
||||
HasInstall: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -158,10 +162,6 @@ func (m *Manager) SyncConfig(cfg *config.AppConfig) {
|
|||
}
|
||||
for i := range m.plugins {
|
||||
installed := installedSet[m.plugins[i].Meta.Name]
|
||||
// Plugins without on_install hook are always "installed"
|
||||
if !m.plugins[i].HasInstall {
|
||||
installed = true
|
||||
}
|
||||
m.plugins[i].Installed = installed
|
||||
m.plugins[i].Active = installed && enabledSet[m.plugins[i].Meta.Name]
|
||||
}
|
||||
|
|
@ -352,16 +352,14 @@ type NodeMeta struct {
|
|||
Type string `json:"type"` // text, url, etc.
|
||||
}
|
||||
|
||||
// Enable activates a plugin by name.
|
||||
// If the plugin has on_install hook, it must be installed first.
|
||||
// Enable activates a plugin by name. Plugin must be installed first.
|
||||
func (m *Manager) Enable(name string) error {
|
||||
for i := range m.plugins {
|
||||
if m.plugins[i].Meta.Name == name {
|
||||
p := &m.plugins[i]
|
||||
if p.HasInstall && !p.Installed {
|
||||
if !m.plugins[i].Installed {
|
||||
return fmt.Errorf("plugin %q must be installed first", name)
|
||||
}
|
||||
p.Active = true
|
||||
m.plugins[i].Active = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ func TestDiscover(t *testing.T) {
|
|||
"plugin.json": []byte(`{
|
||||
"name": "client",
|
||||
"version": "1.0.0",
|
||||
"description": "Client template",
|
||||
"description": "Client plugin",
|
||||
"hooks": { "on_install": "on_install" },
|
||||
"templates": ["client"]
|
||||
}`),
|
||||
},
|
||||
|
|
@ -78,7 +79,8 @@ func TestDiscover(t *testing.T) {
|
|||
t.Errorf("plugin name = %q", plugins[0].Meta.Name)
|
||||
}
|
||||
|
||||
// Enable plugin to load templates
|
||||
// Install & enable plugin to load templates
|
||||
mgr.Install("client")
|
||||
mgr.Enable("client")
|
||||
|
||||
// Templates.
|
||||
|
|
@ -104,12 +106,12 @@ func TestEnableDisable(t *testing.T) {
|
|||
root := setupPluginDir(t, map[string]*fsDir{
|
||||
"plugin-a": {
|
||||
files: map[string][]byte{
|
||||
"plugin.json": []byte(`{"name": "a", "version": "1.0"}`),
|
||||
"plugin.json": []byte(`{"name": "a", "version": "1.0", "hooks": {"on_install": "on_install"}}`),
|
||||
},
|
||||
},
|
||||
"plugin-b": {
|
||||
files: map[string][]byte{
|
||||
"plugin.json": []byte(`{"name": "b", "version": "1.0"}`),
|
||||
"plugin.json": []byte(`{"name": "b", "version": "1.0", "hooks": {"on_install": "on_install"}}`),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -126,7 +128,9 @@ func TestEnableDisable(t *testing.T) {
|
|||
t.Errorf("active after discover = %d, want 0", len(mgr.Active()))
|
||||
}
|
||||
|
||||
// Enable both.
|
||||
// Install & enable both.
|
||||
mgr.Install("a")
|
||||
mgr.Install("b")
|
||||
mgr.Enable("a")
|
||||
mgr.Enable("b")
|
||||
if len(mgr.Active()) != 2 {
|
||||
|
|
@ -148,12 +152,14 @@ func TestEnableDisable(t *testing.T) {
|
|||
|
||||
func TestActiveNames(t *testing.T) {
|
||||
root := setupPluginDir(t, map[string]*fsDir{
|
||||
"p1": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p1"}`)}},
|
||||
"p2": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p2"}`)}},
|
||||
"p1": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p1","hooks":{"on_install":"on_install"}}`)}},
|
||||
"p2": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p2","hooks":{"on_install":"on_install"}}`)}},
|
||||
})
|
||||
|
||||
mgr := NewManager(root)
|
||||
mgr.Discover()
|
||||
mgr.Install("p1")
|
||||
mgr.Install("p2")
|
||||
mgr.Enable("p1")
|
||||
mgr.Enable("p2")
|
||||
mgr.Disable("p1")
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
|
|||
pj := `{
|
||||
"name": "testp",
|
||||
"version": "1.0.0",
|
||||
"hooks": { "on_init": "on_init" }
|
||||
"hooks": { "on_install": "on_install", "on_init": "on_init" }
|
||||
}`
|
||||
if err := os.WriteFile(filepath.Join(pluginsDir, "plugin.json"), []byte(pj), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -228,6 +228,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
|
|||
|
||||
mgr := NewManager(dir)
|
||||
mgr.Discover()
|
||||
mgr.Install("testp")
|
||||
mgr.Enable("testp")
|
||||
mgr.InitRuntimes()
|
||||
defer mgr.CloseRuntimes()
|
||||
|
|
|
|||
Loading…
Reference in New Issue