import { CorporateUser, Type, User } from "@/interfaces/user"; import { getGroupsForUser, getParticipantGroups, getUserGroups, getUsersGroups } from "./groups.be"; import { uniq } from "lodash"; import { getUserCodes } from "./codes.be"; import client from "@/lib/mongodb"; import { EntityWithRoles, WithEntities } from "@/interfaces/entity"; import { getEntity } from "./entities.be"; import { getRole } from "./roles.be"; import { groupAllowedEntitiesByPermissions } from "./permissions"; import { mapBy } from "."; const db = client.db(process.env.MONGODB_DB); export async function getUsers(filter?: object, limit = 0, sort = {}, projection = {}) { return await db .collection("users") .find(filter || {}, { projection: { _id: 0, ...projection } }) .limit(limit) .sort(sort) .toArray(); } export async function searchUsers(searchInput?: string, limit = 50, page = 0, sort: object = { "name": 1 }, projection = {}, filter?: object) { const compoundFilter = { "compound": { "should": [...(searchInput != "" ? [{ "autocomplete": { "query": searchInput, "path": "name", } }, { "autocomplete": { "query": searchInput, "path": "email", } }] : [{ exists: { path: "name" } }])], "minimumShouldMatch": 1, } } const [{ users, totalUsers }] = await db .collection("users").aggregate([ { "$search": { "index": "UsersByNameEmail", ...compoundFilter } }, ...(filter ? [{ "$match": Object.entries(filter).reduce((accm, [key, value]) => { if (value.length === 0) return accm accm[key] = Array.isArray(value) ? { $in: value } : value return accm; }, {} as any) }] : []), { $facet: { totalUsers: [{ $count: "count" }], users: [ { $sort: sort }, { $skip: limit * page }, { $limit: limit }, { "$project": { _id: 0, label: { $concat: ["$name", " - ", "$email"] }, value: "$id", ...projection } } ] } } ]).toArray(); return { users, total: totalUsers[0]?.count || 0 }; } export async function countUsers(filter?: object) { return await db .collection("users") .countDocuments(filter || {}) } export async function countUsersByTypes(types: Type[]) { return await db .collection("users") .aggregate([ { $match: { type: { $in: types } // Filter only specified types } }, { $group: { _id: "$type", count: { $sum: 1 } // Count documents in each group } }, { $group: { _id: null, counts: { $push: { k: "$_id", v: "$count" } // Convert to key-value pairs } } }, { $project: { _id: 0, result: { $arrayToObject: "$counts" } // Convert key-value pairs to an object } } ]).toArray().then(([{ result }]) => result); } export async function getUserWithEntity(id: string): Promise | undefined> { const user = await db.collection("users").findOne({ id: id }, { projection: { _id: 0 } }); if (!user) return undefined; const entities = await Promise.all( user.entities.map(async (e) => { const entity = await getEntity(e.id); const role = await getRole(e.role); return { entity, role }; }), ); return { ...user, entities }; } export async function getUser(id: string, projection = {}): Promise { const user = await db.collection("users").findOne({ id: id }, { projection: { _id: 0, ...projection } }); return !!user ? user : undefined; } export async function getSpecificUsers(ids: string[], projection = {}) { if (ids.length === 0) return []; return await db .collection("users") .find({ id: { $in: ids } }, { projection: { _id: 0, ...projection } }) .toArray(); } export async function getEntityUsers(id: string, limit?: number, filter?: object, projection = {}) { return await db .collection("users") .find({ "entities.id": id, ...(filter || {}) }, { projection: { _id: 0, ...projection } }) .limit(limit || 0) .toArray(); } export async function countEntityUsers(id: string, filter?: object) { return await db.collection("users").countDocuments({ "entities.id": id, ...(filter || {}) }); } export async function getEntitiesUsers(ids: string[], filter?: object, limit?: number, projection = {}) { return await db .collection("users") .find({ "entities.id": { $in: ids }, ...(filter || {}) }, { projection }) .limit(limit || 0) .toArray(); } export async function countEntitiesUsers(ids: string[], filter?: object) { return await db.collection("users").countDocuments({ "entities.id": { $in: ids }, ...(filter || {}) }); } export async function getLinkedUsers( userID?: string, userType?: Type, type?: Type, page?: number, size?: number, sort?: string, direction?: "asc" | "desc", ) { const filters = { ...(!!type ? { type } : {}), }; if (!userID || userType === "admin" || userType === "developer") { const users = await db .collection("users") .find(filters) .sort(sort ? { [sort]: direction === "desc" ? -1 : 1 } : {}) .skip(page && size ? page * size : 0) .limit(size || 0) .toArray(); const total = await db.collection("users").countDocuments(filters); return { users, total }; } const adminGroups = await getUserGroups(userID); const groups = await getUsersGroups(adminGroups.flatMap((x) => x.participants)); const belongingGroups = await getParticipantGroups(userID); const participants = uniq([ ...adminGroups.flatMap((x) => x.participants), ...(userType === "mastercorporate" ? groups.flat().flatMap((x) => x.participants) : []), ...(userType === "teacher" ? belongingGroups.flatMap((x) => x.participants) : []), ]); // тип [FirebaseError: Invalid Query. A non-empty array is required for 'in' filters.] { if (participants.length === 0) return { users: [], total: 0 }; const users = await db .collection("users") .find({ ...filters, id: { $in: participants } }) .skip(page && size ? page * size : 0) .limit(size || 0) .toArray(); const total = await db.collection("users").countDocuments({ ...filters, id: { $in: participants } }); return { users, total }; } export async function getUserBalance(user: User) { const codes = await getUserCodes(user.id); if (user.type !== "corporate" && user.type !== "mastercorporate") return codes.length; const groups = await getGroupsForUser(user.id); const participants = uniq(groups.flatMap((x) => x.participants)); if (user.type === "corporate") return participants.length + codes.filter((x) => !participants.includes(x.userId || "")).length; const participantUsers = await Promise.all(participants.map(getUser)); const corporateUsers = participantUsers.filter((x) => x?.type === "corporate") as CorporateUser[]; return ( corporateUsers.reduce((acc, curr) => acc + 0, 0) + corporateUsers.length + codes.filter((x) => !participants.includes(x.userId || "") && !corporateUsers.map((u) => u.id).includes(x.userId || "")).length ); } export const filterAllowedUsers = async (user: User, entities: EntityWithRoles[], projection = {}) => { const { ["view_students"]: allowedStudentEntities, ["view_teachers"]: allowedTeacherEntities, ["view_corporates"]: allowedCorporateEntities, ["view_mastercorporates"]: allowedMasterCorporateEntities, } = groupAllowedEntitiesByPermissions(user, entities, [ "view_students", "view_teachers", 'view_corporates', 'view_mastercorporates', ]); const [students, teachers, corporates, masterCorporates] = await Promise.all([ getEntitiesUsers(mapBy(allowedStudentEntities, 'id'), { type: "student" }, 0, projection), getEntitiesUsers(mapBy(allowedTeacherEntities, 'id'), { type: "teacher" }, 0, projection), getEntitiesUsers(mapBy(allowedCorporateEntities, 'id'), { type: "corporate" }, 0, projection), getEntitiesUsers(mapBy(allowedMasterCorporateEntities, 'id'), { type: "mastercorporate" }, 0, projection), ]) return [...students, ...teachers, ...corporates, ...masterCorporates] } export const countAllowedUsers = async (user: User, entities: EntityWithRoles[]) => { const { ["view_students"]: allowedStudentEntities, ["view_teachers"]: allowedTeacherEntities, ["view_corporates"]: allowedCorporateEntities, ["view_mastercorporates"]: allowedMasterCorporateEntities, } = groupAllowedEntitiesByPermissions(user, entities, [ "view_students", "view_teachers", 'view_corporates', 'view_mastercorporates', ]); console.log(mapBy(allowedStudentEntities, 'id')) const [student, teacher, corporate, mastercorporate] = await Promise.all([ countEntitiesUsers(mapBy(allowedStudentEntities, 'id'), { type: "student" }), countEntitiesUsers(mapBy(allowedTeacherEntities, 'id'), { type: "teacher" }), countEntitiesUsers(mapBy(allowedCorporateEntities, 'id'), { type: "corporate" }), countEntitiesUsers(mapBy(allowedMasterCorporateEntities, 'id'), { type: "mastercorporate" }), ]) console.log(student) return { student, teacher, corporate, mastercorporate } }