fix(plugin): DisablePlugin 禁止禁用未安装插件,防止脏写 disabled_plugins

问题:POST /api/v1/plugins/<name>/disable 对不存在的插件也会把它写进
disabled_plugins 表(registry.go DisablePlugin 无条件 AddDisabledPlugin),
产生脏数据堆积,且同名插件日后真实安装会被误判为已禁用。

修复:
- registry.go 新增 pluginInstalled(name):已加载 / 已注册工厂(内置) / 插件目录存在,
  任一命中视为已安装
- DisablePlugin 开头校验:未安装返回 'plugin X not installed',不写 disabled_plugins
- webui handler:未安装→404,已禁用→409(原都返回500);enable 失败含'failed'→404
- 新增 registry_disable_test.go:覆盖未安装拒绝/内置判定/目录存在判定/普通文件不算

本机端到端验证:disable 不存在插件返回404且表无脏数据;真实插件 disable/enable 正常
This commit is contained in:
JianFeeeee
2026-08-27 23:29:49 +08:00
parent 2564e53342
commit 5fccc31afc
3 changed files with 122 additions and 3 deletions

View File

@ -2600,7 +2600,16 @@ func (h *Handler) handlePluginByID(w http.ResponseWriter, r *http.Request) {
return
}
if err := h.pluginMgr.DisablePlugin(name, "webui"); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
// 未安装 → 404已禁用 → 409其余为真实内部错误
status := http.StatusInternalServerError
msg := err.Error()
switch {
case strings.HasSuffix(msg, "not installed"):
status = http.StatusNotFound
case strings.HasSuffix(msg, "already disabled"):
status = http.StatusConflict
}
writeJSON(w, status, map[string]string{"error": msg})
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "disabled"})
@ -2616,7 +2625,12 @@ func (h *Handler) handlePluginByID(w http.ResponseWriter, r *http.Request) {
return
}
if err := h.pluginMgr.EnablePlugin(name); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
// 启用失败多数是“插件不存在/加载失败” → 404 更贴切
status := http.StatusInternalServerError
if strings.Contains(err.Error(), "failed") {
status = http.StatusNotFound
}
writeJSON(w, status, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "enabled"})