diff --git a/AGENTS.md b/AGENTS.md index 38cb0b50d..2846b75eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,6 +76,7 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web - 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。 - 代码可读性优先于短写法。复杂条件、三元表达式、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。 - 方法调用链不要直接塞进另一个方法参数;先用有意义的局部变量承接返回值,再传入下一步。 +- 不要在单一表达式内嵌套分支、对象构造与方法调用。优先使用清晰的 `if/else` 分支;仅在确实能降低复杂度时才提取有意义的中间变量,避免为拆分而增加阅读跳转。 - 注释优先使用中文,尤其是业务规则、兼容逻辑、协议细节和隐藏风险;文件已有英文风格或引用外部术语时可保持一致。 - 遵守 DRY 和单一职责;第三次出现的业务规则、字段转换、权限判断、Repository 选择、事务传播、金额计算等逻辑,应优先抽成合适 helper 或 service 方法。 @@ -104,6 +105,7 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web - 只有需要事务传播时才定义 `ctx`;普通查询、纯函数和简单私有方法继续使用明确参数。 - 需要按事务上下文取 Repository 时,用 `BaseService.getRepo(ctx, EntityClass)`。 - 需要“有事务则复用、无事务则开启”时,用 `BaseService.transactionWithCtx(ctx, callback)`。 +- 基础 CRUD 数据访问优先复用 `BaseService` 的 `find`、`findOne`、`list`、`page`、`update`、`deleteWhere` 等方法;不要从 `repository.createQueryBuilder()` 开始重复实现完整查询或更新。仅将关键词组合筛选、联表、业务排序等基类无法表达的部分放入 `list/page` 的 `buildQuery`。 - 拼接可选 `projectId` 查询条件时,**必须**使用 `BaseService.buildUserProjectQuery(userId, projectId)`,禁止直接写 `{ userId, projectId }`。因为 `projectId` 可能为 `null`/`undefined`,直接放入查询会生成错误的 `WHERE projectId = NULL` 条件。 - `ctx` 类型复用 `BaseService` 导出的 `ServiceContext`。 - 新增 service 方法避免与 `BaseService` 方法签名冲突,例如不要用 `delete(id)` 覆盖 `delete(ids, where?)`;改用 `deleteById` 等具体名称。 diff --git a/packages/libs/lib-server/src/basic/base-service.ts b/packages/libs/lib-server/src/basic/base-service.ts index df9b3da21..2bd0d6adc 100644 --- a/packages/libs/lib-server/src/basic/base-service.ts +++ b/packages/libs/lib-server/src/basic/base-service.ts @@ -102,6 +102,18 @@ export abstract class BaseService { }); } + /** + * 按条件直接更新,不触发子类 update 的业务生命周期。 + */ + async updateWhere(where: any, data: any) { + await this.getRepository().update( + { + ...where, + }, + data + ); + } + /** * 删除 * @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3 diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index 2851810a1..de00b947b 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -34,11 +34,11 @@ "@aws-sdk/s3-request-presigner": "^3.964.0", "@certd/vue-js-cron-light": "^4.0.14", "@ctrl/tinycolor": "^4.1.0", - "@fast-crud/editor-code": "^1.28.1", - "@fast-crud/fast-crud": "^1.28.1", - "@fast-crud/fast-extends": "^1.28.1", - "@fast-crud/ui-antdv4": "^1.28.1", - "@fast-crud/ui-interface": "^1.28.1", + "@fast-crud/editor-code": "^1.28.3", + "@fast-crud/fast-crud": "^1.28.3", + "@fast-crud/fast-extends": "^1.28.3", + "@fast-crud/ui-antdv4": "^1.28.3", + "@fast-crud/ui-interface": "^1.28.3", "@iconify/tailwind": "^1.2.0", "@iconify/vue": "^4.1.1", "@manypkg/get-packages": "^2.2.2", diff --git a/packages/ui/certd-client/src/views/sys/plugin/api.ts b/packages/ui/certd-client/src/views/sys/plugin/api.ts index 43cfb3e26..426b8e8bf 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/api.ts +++ b/packages/ui/certd-client/src/views/sys/plugin/api.ts @@ -42,22 +42,6 @@ export async function GetObj(id: any) { }); } -export async function GetDetail(id: any) { - return await request({ - url: apiPrefix + "/detail", - method: "post", - params: { id }, - }); -} - -export async function DeleteBatch(ids: any[]) { - return await request({ - url: apiPrefix + "/deleteByIds", - method: "post", - data: { ids }, - }); -} - export async function SetDisabled(data: { id?: number; name?: string; type?: string; disabled: boolean }) { return await request({ url: apiPrefix + "/setDisabled", @@ -125,15 +109,6 @@ export type OnlinePluginVersionBean = { aiCheckResult?: string; }; -export type OnlinePluginCommentBean = { - id?: number; - createdAt?: number; - updatedAt?: number; - userId?: number; - score?: number; - comment?: string; -}; - export async function OnlinePluginList(body: { pluginType?: string; group?: string; keyword?: string }): Promise { return await request({ url: apiPrefix + "/online/list", @@ -164,37 +139,6 @@ export async function OnlinePluginInstall(body: { fullName: string; version?: st }); } -export async function OnlinePluginDetail(body: { pluginId?: number; fullName?: string; commentPageNo?: number; commentPageSize?: number }): Promise<{ - plugin: OnlinePluginBean; - versions: OnlinePluginVersionBean[]; - myScore?: number; - myComment?: string; - comments?: OnlinePluginCommentBean[]; - commentsTotal?: number; -}> { - return await request({ - url: apiPrefix + "/online/detail", - method: "post", - data: body, - }); -} - -export async function OnlinePluginSource(body: { pluginId?: number; fullName?: string; version?: string }): Promise<{ plugin: OnlinePluginBean; version: OnlinePluginVersionBean; content: string }> { - return await request({ - url: apiPrefix + "/online/source", - method: "post", - data: body, - }); -} - -export async function OnlinePluginRate(body: { pluginId?: number; fullName?: string; score: number; comment: string }): Promise<{ plugin: OnlinePluginBean; myScore?: number; myComment?: string }> { - return await request({ - url: apiPrefix + "/online/rate", - method: "post", - data: body, - }); -} - export async function OnlinePluginSubmitVersion(body: { fullName: string; version: string; content: string; minAppVersion?: string; maxAppVersion?: string }) { return await request({ url: apiPrefix + "/online/version/submit", @@ -301,14 +245,6 @@ export async function savePluginSetting(req: { name: string; sysSetting: any }): }); } -export async function DoTest(req: { id: number; input: any }): Promise { - return await request({ - url: apiPrefix + "/doTest", - method: "post", - data: req, - }); -} - export async function GetPluginByName(name: string): Promise { return await request({ url: apiPrefix + "/getPluginByName", diff --git a/packages/ui/certd-server/src/controller/sys/plugin/plugin-controller.ts b/packages/ui/certd-server/src/controller/sys/plugin/plugin-controller.ts index 0863a618b..3255ea252 100644 --- a/packages/ui/certd-server/src/controller/sys/plugin/plugin-controller.ts +++ b/packages/ui/certd-server/src/controller/sys/plugin/plugin-controller.ts @@ -3,13 +3,10 @@ import { merge } from "lodash-es"; import { CrudController } from "@certd/lib-server"; import { OnlinePluginAuthorAddReq, - OnlinePluginDetailReq, OnlinePluginInstallReq, OnlinePluginListReq, OnlinePluginPublishInfoReq, OnlinePluginPublishReq, - OnlinePluginRateReq, - OnlinePluginSourceReq, OnlinePluginVersionSubmitReq, PluginImportReq, PluginService, @@ -42,11 +39,6 @@ export class PluginController extends CrudController { return await super.page(body); } - @Post("/list", { description: "sys:settings:view" }) - async list(@Body(ALL) body: any) { - return super.list(body); - } - @Post("/add", { description: "sys:settings:edit", summary: "添加插件" }) async add(@Body(ALL) bean: any) { const def: any = { @@ -84,13 +76,6 @@ export class PluginController extends CrudController { return this.ok(res); } - @Post("/deleteByIds", { description: "sys:settings:edit", summary: "批量删除插件" }) - async deleteByIds(@Body("ids") ids: number[]) { - const res = await this.service.deleteByIds(ids); - await this.auditLog({ content: `批量删除了${ids.length}条插件` }); - return this.ok(res); - } - @Post("/setDisabled", { description: "sys:settings:edit", summary: "禁用/启用插件" }) async setDisabled(@Body(ALL) body: { id: number; name: string; type: string; disabled: boolean }) { await this.service.setDisabled(body); @@ -159,25 +144,6 @@ export class PluginController extends CrudController { return this.ok(res); } - @Post("/online/detail", { description: "sys:settings:view", summary: "查看在线插件详情" }) - async onlineDetail(@Body(ALL) body: OnlinePluginDetailReq) { - const res = await this.service.onlinePluginDetail(body); - return this.ok(res); - } - - @Post("/online/source", { description: "sys:settings:view", summary: "查看在线插件源代码" }) - async onlineSource(@Body(ALL) body: OnlinePluginSourceReq) { - const res = await this.service.onlinePluginSource(body); - return this.ok(res); - } - - @Post("/online/rate", { description: "sys:settings:edit", summary: "在线插件评分" }) - async onlineRate(@Body(ALL) body: OnlinePluginRateReq) { - const res = await this.service.rateOnlinePlugin(body); - await this.auditLog({ content: `给在线插件「${body.fullName || body.pluginId}」评分` }); - return this.ok(res); - } - @Post("/online/version/submit", { description: "sys:settings:edit", summary: "提交在线插件版本" }) async onlineVersionSubmit(@Body(ALL) body: OnlinePluginVersionSubmitReq) { const res = await this.service.submitOnlinePluginVersion(body); diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.test.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.test.ts index 30880fb66..9f93592ef 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.test.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.test.ts @@ -13,6 +13,7 @@ function createPluginRepositoryMock(options: RepoOptions = {}) { const state = { savedRows: [] as any[], updates: [] as any[], + findCalls: 0, queryRows: options.queryRows || options.marketRows || [], installedRows: options.installedRows || [], existingStoreRows: options.existingStoreRows || [], @@ -179,7 +180,11 @@ function createPluginRepositoryMock(options: RepoOptions = {}) { return record; }, async find(options?: any) { + state.findCalls += 1; if (options?.where?.type === "store") { + if (options.where.installed === true) { + return state.installedRows; + } return state.existingStoreRows; } return []; @@ -244,6 +249,15 @@ describe("PluginService online plugins", () => { latest: "1.0.1", title: "Deploy Demo", }, + { + type: "store", + fullName: "greper/DeployToOther", + author: "greper", + name: "DeployToOther", + pluginType: "deploy", + latest: "1.0.1", + title: "Deploy Other", + }, ], installedRows: [ { @@ -263,11 +277,13 @@ describe("PluginService online plugins", () => { const list = await service.onlinePluginList({}); - assert.equal(list.length, 1); + assert.equal(list.length, 2); assert.equal(list[0].installed, true); assert.equal(list[0].installedVersion, "1.0.0"); assert.equal(list[0].upgradeAvailable, true); assert.equal(list[0].localPluginId, 3); + assert.equal(list[1].installed, false); + assert.equal((service.repository as any).state.findCalls, 1); }); it("does not treat uninstalled market rows as installed plugins", async () => { @@ -675,7 +691,6 @@ describe("PluginService online plugins", () => { assert.equal(requestConfig.url, "/activation/plugin/publish"); assert.equal(requestConfig.method, "post"); assert.deepEqual(requestConfig.data, { - userId: 12, content: "name: custom-one\nversion: 1.2.3\n", version: "1.2.3", minAppVersion: "", @@ -719,15 +734,12 @@ describe("PluginService online plugins", () => { assert.deepEqual(requestConfigs[0], { url: "/activation/plugin/author/get", method: "post", - data: { - userId: 23, - }, + data: {}, }); assert.deepEqual(requestConfigs[1], { url: "/activation/plugin/author/add", method: "post", data: { - userId: 23, name: "developer", displayName: "Developer", avatar: "", diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts index 3c5e45749..f5b956d2b 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts @@ -38,15 +38,6 @@ export type OnlinePluginDetailReq = { commentPageSize?: number; }; -export type OnlinePluginSourceReq = OnlinePluginDetailReq & { - version?: string; -}; - -export type OnlinePluginRateReq = OnlinePluginDetailReq & { - score: number; - comment: string; -}; - export type OnlinePluginVersionSubmitReq = { fullName: string; version: string; @@ -356,7 +347,7 @@ export class PluginService extends BaseService { } // 初始化设置 - const settingPlugins = await this.repository.find({ + const settingPlugins = await this.find({ select: { id: true, name: true, @@ -459,7 +450,7 @@ export class PluginService extends BaseService { } if (id > 0) { //update - await this.repository.update({ id }, { disabled }); + await this.updateWhere({ id }, { disabled }); return; } @@ -484,7 +475,7 @@ export class PluginService extends BaseService { */ async add(param: any) { param.type = normalizePluginSourceType(param.type); - const old = await this.repository.findOne({ + const old = await this.findOne({ where: { name: param.name, author: param.author, @@ -609,61 +600,52 @@ export class PluginService extends BaseService { private async findOnlinePluginRecords(req: OnlinePluginListReq) { const query = req || {}; const keyword = (query.keyword || "").trim().toLowerCase(); - const builder = this.repository.createQueryBuilder("item"); - builder.where("item.type = :type", { + const listQuery: Partial = { type: "store", - }); + }; if (query.pluginType) { - builder.andWhere("item.pluginType = :pluginType", { - pluginType: query.pluginType, - }); + listQuery.pluginType = query.pluginType; } if (query.group) { - builder.andWhere("item.group = :group", { - group: query.group, - }); + listQuery.group = query.group; } - if (keyword) { - builder.andWhere( - new Brackets(qb => { - qb.where("LOWER(COALESCE(item.fullName, '')) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.author) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.name) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.title) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.desc) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.group) LIKE :keyword", { keyword: `%${keyword}%` }) - .orWhere("LOWER(item.pluginType) LIKE :keyword", { keyword: `%${keyword}%` }); - }) - ); - } - return await builder.orderBy("item.pluginType", "ASC").addOrderBy("item.group", "ASC").addOrderBy("item.title", "ASC").addOrderBy("item.fullName", "ASC").addOrderBy("item.id", "ASC").getMany(); + return await this.list({ + query: listQuery, + buildQuery: builder => { + if (keyword) { + builder.andWhere( + new Brackets(qb => { + qb.where("LOWER(COALESCE(main.fullName, '')) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.author) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.name) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.title) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.desc) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.group) LIKE :keyword", { keyword: `%${keyword}%` }) + .orWhere("LOWER(main.pluginType) LIKE :keyword", { keyword: `%${keyword}%` }); + }) + ); + } + builder.orderBy("main.pluginType", "ASC").addOrderBy("main.group", "ASC").addOrderBy("main.title", "ASC").addOrderBy("main.fullName", "ASC").addOrderBy("main.id", "ASC"); + }, + }); } - private async findInstalledOnlinePlugin(parsed: { author: string; name: string }, record: OnlinePluginBean) { + private findInstalledOnlinePlugin(installedPlugins: PluginEntity[], parsed: { author: string; name: string }, record: OnlinePluginBean) { const fullName = record.fullName || `${parsed.author}/${parsed.name}`; - const builder = this.repository - .createQueryBuilder("plugin") - .where("plugin.type = :type", { type: "store" }) - .andWhere("plugin.installed = :installed", { installed: true }) - .andWhere( - new Brackets(qb => { - qb.where("plugin.fullName = :fullName", { fullName }).orWhere("plugin.name = :fullName", { fullName }).orWhere("plugin.author = :author AND plugin.name = :name", { - author: parsed.author, - name: parsed.name, - }); - if (record.pluginType) { - qb.orWhere("plugin.pluginType = :pluginType AND plugin.name = :name", { - pluginType: record.pluginType, - name: parsed.name, - }); - } - }) - ); - return await builder.getOne(); + return installedPlugins.find(plugin => { + if (plugin.fullName === fullName || plugin.name === fullName) { + return true; + } + if (plugin.author === parsed.author && plugin.name === parsed.name) { + return true; + } + return !!record.pluginType && plugin.pluginType === record.pluginType && plugin.name === parsed.name; + }); } private async attachOnlineInstallState(list: OnlinePluginBean[]) { const records: OnlinePluginBean[] = []; + const parsedRecords = new Map(); let bindUserId = 0; if (this.sysSettingsService) { const installInfo = await this.sysSettingsService.getSetting(SysInstallInfo); @@ -686,14 +668,31 @@ export class PluginService extends BaseService { continue; } - const localPlugin = await this.findInstalledOnlinePlugin(parsed, record); + parsedRecords.set(record, parsed); + records.push(record); + } + + const installedPlugins = + parsedRecords.size > 0 + ? await this.find({ + where: { + type: "store", + installed: true, + }, + }) + : []; + for (const record of records) { + const parsed = parsedRecords.get(record); + if (!parsed) { + continue; + } + const localPlugin = this.findInstalledOnlinePlugin(installedPlugins, parsed, record); record.installed = !!localPlugin; record.localPluginId = localPlugin?.id; record.localDisabled = localPlugin?.disabled; record.installedVersion = localPlugin?.version; record.upgradeAvailable = localPlugin ? isOnlinePluginUpgradeAvailable(localPlugin.version, record.latest) : false; record.localEditable = canEditStorePlugin(localPlugin?.developerId ?? record.developerId, bindUserId); - records.push(record); } return records; } @@ -751,7 +750,7 @@ export class PluginService extends BaseService { } pageStart += ONLINE_PLUGIN_SYNC_PAGE_SIZE; } - const existingList = await this.repository.find({ + const existingList = await this.find({ where: { type: "store", }, @@ -767,7 +766,7 @@ export class PluginService extends BaseService { const records = Array.from(pluginMap.values()).map(item => { return this.normalizeOnlinePluginRecord(item, syncTime, existingMap.get(item.fullName)); }); - await this.repository.save(records); + await this.addOrUpdate(records); await this.saveOnlinePluginSyncTime(syncTime); return await this.onlinePluginList({}); } @@ -810,14 +809,12 @@ export class PluginService extends BaseService { async onlinePluginDetail(req: OnlinePluginDetailReq) { await this.plusService.register(); - const installInfo = await this.sysSettingsService.getSetting(SysInstallInfo); const res = await this.plusService.request({ url: "/activation/plugin/detail", method: "post", data: { pluginId: req.pluginId || 0, fullName: req.fullName || "", - userId: installInfo.bindUserId || 0, commentPageNo: req.commentPageNo || 1, commentPageSize: req.commentPageSize || 5, }, @@ -829,52 +826,6 @@ export class PluginService extends BaseService { return res; } - async onlinePluginSource(req: OnlinePluginSourceReq) { - await this.plusService.register(); - const installInfo = await this.sysSettingsService.getSetting(SysInstallInfo); - return await this.plusService.request({ - url: "/activation/plugin/source", - method: "post", - data: { - pluginId: req.pluginId || 0, - fullName: req.fullName || "", - version: req.version || "", - userId: installInfo.bindUserId || 0, - }, - }); - } - - async rateOnlinePlugin(req: OnlinePluginRateReq) { - const installInfo = await this.sysSettingsService.getSetting(SysInstallInfo); - if (!installInfo.bindUserId) { - throw new Error("请先绑定账号后再评分"); - } - await this.plusService.register(); - const res = await this.plusService.request({ - url: "/activation/plugin/rate", - method: "post", - data: { - pluginId: req.pluginId || 0, - fullName: req.fullName || "", - userId: installInfo.bindUserId, - score: req.score, - comment: req.comment, - }, - }); - if (res?.plugin?.score != null) { - await this.repository.update( - { - type: "store", - fullName: res.plugin.fullName || req.fullName, - }, - { - score: res.plugin.score, - } - ); - } - return res; - } - async submitOnlinePluginVersion(req: OnlinePluginVersionSubmitReq) { const installInfo = await this.sysSettingsService.getSetting(SysInstallInfo); if (!installInfo.bindUserId) { @@ -885,7 +836,6 @@ export class PluginService extends BaseService { url: "/activation/plugin/version/submit", method: "post", data: { - userId: installInfo.bindUserId, fullName: req.fullName, version: req.version, content: req.content, @@ -919,7 +869,6 @@ export class PluginService extends BaseService { url: "/activation/plugin/publish", method: "post", data: { - userId: installInfo.bindUserId, content, version: req.version || plugin.version || "", minAppVersion: req.minAppVersion || "", @@ -955,25 +904,22 @@ export class PluginService extends BaseService { } async getOnlinePluginAuthor(): Promise { - const bindUserId = await this.getBindUserId("发布插件"); + await this.getBindUserId("发布插件"); await this.plusService.register(); return await this.plusService.request({ url: "/activation/plugin/author/get", method: "post", - data: { - userId: bindUserId, - }, + data: {}, }); } async addOnlinePluginAuthor(req: OnlinePluginAuthorAddReq): Promise { - const bindUserId = await this.getBindUserId("注册插件作者"); + await this.getBindUserId("注册插件作者"); await this.plusService.register(); return await this.plusService.request({ url: "/activation/plugin/author/add", method: "post", data: { - userId: bindUserId, name: req.name, displayName: req.displayName || "", avatar: req.avatar || "", @@ -1042,7 +988,7 @@ export class PluginService extends BaseService { if (!fullName || downloadCount == null) { return; } - await this.repository.update( + await this.updateWhere( { type: "store", fullName, @@ -1092,7 +1038,7 @@ export class PluginService extends BaseService { async update(param: any) { param.type = normalizePluginSourceType(param.type); - const old = await this.repository.findOne({ + const old = await this.findOne({ where: { name: param.name, author: param.author, @@ -1303,27 +1249,29 @@ export class PluginService extends BaseService { const fullName = loaded.fullName || (loaded.author && loaded.name ? `${loaded.author}/${loaded.name}` : ""); const entityType = normalizePluginSourceType(req.type || loaded.type); - const old = - entityType === "store" - ? await this.repository.findOne({ - where: [ - { - type: "store", - fullName, - }, - { - type: "store", - author: loaded.author, - name: loaded.name, - }, - ], - }) - : await this.repository.findOne({ - where: { - name: loaded.name, - author: loaded.author, - }, - }); + let old: PluginEntity | null; + if (entityType === "store") { + old = await this.findOne({ + where: [ + { + type: "store", + fullName, + }, + { + type: "store", + author: loaded.author, + name: loaded.name, + }, + ], + }); + } else { + old = await this.findOne({ + where: { + name: loaded.name, + author: loaded.author, + }, + }); + } const metadata = { input: loaded.input, @@ -1381,7 +1329,7 @@ export class PluginService extends BaseService { } await this.unRegisterById(id); if (item.type === "store" && item.developerId) { - await this.repository.update( + await this.updateWhere( { id }, { installed: false,