import Prisma from "@prisma/client"; const prisma = new Prisma.PrismaClient(); import { applyKey, hasPermission, listPermissions, revokeKey, } from "./permission.js"; // Get info API endpoint (/api/info) // Returns information about the requesting session export async function getInfo(req, res) { // If server has auth disabled if (!req.locals.session) { res.send({ authenticated: true, appliedKeys: [], permissions: [], }); return; } const session = await prisma.session.findUnique({ where: { token: req.locals.session, }, include: { appliedKeys: { select: { key: true, permissions: true, validUntil: true, }, }, }, }); res.send({ authenticated: true, appliedKeys: session.appliedKeys, permissions: await listPermissions(session.token), }); } // Put and Delete key API endpoints (/api/key) // Applies or revokes a key from the requesting user's session export async function putKey(req, res) { if (await applyKey(req.locals.session, req.query.key)) { res.status(200).send(); } else { res.status(400).send({ success: false, error: "invalid_key", message: "This key does not exist", }); } } export async function deleteKey(req, res) { if (await revokeKey(req.locals.session, req.query.key)) { res.status(200).send(); } else { res.status(400).send(); } } // Get timetable API endpoint (/api/timetable) // Returns timetable data for requested class if available export async function getTimetable(req, res) { if (!req.query.class) { res.status(400).send({ success: false, error: "missing_parameter", message: "No class parameter provided", }); return; } const requestedClass = req.query.class.toLowerCase(); const timetables = await prisma.timetable.findMany({ where: { class: requestedClass, }, orderBy: { updatedAt: "desc", }, }); const times = await prisma.time.findMany(); res.send({ timetables, times, }); } // Edit timetable API endpoint (/api/timetable) // Updates a remote timetable with the requested data export async function putTimetable(req, res) { const timetableId = parseInt(req.query.id); const data = req.body.data; if ( !(await hasPermission(req.locals.session, "timetable.update", timetableId)) ) { res.status(401).send({ success: false, error: "missing_permission", message: "You don't have permission to update this timetable!", }); return; } await prisma.timetable.update({ where: { id: timetableId, }, data: { data, title: req.body.title, }, }); res.status(201).send(); } // Helper function for converting a date string // (eg. "2022-06-02" or "1654128000000") to a // unix timestamp function convertToDate(dateQuery) { var date; if (dateQuery.match(/^[0-9]+$/) != null) date = parseInt(dateQuery); else date = dateQuery; date = new Date(date).setUTCHours(0, 0, 0, 0); return new Date(date); } // Get substitutions API endpoint (/api/substitutions) // Returns all known substitutions for requested date / class // If no class is supplied, all substitutions are returned export async function getSubstitutions(req, res) { const requestedClass = (req.query.class || "").toLowerCase(); var from, to, date; // Check if from or to date is set in request if (req.query.from && req.query.to) { from = convertToDate(req.query.from); to = convertToDate(req.query.to); } else if (req.query.date) { date = convertToDate(req.query.date); } const prismaOptions = { where: { removed: false, }, orderBy: { lesson: "asc", }, }; if (requestedClass) { prismaOptions.where.class = { has: requestedClass }; } // Choose which date to use in database query if (from && to) { prismaOptions.where.date = { gte: from, lte: to, }; } else if (date) { prismaOptions.where.date = date; } else { // Default to all substitutions for today and in the future prismaOptions.where.date = { gte: new Date(new Date().setUTCHours(0, 0, 0, 0)), }; } const rawSubstitutions = await prisma.substitution.findMany(prismaOptions); const substitutions = rawSubstitutions.map((element) => { const substitution = { id: element.id, class: element.class, type: element.type, rawType: element.rawType, lesson: element.lesson, date: new Date(element.date).getTime(), notes: element.notes, teacher: element.teacher, change: {}, }; if (element.changedRoom) substitution.change.room = element.changedRoom; if (element.changedTeacher) substitution.change.teacher = element.changedTeacher; if (element.changedSubject) substitution.change.subject = element.changedSubject; return substitution; }); res.send(substitutions); } // Get history API endpoint (/api/history) // Returns history of changes for all substituions in the date range // for the requested class if supplied export async function getHistory(req, res) { const requestedClass = (req.query.class || "").toLowerCase(); var from, to, date; // Check if from or to date is set in request if (req.query.from && req.query.to) { from = convertToDate(req.query.from); to = convertToDate(req.query.to); } else if (req.query.date) { date = convertToDate(req.query.date); } const prismaOptions = { where: { substitution: {}, }, include: { substitution: true, }, orderBy: { createdAt: "desc", }, }; if (requestedClass) { prismaOptions.where.substitution.class = { has: requestedClass }; } // Choose which date to use in database query if (from && to) { prismaOptions.where.substitution.date = { gte: from, lte: to, }; } else if (date) { prismaOptions.where.substitution.date = date; } else { // Default to history of all substitutions for today and in the future prismaOptions.where.substitution.date = { gte: new Date(new Date().setUTCHours(0, 0, 0, 0)), }; } const rawChanges = await prisma.substitutionChange.findMany(prismaOptions); const changes = rawChanges.map((element) => { return { id: element.id, type: element.type, class: element.substitution.class, substitutionId: element.substitutionId, lesson: element.substitution.lesson, updatedAt: new Date(element.createdAt).getTime(), date: new Date(element.substitution.date).getTime(), teacher: element.teacher, change: element.changes, notes: element.notes, parseEventId: element.parseEventId, }; }); res.send(changes); } // Get classes API endpoints (/api/classes) // Get all available classes where timetable and // substitutions can be requested for export async function getClasses(_req, res) { const classes = await prisma.class.findMany({ select: { name: true, regex: false, }, orderBy: { name: "asc", }, }); // Only return the name of the class const classList = classes.map((element) => element.name); res.send(classList); }