import { readFile, access } from "fs/promises"; import path from "path"; import matter from "gray-matter"; import { Client } from "ldapts"; import { serialize } from "next-mdx-remote/serialize"; import { capitalize, getCurrentTermYear, getTermYear, TermYear } from "@/utils"; const EXECS_PATH = path.join("content", "team", "execs"); const FILETYPE = ".md"; const execPositions: { [position: string]: string } = { president: "President", "vice-president": "Vice-President", secretary: "Assistant Vice-President", treasurer: "Treasurer", sysadmin: "Systems Administrator", }; const orderedExecPositions: string[] = [ "president", "vice-president", "secretary", "treasurer", "sysadmin", ]; export interface Metadata { name: string; role?: string; image: string; } export async function getExecs() { const execNamePosPairs = await getExecNamePosPairs(); return await Promise.all( execNamePosPairs.map((namePosPair) => getExec(namePosPair[0], namePosPair[1]) ) ); } async function getExec(name: string, pos: string) { let content, metadata; try { const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`)); ({ content, data: metadata } = matter(raw)); const image = await getMemberImagePath(metadata.name as string); return { content: await serialize(content), metadata: { ...metadata, image } as Metadata, }; } catch (err) { // Capitalize the first letter of the first name and last name const firstName = capitalize(name.split("-")[0]); const lastName = capitalize(name.split("-")[1]); const posName = execPositions[pos]; content = "Coming Soon!"; metadata = { name: `${firstName} ${lastName}`, role: `${posName}`, }; const image = await getMemberImagePath(metadata.name); return { content: await serialize(content), metadata: { ...metadata, image } as Metadata, }; } } async function getExecNamePosPairs(): Promise< [person: string, position: string][] > { if (process.env.USE_LDAP?.toLowerCase() !== "true") { return [["codey", "mascot"]]; } const url = "ldap://ldap1.csclub.uwaterloo.ca"; const searchDN = "ou=People,dc=csclub,dc=uwaterloo,dc=ca"; const client = new Client({ url }); // position: name const execMembers: { [position: string]: string } = {}; let formattedExec: [person: string, position: string][] = []; try { await client.bind("", ""); const terms: TermYear[] = []; // check members from last two terms (including current term) for (const termYear of getTermYear(getCurrentTermYear(), { goBackwards: true, })) { if (terms.length >= 2) { break; } terms.push(termYear); } const termsFilters = terms .map( ({ term, year }: TermYear) => `(term=${(term as string).slice(0, 1)}${year})` ) .join(""); const { searchEntries } = await client.search(searchDN, { scope: "sub", filter: `(&(objectClass=member)(|${termsFilters}))`, }); // item.position might be an array if the member has more than one position searchEntries.forEach((item) => { if (typeof item.position === "string" && item.position in execPositions) { execMembers[item.position] = item.cn as string; } else if (item.position instanceof Array) { item.position.forEach((p) => { if ((p as string) in execPositions) { execMembers[p as string] = item.cn as string; } }); } }); formattedExec = orderedExecPositions .map((position) => { const fullName = execMembers[position]; if (fullName == undefined) { return null; } const firstName = fullName.split(" ")[0].toLowerCase(); const lastName = fullName.split(" ")[1].toLowerCase(); return [`${firstName}-${lastName}`, position]; }) .filter((pair) => pair != null) as [person: string, position: string][]; formattedExec = [...formattedExec, ["codey", "mascot"]]; } finally { await client.unbind(); } return formattedExec; } async function getImage(imgPath: string) { try { await access(path.join("public", imgPath)); return imgPath; } catch { return undefined; } } export async function getMemberImagePath(name: string) { const imgPath = path.join("images", "team", name.replace(" ", "")); const placeholder = path.join( "images", "team", "team-member-placeholder.svg" ); const img = (await getImage(imgPath + ".jpg")) ?? (await getImage(imgPath + ".png")) ?? (await getImage(imgPath + ".gif")) ?? (await getImage(imgPath + ".jpeg")) ?? (await getImage(imgPath + ".JPG")) ?? (await getImage(imgPath + ".PNG")) ?? (await getImage(imgPath + ".GIF")) ?? (await getImage(imgPath + ".JPEG")) ?? placeholder; return img; }