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>
|
</div>
|
||||||
<div class="plugin-toggle">
|
<div class="plugin-toggle">
|
||||||
{#if p.hasInstall && !p.installed}
|
{#if !p.installed}
|
||||||
<button class="install-btn" on:click={() => install(p)}>
|
<button class="install-btn" on:click={() => install(p)}>
|
||||||
📦 Установить
|
📦 Установить
|
||||||
</button>
|
</button>
|
||||||
{:else if p.hasInstall && p.installed}
|
{:else}
|
||||||
<div class="toggle-group">
|
<div class="toggle-group">
|
||||||
<button
|
<button
|
||||||
class="toggle-btn"
|
class="toggle-btn"
|
||||||
|
|
@ -126,16 +126,6 @@
|
||||||
🗑
|
🗑
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -130,14 +130,18 @@ func (m *Manager) Discover() {
|
||||||
os.MkdirAll(dataDir, 0o750)
|
os.MkdirAll(dataDir, 0o750)
|
||||||
|
|
||||||
hasInstall := meta.Hooks["on_install"] != ""
|
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{
|
m.plugins = append(m.plugins, Plugin{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
Dir: filepath.Join(pluginsDir, e.Name()),
|
Dir: filepath.Join(pluginsDir, e.Name()),
|
||||||
DataDir: dataDir,
|
DataDir: dataDir,
|
||||||
Active: false,
|
Active: false,
|
||||||
Installed: false,
|
Installed: false,
|
||||||
HasInstall: hasInstall,
|
HasInstall: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,10 +162,6 @@ func (m *Manager) SyncConfig(cfg *config.AppConfig) {
|
||||||
}
|
}
|
||||||
for i := range m.plugins {
|
for i := range m.plugins {
|
||||||
installed := installedSet[m.plugins[i].Meta.Name]
|
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].Installed = installed
|
||||||
m.plugins[i].Active = installed && enabledSet[m.plugins[i].Meta.Name]
|
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.
|
Type string `json:"type"` // text, url, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable activates a plugin by name.
|
// Enable activates a plugin by name. Plugin must be installed first.
|
||||||
// If the plugin has on_install hook, it must be installed first.
|
|
||||||
func (m *Manager) Enable(name string) error {
|
func (m *Manager) Enable(name string) error {
|
||||||
for i := range m.plugins {
|
for i := range m.plugins {
|
||||||
if m.plugins[i].Meta.Name == name {
|
if m.plugins[i].Meta.Name == name {
|
||||||
p := &m.plugins[i]
|
if !m.plugins[i].Installed {
|
||||||
if p.HasInstall && !p.Installed {
|
|
||||||
return fmt.Errorf("plugin %q must be installed first", name)
|
return fmt.Errorf("plugin %q must be installed first", name)
|
||||||
}
|
}
|
||||||
p.Active = true
|
m.plugins[i].Active = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ func TestDiscover(t *testing.T) {
|
||||||
"plugin.json": []byte(`{
|
"plugin.json": []byte(`{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Client template",
|
"description": "Client plugin",
|
||||||
|
"hooks": { "on_install": "on_install" },
|
||||||
"templates": ["client"]
|
"templates": ["client"]
|
||||||
}`),
|
}`),
|
||||||
},
|
},
|
||||||
|
|
@ -78,7 +79,8 @@ func TestDiscover(t *testing.T) {
|
||||||
t.Errorf("plugin name = %q", plugins[0].Meta.Name)
|
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")
|
mgr.Enable("client")
|
||||||
|
|
||||||
// Templates.
|
// Templates.
|
||||||
|
|
@ -104,12 +106,12 @@ func TestEnableDisable(t *testing.T) {
|
||||||
root := setupPluginDir(t, map[string]*fsDir{
|
root := setupPluginDir(t, map[string]*fsDir{
|
||||||
"plugin-a": {
|
"plugin-a": {
|
||||||
files: map[string][]byte{
|
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": {
|
"plugin-b": {
|
||||||
files: map[string][]byte{
|
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()))
|
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("a")
|
||||||
mgr.Enable("b")
|
mgr.Enable("b")
|
||||||
if len(mgr.Active()) != 2 {
|
if len(mgr.Active()) != 2 {
|
||||||
|
|
@ -148,12 +152,14 @@ func TestEnableDisable(t *testing.T) {
|
||||||
|
|
||||||
func TestActiveNames(t *testing.T) {
|
func TestActiveNames(t *testing.T) {
|
||||||
root := setupPluginDir(t, map[string]*fsDir{
|
root := setupPluginDir(t, map[string]*fsDir{
|
||||||
"p1": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p1"}`)}},
|
"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"}`)}},
|
"p2": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p2","hooks":{"on_install":"on_install"}}`)}},
|
||||||
})
|
})
|
||||||
|
|
||||||
mgr := NewManager(root)
|
mgr := NewManager(root)
|
||||||
mgr.Discover()
|
mgr.Discover()
|
||||||
|
mgr.Install("p1")
|
||||||
|
mgr.Install("p2")
|
||||||
mgr.Enable("p1")
|
mgr.Enable("p1")
|
||||||
mgr.Enable("p2")
|
mgr.Enable("p2")
|
||||||
mgr.Disable("p1")
|
mgr.Disable("p1")
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
|
||||||
pj := `{
|
pj := `{
|
||||||
"name": "testp",
|
"name": "testp",
|
||||||
"version": "1.0.0",
|
"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 {
|
if err := os.WriteFile(filepath.Join(pluginsDir, "plugin.json"), []byte(pj), 0644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -228,6 +228,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
|
||||||
|
|
||||||
mgr := NewManager(dir)
|
mgr := NewManager(dir)
|
||||||
mgr.Discover()
|
mgr.Discover()
|
||||||
|
mgr.Install("testp")
|
||||||
mgr.Enable("testp")
|
mgr.Enable("testp")
|
||||||
mgr.InitRuntimes()
|
mgr.InitRuntimes()
|
||||||
defer mgr.CloseRuntimes()
|
defer mgr.CloseRuntimes()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue