344 lines
8.5 KiB
TypeScript
344 lines
8.5 KiB
TypeScript
|
|
import { sessionOptions } from '@/lib/session';
|
|
import { withIronSessionApiRoute } from 'iron-session/next';
|
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
import client from "@/lib/mongodb";
|
|
|
|
const db = client.db(process.env.MONGODB_DB);
|
|
|
|
export default withIronSessionApiRoute(handler, sessionOptions);
|
|
|
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
const { op } = req.query
|
|
|
|
if (req.method === 'GET') {
|
|
switch (op) {
|
|
default:
|
|
res.status(400).json({ error: 'Invalid operation!' })
|
|
}
|
|
}
|
|
else if (req.method === 'POST') {
|
|
switch (op) {
|
|
case 'crossRefEmails':
|
|
res.status(200).json(await crossRefEmails(req.body.emails));
|
|
break;
|
|
case 'dontExist':
|
|
res.status(200).json(await dontExist(req.body.emails))
|
|
break;
|
|
case 'entityCheck':
|
|
res.status(200).json(await entityCheck(req.body));
|
|
break;
|
|
case 'crossRefClassrooms':
|
|
res.status(200).json(await crossRefClassrooms(req.body));
|
|
break;
|
|
case 'getIds':
|
|
res.status(200).json(await getIds(req.body.emails));
|
|
break;
|
|
case 'assignToEntity':
|
|
await assignToEntity(req.body);
|
|
res.status(200).json({"ok": true});
|
|
break;
|
|
case 'getEntities':
|
|
res.status(200).json(await getEntities(req.body.emails))
|
|
break;
|
|
default:
|
|
res.status(400).json({ error: 'Invalid operation!' })
|
|
}
|
|
} else {
|
|
res.status(400).end(`Method ${req.method} Not Allowed`)
|
|
}
|
|
}
|
|
|
|
async function crossRefEmails(emails: string[]) {
|
|
return await db.collection("users").aggregate([
|
|
{
|
|
$match: {
|
|
email: { $in: emails }
|
|
}
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
email: 1
|
|
}
|
|
}
|
|
]).toArray();
|
|
}
|
|
|
|
async function dontExist(emails: string[]): Promise<string[]> {
|
|
const existingUsers = await db.collection('users')
|
|
.find(
|
|
{ email: { $in: emails } },
|
|
{ projection: { _id: 0, email: 1 } }
|
|
)
|
|
.toArray();
|
|
|
|
const existingEmails = new Set(existingUsers.map(u => u.email));
|
|
return emails.filter(email => !existingEmails.has(email));
|
|
}
|
|
|
|
|
|
async function entityCheck(body: Record<string, any>): Promise<Array<{
|
|
email: string;
|
|
names?: string[];
|
|
}>> {
|
|
const { entities, emails } = body;
|
|
|
|
const pipeline = [
|
|
// Match users with the provided emails
|
|
{
|
|
$match: {
|
|
email: { $in: emails }
|
|
}
|
|
},
|
|
// Match users who don't have any of the entities
|
|
{
|
|
$match: {
|
|
$or: [
|
|
// Either they have no entities array
|
|
{ entities: { $exists: false } },
|
|
// Or their entities array is empty
|
|
{ entities: { $size: 0 } },
|
|
// Or none of their entities match the provided IDs
|
|
{
|
|
entities: {
|
|
$not: {
|
|
$elemMatch: {
|
|
id: { $in: entities.map((e: any) => e.id) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
// Unwind the entities array (if it exists)
|
|
{
|
|
$unwind: {
|
|
path: "$entities",
|
|
preserveNullAndEmptyArrays: true
|
|
}
|
|
},
|
|
// Lookup entity details from entities collection
|
|
{
|
|
$lookup: {
|
|
from: 'entities',
|
|
localField: 'entities.id',
|
|
foreignField: 'id',
|
|
as: 'entityDetails'
|
|
}
|
|
},
|
|
// Unwind the entityDetails array
|
|
{
|
|
$unwind: {
|
|
path: "$entityDetails",
|
|
preserveNullAndEmptyArrays: true
|
|
}
|
|
},
|
|
// Group by email to collect all entity names in an array
|
|
{
|
|
$group: {
|
|
_id: "$email",
|
|
email: { $first: "$email" },
|
|
name: {
|
|
$push: {
|
|
$cond: [
|
|
{ $ifNull: ["$entityDetails.label", false] },
|
|
"$entityDetails.label",
|
|
"$$REMOVE"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
// Final projection to clean up
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
email: 1,
|
|
name: 1
|
|
}
|
|
}
|
|
];
|
|
|
|
const results = await db.collection('users').aggregate(pipeline).toArray();
|
|
return results.map(result => ({
|
|
email: result.email as string,
|
|
names: result.name as string[] | undefined
|
|
}));
|
|
}
|
|
|
|
async function crossRefClassrooms(body: any) {
|
|
const { sets, entity } = body as { sets: { email: string, classroom: string }[], entity: string };
|
|
|
|
const pipeline = [
|
|
// Match users with the provided emails
|
|
{
|
|
$match: {
|
|
email: { $in: sets.map(set => set.email) }
|
|
}
|
|
},
|
|
// Lookup groups that contain the user's ID in participants
|
|
{
|
|
$lookup: {
|
|
from: 'groups',
|
|
let: { userId: '$id', userEmail: '$email' },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: {
|
|
$and: [
|
|
{ $in: ['$$userId', '$participants'] },
|
|
{
|
|
$let: {
|
|
vars: {
|
|
matchingSet: {
|
|
$arrayElemAt: [
|
|
{
|
|
$filter: {
|
|
input: sets,
|
|
cond: { $eq: ['$$this.email', '$$userEmail'] }
|
|
}
|
|
},
|
|
0
|
|
]
|
|
}
|
|
},
|
|
in: { $eq: ['$name', '$$matchingSet.classroom'] }
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
// Lookup admin's entities
|
|
{
|
|
$lookup: {
|
|
from: 'users',
|
|
let: { adminId: '$admin' },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: { $eq: ['$id', '$$adminId'] }
|
|
}
|
|
}
|
|
],
|
|
as: 'adminInfo'
|
|
}
|
|
},
|
|
// Filter where admin has the target entity
|
|
{
|
|
$match: {
|
|
$expr: {
|
|
$in: [
|
|
entity,
|
|
{
|
|
$map: {
|
|
input: { $arrayElemAt: ['$adminInfo.entities', 0] },
|
|
as: 'entityObj',
|
|
in: '$$entityObj.id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
as: 'matchingGroups'
|
|
}
|
|
},
|
|
// Only keep users who have matching groups
|
|
{
|
|
$match: {
|
|
matchingGroups: { $ne: [] }
|
|
}
|
|
},
|
|
// Project only the email
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
email: 1
|
|
}
|
|
}
|
|
];
|
|
|
|
const results = await db.collection('users').aggregate(pipeline).toArray();
|
|
return results.map((result: any) => result.email);
|
|
}
|
|
|
|
|
|
async function getIds(emails: string[]): Promise<Array<{ email: string; id: string }>> {
|
|
const users = await db.collection('users')
|
|
.find({ email: { $in: emails } })
|
|
.project({ email: 1, id: 1, _id: 0 })
|
|
.toArray();
|
|
|
|
return users.map(user => ({
|
|
email: user.email,
|
|
id: user.id
|
|
}));
|
|
}
|
|
|
|
async function getEntities(emails: string[]): Promise<Array<{ email: string; entityLabels: string[] }>> {
|
|
const users = await db.collection('users')
|
|
.find({ email: { $in: emails } })
|
|
.project({ email: 1, entities: 1, _id: 0 })
|
|
.toArray();
|
|
|
|
const entityIds = [...new Set(
|
|
users.flatMap(user =>
|
|
(user.entities || []).map((entity: any) => entity.id)
|
|
)
|
|
)];
|
|
|
|
const entityRecords = await db.collection('entities')
|
|
.find({ id: { $in: entityIds } })
|
|
.project({ id: 1, label: 1, _id: 0 })
|
|
.toArray();
|
|
|
|
const entityMap = new Map(
|
|
entityRecords.map(entity => [entity.id, entity.label])
|
|
);
|
|
|
|
return users.map(user => ({
|
|
email: user.email,
|
|
entityLabels: (user.entities || [])
|
|
.map((entity: any) => entityMap.get(entity.id))
|
|
.filter((label: string): label is string => !!label)
|
|
}));
|
|
}
|
|
|
|
|
|
|
|
async function assignToEntity(body: any) {
|
|
const { ids, entity } = body;
|
|
|
|
if (!Array.isArray(ids) || ids.length === 0 || !entity) {
|
|
return;
|
|
}
|
|
|
|
const users = await db.collection('users')
|
|
.find({ id: { $in: ids } })
|
|
.project({ id: 1, entities: 1 })
|
|
.toArray();
|
|
|
|
const toUpdateUsers = users.filter((u) => u.entities[0].id !== entity);
|
|
|
|
if (toUpdateUsers.length > 0) {
|
|
const writes = users.map(user => ({
|
|
updateOne: {
|
|
filter: { id: user.id },
|
|
update: {
|
|
$set: {
|
|
entities: [{
|
|
id: entity,
|
|
role: user.entities?.[0]?.role
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
|
|
db.collection('users').bulkWrite(writes);
|
|
}
|
|
} |