diff --git a/src/backend/src/om/entitystorage/AppES.js b/src/backend/src/om/entitystorage/AppES.js index 5689190ca..446322345 100644 --- a/src/backend/src/om/entitystorage/AppES.js +++ b/src/backend/src/om/entitystorage/AppES.js @@ -17,7 +17,7 @@ * along with this program. If not, see . */ const APIError = require("../../api/APIError"); -const { app_name_exists, get_user, refresh_apps_cache } = require("../../helpers"); +const { app_name_exists, refresh_apps_cache } = require("../../helpers"); const { AppUnderUserActorType } = require("../../services/auth/Actor"); const { DB_WRITE } = require("../../services/database/consts"); @@ -35,6 +35,13 @@ class AppES extends BaseES { const services = this.context.get('services'); this.db = services.get('database').get(DB_WRITE, 'apps'); }, + + /** + * Creates query predicates for filtering apps + * @param {string} id - Predicate identifier + * @param {...any} args - Additional arguments for predicate creation + * @returns {Promise} Query predicate object + */ async create_predicate (id, ...args) { if ( id === 'user-can-edit' ) { return new Eq({ @@ -53,6 +60,12 @@ class AppES extends BaseES { const svc_appInformation = this.context.get('services').get('app-information'); await svc_appInformation.delete_app(uid); }, + + /** + * Filters app selection based on user permissions and visibility settings + * @param {Object} options - Selection options including predicates + * @returns {Promise} Filtered selection results + */ async select (options) { const actor = Context.get('actor'); const user = actor.type.user; @@ -85,6 +98,13 @@ class AppES extends BaseES { return await this.upstream.select(options); }, + + /** + * Creates or updates an application with proper name handling and associations + * @param {Object} entity - Application entity to upsert + * @param {Object} extra - Additional upsert parameters + * @returns {Promise} Upsert operation results + */ async upsert (entity, extra) { if ( await app_name_exists(await entity.get('name')) ) { const { old_entity } = extra; @@ -242,6 +262,11 @@ class AppES extends BaseES { }; return await recurse(predicate); }, + + /** + * Transforms app data before reading by adding associations and handling permissions + * @param {Object} entity - App entity to transform + */ async read_transform (entity) { // Add file associations const rows = await this.db.read( @@ -264,6 +289,7 @@ class AppES extends BaseES { ? origin : null ; })()); + // Check if the user is the owner const is_owner = await (async () => { let owner = await entity.get('owner'); @@ -277,6 +303,7 @@ class AppES extends BaseES { return actor.type.user.id === owner.id; })(); + // Remove fields that are not allowed for non-owners if ( ! is_owner ) { entity.del('approved_for_listing'); entity.del('approved_for_opening_items'); @@ -304,6 +331,13 @@ class AppES extends BaseES { } } }, + + /** + * Creates a subdomain entry for the app if required + * @param {Object} entity - App entity + * @returns {Promise} Subdomain ID if created + * @private + */ async maybe_insert_subdomain_ (entity) { // Create and update is a situation where we might create a subdomain diff --git a/src/backend/src/services/AppInformationService.js b/src/backend/src/services/AppInformationService.js index 9bbaf95cd..f5724be5c 100644 --- a/src/backend/src/services/AppInformationService.js +++ b/src/backend/src/services/AppInformationService.js @@ -136,20 +136,39 @@ class AppInformationService { const key_open_count = `apps:open_count:uid:${app_uid}`; let open_count = kv.get(key_open_count); if ( ! open_count ) { - open_count = (await db.read( - `SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = ?`, - [app_uid] - ))[0].open_count; + if(global.clickhouseClient) { + const result = await global.clickhouseClient.query({ + query: `SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = '${app_uid}'`, + format: 'JSONEachRow' + }); + const rows = await result.json(); + open_count = rows[0].open_count; + }else{ + open_count = (await db.read( + `SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = ?`, + [app_uid] + ))[0].open_count; + } } // TODO: cache const key_user_count = `apps:user_count:uid:${app_uid}`; let user_count = kv.get(key_user_count); if ( ! user_count ) { - user_count = (await db.read( - `SELECT COUNT(DISTINCT user_id) AS user_count FROM app_opens WHERE app_uid = ?`, - [app_uid] - ))[0].user_count; + if(global.clickhouseClient) { + const result = await global.clickhouseClient.query({ + query: `SELECT COUNT(DISTINCT user_id) AS uniqueUsers FROM app_opens WHERE app_uid = '${app_uid}'`, + format: 'JSONEachRow' + }); + const rows = await result.json(); + user_count = rows[0].uniqueUsers; + }else{ + user_count = (await db.read( + `SELECT COUNT(DISTINCT user_id) AS user_count FROM app_opens WHERE app_uid = ?`, + [app_uid] + ))[0].user_count; + } + } const key_referral_count = `apps:referral_count:uid:${app_uid}`;