From 929fc6956ecb0670b6236e78a41dae6b50432b33 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Tue, 20 Jan 2026 23:42:24 -0800 Subject: [PATCH] [PUT-456] fix: delete apps from marketplace when deleting anywhere else (#2310) --- extensions/api.d.ts | 4 ++++ .../src/modules/apps/AppInformationService.js | 5 +++++ .../src/modules/data-access/AppService.js | 8 +++++++- .../src/modules/selfhosted/SelfhostedService.js | 16 ++++++++++++---- src/backend/src/om/entitystorage/AppES.js | 12 ++++++++++-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/extensions/api.d.ts b/extensions/api.d.ts index 2f2fc0542..0df607ece 100644 --- a/extensions/api.d.ts +++ b/extensions/api.d.ts @@ -138,6 +138,10 @@ export interface ExtensionEventTypeMap { [key: string]: unknown; }; }; + 'app.changed': { + app_uid: string; + action: 'updated' | 'deleted'; + }; } interface Extension extends RouterMethods { diff --git a/src/backend/src/modules/apps/AppInformationService.js b/src/backend/src/modules/apps/AppInformationService.js index 2d9a86726..2ada3bf8b 100644 --- a/src/backend/src/modules/apps/AppInformationService.js +++ b/src/backend/src/modules/apps/AppInformationService.js @@ -696,6 +696,11 @@ class AppInformationService extends BaseService { } } + const svc_event = this.services.get('event'); + svc_event.emit('app.changed', { + app_uid: app.uid, + action: 'deleted', + }); } // Helper function to generate array of all periods between start and end dates diff --git a/src/backend/src/modules/data-access/AppService.js b/src/backend/src/modules/data-access/AppService.js index c1093639b..be1b38473 100644 --- a/src/backend/src/modules/data-access/AppService.js +++ b/src/backend/src/modules/data-access/AppService.js @@ -88,7 +88,7 @@ export default class AppService extends BaseService { // value of require('om/mappings/app.js').redundant_identifiers static REDUNDANT_IDENTIFIERS = ['name']; - async #select ({ predicate, params, ...rest }) { + async #select ({ predicate, params, ..._rest }) { const db = this.db; if ( predicate === undefined ) predicate = []; @@ -694,6 +694,12 @@ export default class AppService extends BaseService { const merged_app = { ...old_app, ...object }; this.#refresh_cache(merged_app, old_app); + const svc_event = this.services.get('event'); + svc_event.emit('app.changed', { + app_uid: old_app.uid, + action: 'updated', + }); + // Return the updated app (re-fetch for client-safe output) // TODO: optimize this return await this.#read({ uid: old_app.uid }); diff --git a/src/backend/src/modules/selfhosted/SelfhostedService.js b/src/backend/src/modules/selfhosted/SelfhostedService.js index 9c079bb29..1d71db8e6 100644 --- a/src/backend/src/modules/selfhosted/SelfhostedService.js +++ b/src/backend/src/modules/selfhosted/SelfhostedService.js @@ -16,10 +16,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { Actor } = require('../../services/auth/Actor'); const BaseService = require('../../services/BaseService'); const { DB_WRITE } = require('../../services/database/consts'); -const { Context } = require('../../util/context'); class SelfhostedService extends BaseService { static description = ` @@ -36,7 +34,7 @@ class SelfhostedService extends BaseService { { id: 'godmode-on', description: 'Toggle godmode for an app', - handler: async (args, log) => { + handler: async (args, _log) => { const svc_su = this.services.get('su'); await await svc_su.sudo(async () => { const [app_uid] = args; @@ -46,6 +44,11 @@ class SelfhostedService extends BaseService { throw new Error(`App ${app_uid} not found`); } await db.write('UPDATE apps SET godmode = 1 WHERE uid = ?', [app_uid]); + const svc_event = this.services.get('event'); + svc_event.emit('app.changed', { + app_uid, + action: 'updated', + }); }); }, }, @@ -54,7 +57,7 @@ class SelfhostedService extends BaseService { { id: 'godmode-off', description: 'Toggle godmode for an app', - handler: async (args, log) => { + handler: async (args, _log) => { const svc_su = this.services.get('su'); await await svc_su.sudo(async () => { const [app_uid] = args; @@ -64,6 +67,11 @@ class SelfhostedService extends BaseService { throw new Error(`App ${app_uid} not found`); } await db.write('UPDATE apps SET godmode = 0 WHERE uid = ?', [app_uid]); + const svc_event = this.services.get('event'); + svc_event.emit('app.changed', { + app_uid, + action: 'updated', + }); }); }, }, diff --git a/src/backend/src/om/entitystorage/AppES.js b/src/backend/src/om/entitystorage/AppES.js index 93ce82285..5bcfbae9a 100644 --- a/src/backend/src/om/entitystorage/AppES.js +++ b/src/backend/src/om/entitystorage/AppES.js @@ -56,7 +56,7 @@ class AppES extends BaseES { }); } }, - async delete (uid, extra) { + async delete (uid, _extra) { const svc_appInformation = this.context.get('services').get('app-information'); await svc_appInformation.delete_app(uid); }, @@ -227,6 +227,14 @@ class AppES extends BaseES { refresh_apps_cache({ uid: raw_app.uuid }, raw_app); } + if ( extra.old_entity ) { + const svc_event = this.context.get('services').get('event'); + svc_event.emit('app.changed', { + app_uid: await full_entity.get('uid'), + action: 'updated', + }); + } + return result; }, async retry_predicate_rewrite ({ predicate }) { @@ -394,7 +402,7 @@ class AppES extends BaseES { const svc_puterSite = this.context.get('services').get('puter-site'); const site = await svc_puterSite.get_subdomain(subdomain, { is_custom_domain: false }); - if ( ! site || site.user_id !== user.id ) { + if ( !site || site.user_id !== user.id ) { throw APIError.create('subdomain_not_owned', null, { subdomain }); } },