From a090a1745cd430bd2101e8799510823d5f74c449 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Thu, 9 Oct 2025 18:55:49 +0530 Subject: [PATCH] review changes --- backend/src/server/routes/v1/admin-router.ts | 51 ++--------- backend/src/services/identity/identity-dal.ts | 14 ++- backend/src/services/org/org-dal.ts | 91 ++++++++----------- .../super-admin/super-admin-service.ts | 45 ++------- .../services/super-admin/super-admin-types.ts | 13 --- backend/src/services/user/user-dal.ts | 41 ++------- frontend/src/hooks/api/admin/queries.ts | 6 +- frontend/src/hooks/api/admin/types.ts | 10 +- 8 files changed, 87 insertions(+), 184 deletions(-) diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index eee4694e8..03dd8ed94 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -173,9 +173,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { id: true, superAdmin: true }).array(), - meta: z.object({ - total: z.number() - }) + total: z.number() }) } }, @@ -185,21 +183,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }); }, handler: async (req) => { - const users = await server.services.superAdmin.getUsers({ + const result = await server.services.superAdmin.getUsers({ ...req.query }); - const count = await server.services.superAdmin.countUsers({ - searchTerm: req.query.searchTerm, - adminsOnly: req.query.adminsOnly - }); - - return { - users, - meta: { - total: count - } - }; + return result; } }); @@ -242,9 +230,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }) .array() }).array(), - meta: z.object({ - total: z.number() - }) + total: z.number() }) } }, @@ -254,19 +240,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }); }, handler: async (req) => { - const organizations = await server.services.superAdmin.getOrganizations({ + const result = await server.services.superAdmin.getOrganizations({ ...req.query }); - const count = await server.services.superAdmin.countOrganizations({ - searchTerm: req.query.searchTerm - }); - return { - organizations, - meta: { - total: count - } - }; + return result; } }); @@ -358,9 +336,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { isInstanceAdmin: z.boolean() }) .array(), - meta: z.object({ - total: z.number() - }) + total: z.number() }) } }, @@ -370,20 +346,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }); }, handler: async (req) => { - const identities = await server.services.superAdmin.getIdentities({ + const result = await server.services.superAdmin.getIdentities({ ...req.query }); - const count = await server.services.superAdmin.countIdentities({ - searchTerm: req.query.searchTerm - }); - - return { - identities, - meta: { - total: count - } - }; + return result; } }); diff --git a/backend/src/services/identity/identity-dal.ts b/backend/src/services/identity/identity-dal.ts index c23e650d0..56224bd57 100644 --- a/backend/src/services/identity/identity-dal.ts +++ b/backend/src/services/identity/identity-dal.ts @@ -50,11 +50,23 @@ export const identityDALFactory = (db: TDbClient) => { }); } + const countQuery = query.clone(); + if (sortBy) { query = query.orderBy(sortBy); } - return await query.limit(limit).offset(offset).select(selectAllTableCols(TableName.Identity)); + const [identities, totalResult] = await Promise.all([ + query.limit(limit).offset(offset).select(selectAllTableCols(TableName.Identity)), + countQuery.countDistinct(`${TableName.Identity}.id`, { as: "count" }).first() + ]); + + const total = Number(totalResult?.count || 0); + + return { + identities, + total + }; } catch (error) { throw new DatabaseError({ error, name: "Get identities by filter" }); } diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index 6f91d2dc3..6ac363c76 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -43,8 +43,6 @@ export const orgDALFactory = (db: TDbClient) => { sortBy?: keyof TOrganizations; }) => { try { - const query = db.replicaNode()(TableName.Organization); - // Build the subquery for limited organization IDs const orgSubquery = db.replicaNode().select("id").from(TableName.Organization); @@ -54,37 +52,45 @@ export const orgDALFactory = (db: TDbClient) => { }); } + const countQuery = orgSubquery.clone(); + if (sortBy) { void orgSubquery.orderBy(sortBy); } - void orgSubquery.limit(limit).offset(offset); - // Main query with joins, limited to the subquery results - const docs = await query - .whereIn(`${TableName.Organization}.id`, orgSubquery) - .leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`) - .leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) - .leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) - .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) - .where((qb) => { - void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`); - }) - .select(selectAllTableCols(TableName.Organization)) - .select(db.ref("name").withSchema(TableName.Project).as("projectName")) - .select(db.ref("id").withSchema(TableName.Project).as("projectId")) - .select(db.ref("slug").withSchema(TableName.Project).as("projectSlug")) - .select(db.ref("createdAt").withSchema(TableName.Project).as("projectCreatedAt")) - .select(db.ref("email").withSchema(TableName.Users).as("userEmail")) - .select(db.ref("username").withSchema(TableName.Users).as("username")) - .select(db.ref("firstName").withSchema(TableName.Users).as("firstName")) - .select(db.ref("lastName").withSchema(TableName.Users).as("lastName")) - .select(db.ref("id").withSchema(TableName.Users).as("userId")) - .select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId")) - .select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole")) - .select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId")) - .select(db.ref("status").withSchema(TableName.OrgMembership).as("orgMembershipStatus")) - .select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName")); + const buildBaseQuery = (orgIdSubquery: Knex.QueryBuilder) => { + return db + .replicaNode()(TableName.Organization) + .whereIn(`${TableName.Organization}.id`, orgIdSubquery) + .leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`) + .leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) + .leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) + .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) + .where((qb) => { + void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`); + }); + }; + + const [docs, totalResult] = await Promise.all([ + buildBaseQuery(orgSubquery) + .select(selectAllTableCols(TableName.Organization)) + .select(db.ref("name").withSchema(TableName.Project).as("projectName")) + .select(db.ref("id").withSchema(TableName.Project).as("projectId")) + .select(db.ref("slug").withSchema(TableName.Project).as("projectSlug")) + .select(db.ref("createdAt").withSchema(TableName.Project).as("projectCreatedAt")) + .select(db.ref("email").withSchema(TableName.Users).as("userEmail")) + .select(db.ref("username").withSchema(TableName.Users).as("username")) + .select(db.ref("firstName").withSchema(TableName.Users).as("firstName")) + .select(db.ref("lastName").withSchema(TableName.Users).as("lastName")) + .select(db.ref("id").withSchema(TableName.Users).as("userId")) + .select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId")) + .select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole")) + .select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId")) + .select(db.ref("status").withSchema(TableName.OrgMembership).as("orgMembershipStatus")) + .select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName")), + buildBaseQuery(countQuery).countDistinct(`${TableName.Organization}.id`, { as: "count" }).first() + ]); const formattedDocs = sqlNestRelationships({ data: docs, @@ -132,33 +138,17 @@ export const orgDALFactory = (db: TDbClient) => { ] }); - return formattedDocs; + const total = Number(totalResult?.count || 0); + + return { + organizations: formattedDocs, + total + }; } catch (error) { throw new DatabaseError({ error, name: "Find organizations by filter" }); } }; - const countOrganizationsByFilter = async ({ searchTerm }: { searchTerm: string }) => { - interface CountResult { - count: string; - } - try { - const count = await db - .replicaNode()(TableName.Organization) - .where((qb) => { - if (searchTerm) { - void qb.whereILike(`${TableName.Organization}.name`, `%${searchTerm}%`); - } - }) - .count("*") - .first(); - - return parseInt((count as unknown as CountResult).count || "0", 10); - } catch (error) { - throw new DatabaseError({ error, name: "Count organizations by filter" }); - } - }; - const findOrgById = async (orgId: string) => { try { const org = (await db @@ -691,7 +681,6 @@ export const orgDALFactory = (db: TDbClient) => { findOrgBySlug, findAllOrgsByUserId, findOrganizationsByFilter, - countOrganizationsByFilter, ghostUserExists, findOrgMembersByUsername, findOrgMembersByRole, diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 6f26b40e9..8897a1b3f 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -55,9 +55,6 @@ import { TAdminGetUsersDTO, TAdminIntegrationConfig, TAdminSignUpDTO, - TCountIdentitiesDTO, - TCountOrganizationsDTO, - TCountUsersDTO, TCreateOrganizationDTO, TGetOrganizationsDTO, TResendOrgInviteDTO @@ -671,15 +668,6 @@ export const superAdminServiceFactory = ({ }); }; - const countUsers = async ({ searchTerm, adminsOnly }: TCountUsersDTO) => { - const count = await userDAL.countUsersByFilter({ - searchTerm, - adminsOnly - }); - - return count; - }; - const deleteUser = async (userId: string) => { const superAdmins = await userDAL.find({ superAdmin: true @@ -753,20 +741,12 @@ export const superAdminServiceFactory = ({ }; const getOrganizations = async ({ offset, limit, searchTerm }: TGetOrganizationsDTO) => { - const organizations = await orgDAL.findOrganizationsByFilter({ + return orgDAL.findOrganizationsByFilter({ offset, searchTerm, sortBy: "name", limit }); - return organizations; - }; - - const countOrganizations = async ({ searchTerm }: TCountOrganizationsDTO) => { - const count = await orgDAL.countOrganizationsByFilter({ - searchTerm - }); - return count; }; const createOrganization = async ( @@ -1034,7 +1014,7 @@ export const superAdminServiceFactory = ({ }; const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => { - const identities = await identityDAL.getIdentitiesByFilter({ + const result = await identityDAL.getIdentitiesByFilter({ limit, offset, searchTerm, @@ -1042,17 +1022,13 @@ export const superAdminServiceFactory = ({ }); const serverCfg = await getServerCfg(); - return identities.map((identity) => ({ - ...identity, - isInstanceAdmin: Boolean(serverCfg?.adminIdentityIds?.includes(identity.id)) - })); - }; - - const countIdentities = async ({ searchTerm }: TCountIdentitiesDTO) => { - const count = await identityDAL.countIdentitiesByFilter({ - searchTerm - }); - return count; + return { + identities: result.identities.map((identity) => ({ + ...identity, + isInstanceAdmin: Boolean(serverCfg?.adminIdentityIds?.includes(identity.id)) + })), + total: result.total + }; }; const grantServerAdminAccessToUser = async (userId: string) => { @@ -1161,10 +1137,8 @@ export const superAdminServiceFactory = ({ adminSignUp, bootstrapInstance, getUsers, - countUsers, deleteUser, getIdentities, - countIdentities, getAdminIntegrationsConfig, updateRootEncryptionStrategy, getConfiguredEncryptionStrategies, @@ -1174,7 +1148,6 @@ export const superAdminServiceFactory = ({ invalidateCache, checkIfInvalidatingCache, getOrganizations, - countOrganizations, deleteOrganization, deleteOrganizationMembership, initializeAdminIntegrationConfigSync, diff --git a/backend/src/services/super-admin/super-admin-types.ts b/backend/src/services/super-admin/super-admin-types.ts index 467b6d808..6cfdd384d 100644 --- a/backend/src/services/super-admin/super-admin-types.ts +++ b/backend/src/services/super-admin/super-admin-types.ts @@ -34,19 +34,6 @@ export type TGetOrganizationsDTO = { searchTerm: string; }; -export type TCountOrganizationsDTO = { - searchTerm: string; -}; - -export type TCountIdentitiesDTO = { - searchTerm: string; -}; - -export type TCountUsersDTO = { - searchTerm: string; - adminsOnly: boolean; -}; - export type TCreateOrganizationDTO = { name: string; inviteAdminEmails: string[]; diff --git a/backend/src/services/user/user-dal.ts b/backend/src/services/user/user-dal.ts index 80f9fa510..0da8a46a8 100644 --- a/backend/src/services/user/user-dal.ts +++ b/backend/src/services/user/user-dal.ts @@ -60,45 +60,25 @@ export const userDALFactory = (db: TDbClient) => { query = query.where("superAdmin", true); } + const countQuery = query.clone(); + if (sortBy) { query = query.orderBy(sortBy); } - return await query.limit(limit).offset(offset).select(selectAllTableCols(TableName.Users)); + const [users, totalResult] = await Promise.all([ + query.limit(limit).offset(offset).select(selectAllTableCols(TableName.Users)), + countQuery.count("*", { as: "count" }).first() + ]); + + const total = Number(totalResult?.count || 0); + + return { users, total }; } catch (error) { throw new DatabaseError({ error, name: "Get users by filter" }); } }; - const countUsersByFilter = async ({ searchTerm, adminsOnly }: { searchTerm: string; adminsOnly: boolean }) => { - interface CountResult { - count: string; - } - try { - const count = await db - .replicaNode()(TableName.Users) - .where("isGhost", "=", false) - .where((qb) => { - if (searchTerm) { - void qb - .whereILike("email", `%${searchTerm}%`) - .orWhereILike("firstName", `%${searchTerm}%`) - .orWhereILike("lastName", `%${searchTerm}%`) - .orWhereRaw('lower("username") like ?', `%${searchTerm}%`); - } - if (adminsOnly) { - void qb.where("superAdmin", true); - } - }) - .count("*") - .first(); - - return parseInt((count as unknown as CountResult).count || "0", 10); - } catch (error) { - throw new DatabaseError({ error, name: "Count Users by filter" }); - } - }; - // USER ENCRYPTION FUNCTIONS // ------------------------- const findUserEncKeyByUsername = async ({ username }: { username: string }) => { @@ -272,7 +252,6 @@ export const userDALFactory = (db: TDbClient) => { findOneUserAction, createUserAction, getUsersByFilter, - countUsersByFilter, findAllMyAccounts, findUserByEmail }; diff --git a/frontend/src/hooks/api/admin/queries.ts b/frontend/src/hooks/api/admin/queries.ts index e52931d73..ea7226ad6 100644 --- a/frontend/src/hooks/api/admin/queries.ts +++ b/frontend/src/hooks/api/admin/queries.ts @@ -54,7 +54,7 @@ export const useAdminGetOrganizations = (filters: AdminGetOrganizationsFilters) } ); - return { organizations: data.organizations, totalCount: data.meta.total }; + return { organizations: data.organizations, totalCount: data.total }; }, placeholderData: (previousData) => previousData }); @@ -93,7 +93,7 @@ export const useAdminGetUsers = (filters: AdminGetUsersFilters) => { } ); - return { users: data.users, totalCount: data.meta.total }; + return { users: data.users, totalCount: data.total }; }, placeholderData: (previousData) => previousData }); @@ -112,7 +112,7 @@ export const useAdminGetIdentities = (filters: AdminGetIdentitiesFilters) => { } ); - return { identities: data.identities, totalCount: data.meta.total }; + return { identities: data.identities, totalCount: data.total }; }, placeholderData: (previousData) => previousData }); diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index 1a9a65994..ca27c0f23 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -35,23 +35,19 @@ export type OrganizationWithProjects = Organization & { }[]; }; -type PaginatedDataMeta = { - total: number; -}; - export type TGetOrganizationsResponse = { organizations: OrganizationWithProjects[]; - meta: PaginatedDataMeta; + total: number; }; export type TGetIdentitiesResponse = { identities: Identity[]; - meta: PaginatedDataMeta; + total: number; }; export type TGetUsersResponse = { users: User[]; - meta: PaginatedDataMeta; + total: number; }; export type TServerConfig = {