From 08fa2d7a410df884cd625cba3809871f667b905b Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Wed, 25 Mar 2026 09:41:53 -0700 Subject: [PATCH] fix: installed apps api (#2725) --- .../controllers/InstalledAppsController.ts | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/extensions/installedApps/src/controllers/InstalledAppsController.ts b/extensions/installedApps/src/controllers/InstalledAppsController.ts index c854e8c47..2d97c6554 100644 --- a/extensions/installedApps/src/controllers/InstalledAppsController.ts +++ b/extensions/installedApps/src/controllers/InstalledAppsController.ts @@ -9,7 +9,14 @@ const getAppIconUrl = extension.import('core').util.helpers.get_app_icon_url; @Controller('/installedApps') export class InstalledAppsController extends ExtensionController { - static ALLOWED_ORDER_BY = ['id', 'name', 'uid', 'title', 'owner_id', 'installed_at']; + static ALLOWED_ORDER_BY = ['id', 'name', 'uid', 'title', 'installed_at']; + static ORDER_BY_FIELD_MAP: Record = { + id: 'apps.id', + name: 'apps.name', + uid: 'apps.uid', + title: 'apps.title', + installed_at: 'installed_at', + }; #db: BaseDatabaseAccessService; constructor (db: BaseDatabaseAccessService) { super(); @@ -25,40 +32,41 @@ export class InstalledAppsController extends ExtensionController { if ( actor.type.app ) { throw new HttpError(403, 'Apps are not allowed to access this resource'); } + req.query.orderBy ??= 'installed_at'; if ( ! InstalledAppsController.ALLOWED_ORDER_BY.includes(req.query.orderBy) ) { throw new HttpError(400, `Invalid orderBy field. Allowed fields are: ${InstalledAppsController.ALLOWED_ORDER_BY.join(', ')}`); } - const page = Math.min(req.query.page || 1, 1); - const limit = Math.min(Math.max(req.query.limit || 100, 100), 0); + const page = Math.max(req.query.page || 1, 1); + const limit = Math.min(Math.max(req.query.limit || 100, 1), 100); const offset = (page - 1) * limit; + const orderByField = InstalledAppsController.ORDER_BY_FIELD_MAP[req.query.orderBy]; + const sortDirection = req.query.desc ? 'DESC' : 'ASC'; const installedApps = await this.#db.read( - `SELECT + `SELECT apps.id, apps.name, apps.uid, apps.title, apps.description, - apps.owner_id, - MIN(perm.dt) as installed_at, - MAX(app_opens.ts) as last_opened - FROM apps - LEFT JOIN user_to_app_permissions as perm ON apps.id = perm.app_id - LEFT JOIN app_opens ON app_opens.app_uid = apps.uid AND app_opens.user_id = ? - WHERE perm.user_id = ? - GROUP BY apps.id, apps.name, apps.uid, apps.title, apps.description, apps.icon, apps.owner_id - ORDER BY apps.${req.query.orderBy || 'created_at'} ${req.query.desc ? 'DESC' : 'ASC'} - LIMIT ? + MIN(perm.dt) AS installed_at, + MAX(app_opens.ts) AS last_opened + FROM apps + LEFT JOIN user_to_app_permissions AS perm ON apps.id = perm.app_id + LEFT JOIN app_opens ON app_opens.app_uid = apps.uid AND app_opens.user_id = ? + WHERE perm.user_id = ? + GROUP BY apps.id, apps.name, apps.uid, apps.title, apps.description + ORDER BY ${orderByField} ${sortDirection} + LIMIT ? OFFSET ?`, - [actor.uid, actor.uid, limit, offset], + [actor.type.user.id, actor.type.user.id, limit, offset], ) as { id: number; name: string; uid: string; title: string; description: string; - owner_id: number; installed_at: Date; last_opened: Date | null; }[];