diff --git a/server/api/auth.js b/server/api/auth.js index 6e0ea35..c33308e 100644 --- a/server/api/auth.js +++ b/server/api/auth.js @@ -3,64 +3,69 @@ const prisma = new Prisma.PrismaClient(); import { log } from "../logs.js"; -export class Auth { - constructor() {} - login = async (req, res) => { - if (!req.body.password) { - res.redirect("/login"); - return; - } else { - if (req.body.password == process.env.AUTH_PASSWORD) { - const session = await prisma.session.create({ - data: { - validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), // Expires after 14 days - }, - }); - res.cookie("session", session.token, { - httpOnly: true, - expires: new Date(253402300000000), - }); - log("API / Auth", `New session: ${session.token}`); - res.redirect("/"); - } else { - res.redirect("/login"); - return; - } - } - }; - checkLogin = async (req, res, next) => { - if (!process.env.AUTH_PASSWORD) { - next(); - return; - } - if (!req.cookies.session) { - res.sendStatus(401); - return; - } else { - const session = await prisma.session.findUnique({ - where: { - token: req.cookies.session, - }, - }); - if (!session) { - res.sendStatus(401); - return; - } - // Renew session expiration date - await prisma.session.update({ - where: { - token: session.token, - }, - data: { - validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), - }, - }); - } - next(); - }; +async function login(req, res) { + // Check password + if (!req.body.password || req.body.password != process.env.AUTH_PASSWORD) { + res.redirect("/login"); + return; + } + // Create a new auth session + const session = await prisma.session.create({ + data: { + // Expires after 14 days of inactivity + validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), + }, + }); + res.cookie("session", session.token, { + httpOnly: true, + // Expire "never" + expires: new Date(253402300000000), + }); + log("API / Auth", `New session: ${session.token}`); + res.redirect("/"); } -// Check for expired sessions every hour +async function checkLogin(req, res, next) { + // If AUTH_PASSWORD env variable is not present don't require any login + if (!process.env.AUTH_PASSWORD) { + next(); + return; + } + // If no session cookie is set send 401 Unauthorized + // so the app redirects to the login page + if (!req.cookies.session) { + res.sendStatus(401); + return; + } + // Check the provided session cookie + const session = await prisma.session.findUnique({ + where: { + token: req.cookies.session, + }, + }); + // If no session is found also send 401 + if (!session) { + res.sendStatus(401); + return; + } + // Renew session expiration date + await prisma.session.update({ + where: { + token: session.token, + }, + data: { + validUntil: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), + }, + }); + next(); +} + +export default { + login, + checkLogin, +}; + +// Clean up expired sessions every hour setInterval(async () => { const sessions = await prisma.session.findMany(); for (const session of sessions) { diff --git a/server/api/index.js b/server/api/index.js index 9fe13f7..1d85213 100644 --- a/server/api/index.js +++ b/server/api/index.js @@ -22,7 +22,7 @@ export async function getTimetable(req, res) { }, }); if (!timetable) { - res.status(400).send({ + res.status(404).send({ success: false, error: "no_timetable", message: "No timetable was found for this class", @@ -44,7 +44,7 @@ function convertToDate(dateQuery) { if (dateQuery.match(/^[0-9]+$/) != null) date = parseInt(dateQuery); else date = dateQuery; date = new Date(date).setUTCHours(0, 0, 0, 0); - return date; + return new Date(date); } // Get substitutions API endpoint (/api/substitutions) @@ -75,11 +75,11 @@ export async function getSubstitutions(req, res) { // Choose which date to use in database query if (from && to) { prismaOptions.where.date = { - gte: new Date(from), - lte: new Date(to), + gte: from, + lte: to, }; } else if (date) { - prismaOptions.where.date = new Date(date); + prismaOptions.where.date = date; } else { // Default to all substitutions for today and in the future prismaOptions.where.date = { @@ -140,11 +140,11 @@ export async function getHistory(req, res) { // Choose which date to use in database query if (from && to) { prismaOptions.where.substitution.date = { - gte: new Date(from), - lte: new Date(to), + gte: from, + lte: to, }; } else if (date) { - prismaOptions.where.substitution.date = new Date(date); + prismaOptions.where.substitution.date = date; } else { // Default to history of all substitutions for today and in the future prismaOptions.where.substitution.date = { @@ -173,7 +173,7 @@ export async function getHistory(req, res) { // Get classes API endpoints (/api/classes) // Get all available classes where timetable and // substitutions can be requested for -export async function getClasses(req, res) { +export async function getClasses(_req, res) { const classes = await prisma.class.findMany({ select: { name: true, diff --git a/server/index.js b/server/index.js index 6f54d8a..1146741 100644 --- a/server/index.js +++ b/server/index.js @@ -9,7 +9,7 @@ import { getHistory, getClasses, } from "./api/index.js"; -import { Auth } from "./api/auth.js"; +import auth from "./api/auth.js"; import { Parser } from "./parser/index.js"; // Check DSB Mobile credentials are supplied @@ -34,7 +34,6 @@ new Parser( ); // Create new Auth class to store sessions -const auth = new Auth(); app.post("/login", auth.login); // Check login for every API request app.use("/api", auth.checkLogin); diff --git a/server/parser/dsbmobile.js b/server/parser/dsbmobile.js index e428adb..8a01354 100644 --- a/server/parser/dsbmobile.js +++ b/server/parser/dsbmobile.js @@ -5,7 +5,7 @@ export async function getAuthtoken(username, password) { const response = await axios.get( `${baseUrl}/authid?user=${username}&password=${password}&bundleid&appversion&osversion&pushid` ); - if (response.data == "") throw "Wrong username or password"; + if (response.data == "") throw "Wrong DSB username or password"; return response.data; } diff --git a/server/parser/index.js b/server/parser/index.js index 4de7c4e..1d37f32 100644 --- a/server/parser/index.js +++ b/server/parser/index.js @@ -28,9 +28,9 @@ export class Parser { const plans = []; for (const entry of data) { // Download the substitution plan - const data = await this.fetchFile(entry.url); + const file = await this.fetchFile(entry.url); // Parse the substitution plan - const parsed = parseSubstitutionPlan(data); + const parsed = parseSubstitutionPlan(file); plans.push(parsed); } // Create a new parse event