Files
Timetable-V2/server/api/index.js
minie4 0cb55eaf68 Add support for other substitution types
- Store and display original substitution type text
- Treat "eigenverantwortliches Arbeiten" as cancellation
2023-08-27 18:00:09 +02:00

276 lines
7.1 KiB
JavaScript

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);
}