mirror of
https://github.com/detleph/server.git
synced 2026-09-04 08:36:06 +02:00
patched review suggestions and discovered bugs
This commit is contained in:
@@ -137,12 +137,14 @@ export const addEvent = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = result.data;
|
||||||
|
|
||||||
const event = await prisma.event.create({
|
const event = await prisma.event.create({
|
||||||
data: {
|
data: {
|
||||||
name: req.body.name,
|
name: body.name,
|
||||||
date: req.body.date,
|
date: body.date,
|
||||||
briefDescription: req.body.briefDescription,
|
briefDescription: body.briefDescription,
|
||||||
fullDescription: req.body.fullDescription,
|
fullDescription: body.fullDescription,
|
||||||
},
|
},
|
||||||
select: basicEvent,
|
select: basicEvent,
|
||||||
});
|
});
|
||||||
@@ -206,7 +208,7 @@ export const updateEvent = async (req: Request<{ pid: string }>, res: Response)
|
|||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
throw new NotFoundError("discipline", pid);
|
throw new NotFoundError("event", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
@@ -231,7 +233,7 @@ export const deleteEvent = async (req: Request<DeleteEventQueryParams>, res: Res
|
|||||||
return res.status(204).end();
|
return res.status(204).end();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
throw new NotFoundError("discipline", pid);
|
throw new NotFoundError("event", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
|||||||
import { generateInvalidBodyError, DataType } from "./common";
|
import { generateInvalidBodyError, DataType } from "./common";
|
||||||
import { unlink } from "fs/promises";
|
import { unlink } from "fs/promises";
|
||||||
import ForwardableError from "../Middleware/error/ForwardableError";
|
import ForwardableError from "../Middleware/error/ForwardableError";
|
||||||
|
import { table } from "console";
|
||||||
|
|
||||||
require("express-async-errors");
|
require("express-async-errors");
|
||||||
|
|
||||||
@@ -204,21 +205,26 @@ export const linkMedia = async (req: Request<{ pid: string }, {}, { mediaPid: st
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedRec = await getPrismaUpdateFKT(tableToUpdate[2])({
|
try {
|
||||||
where: { pid },
|
const updatedRec = await getPrismaUpdateFKT(tableToUpdate[2])({
|
||||||
data: {
|
where: { pid },
|
||||||
visual: { connect: { pid: mediaPid } },
|
data: {
|
||||||
},
|
visual: { connect: { pid: mediaPid } },
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!updatedRec) {
|
return res.status(200).json({
|
||||||
throw new NotFoundError(tableToUpdate[2], pid);
|
type: "success",
|
||||||
|
payload: { message: "Linking with the visual was successful" },
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
|
throw new NotFoundError(tableToUpdate[2], pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({
|
|
||||||
type: "success",
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unlinkMedia = async (req: Request<{ pid: string; mediaPid: string }>, res: Response) => {
|
export const unlinkMedia = async (req: Request<{ pid: string; mediaPid: string }>, res: Response) => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const detailedOrganisation = {
|
|||||||
pid: true,
|
pid: true,
|
||||||
name: true,
|
name: true,
|
||||||
date: true,
|
date: true,
|
||||||
description: true,
|
briefDescription: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
@@ -191,7 +191,7 @@ export const updateOrganisation = async (
|
|||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
throw new NotFoundError("discipline", pid);
|
throw new NotFoundError("organisation", pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ const InitialParticipant = z.object({
|
|||||||
groupPid: z.string().min(1).uuid(),
|
groupPid: z.string().min(1).uuid(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ParticipantBody = InitialParticipant.extend({ teamPid: z.string().uuid() });
|
|
||||||
|
|
||||||
const returnedParticipant = {
|
const returnedParticipant = {
|
||||||
pid: true,
|
pid: true,
|
||||||
firstName: true,
|
firstName: true,
|
||||||
@@ -41,7 +39,7 @@ const returnedParticipant = {
|
|||||||
export const createParticipant = async (req: Request<{ teamPid: string }>, res: Response) => {
|
export const createParticipant = async (req: Request<{ teamPid: string }>, res: Response) => {
|
||||||
const { teamPid } = req.params;
|
const { teamPid } = req.params;
|
||||||
|
|
||||||
const result = ParticipantBody.safeParse(req.body);
|
const result = InitialParticipant.safeParse(req.body);
|
||||||
|
|
||||||
if (result.success === false) {
|
if (result.success === false) {
|
||||||
return res.status(400).json(
|
return res.status(400).json(
|
||||||
@@ -59,20 +57,20 @@ export const createParticipant = async (req: Request<{ teamPid: string }>, res:
|
|||||||
|
|
||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireLeaderOfTeam(req.teamleader, teamPid);
|
await requireLeaderOfTeam(req.teamleader, teamPid);
|
||||||
} else {
|
} else if (req.auth?.permission_level == "STANDARD") {
|
||||||
requireResponsibleForGroups(req.auth, body.groupPid);
|
requireResponsibleForGroups(req.auth, body.groupPid);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const discipline = await prisma.team.findUnique({
|
const discipline = await prisma.team.findUnique({
|
||||||
where: { pid: body.teamPid },
|
where: { pid: teamPid },
|
||||||
select: { discipline: true }
|
select: { discipline: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxteamsize = discipline?.discipline.maxTeamSize;
|
const maxteamsize = discipline?.discipline.maxTeamSize;
|
||||||
|
|
||||||
const userCount = await prisma.participant.count({
|
const userCount = await prisma.participant.count({
|
||||||
where: { team: { pid: body.teamPid } }
|
where: { team: { pid: teamPid } }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (maxteamsize == userCount) {
|
if (maxteamsize == userCount) {
|
||||||
@@ -85,7 +83,7 @@ export const createParticipant = async (req: Request<{ teamPid: string }>, res:
|
|||||||
lastName: body.lastName,
|
lastName: body.lastName,
|
||||||
relevance: "MEMBER",
|
relevance: "MEMBER",
|
||||||
group: { connect: { pid: body.groupPid } },
|
group: { connect: { pid: body.groupPid } },
|
||||||
team: { connect: { pid: body.teamPid } },
|
team: { connect: { pid: teamPid } },
|
||||||
},
|
},
|
||||||
select: returnedParticipant,
|
select: returnedParticipant,
|
||||||
});
|
});
|
||||||
@@ -95,7 +93,7 @@ export const createParticipant = async (req: Request<{ teamPid: string }>, res:
|
|||||||
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
return res
|
return res
|
||||||
.status(404)
|
.status(404)
|
||||||
.json(generateError(`Could not link to team with ID '${body.teamPid}, or group with ID ${body.groupPid}'`));
|
.json(generateError(`Could not link to team with ID '${teamPid}, or group with ID ${body.groupPid}'`));
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -107,6 +105,8 @@ export const updateParticipant = async (req: Request<{ pid: string }>, res: Resp
|
|||||||
|
|
||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireResponsibleForParticipant(req.teamleader, pid);
|
await requireResponsibleForParticipant(req.teamleader, pid);
|
||||||
|
} else if (req.auth?.permission_level == "STANDARD") {
|
||||||
|
requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = InitialParticipant.partial().safeParse(req.body);
|
const result = InitialParticipant.partial().safeParse(req.body);
|
||||||
@@ -143,7 +143,9 @@ export const updateParticipant = async (req: Request<{ pid: string }>, res: Resp
|
|||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
throw new NotFoundError("participant", pid);
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json(generateError(`Could not find participant '${pid}, or link to group with ID ${body.groupPid}.'`));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
@@ -156,7 +158,7 @@ export const deleteParticipant = async (req: Request<{ pid: string }>, res: Resp
|
|||||||
|
|
||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireResponsibleForParticipant(req.teamleader, pid);
|
await requireResponsibleForParticipant(req.teamleader, pid);
|
||||||
} else {
|
} else if (req.auth?.permission_level == "STANDARD") {
|
||||||
requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(pid));
|
requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import prisma from "../lib/prisma";
|
import prisma from "../lib/prisma";
|
||||||
@@ -61,7 +62,7 @@ export async function getRolesForTeam(req: Request<{ pid: string }>, res: Respon
|
|||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireLeaderOfTeam(req.teamleader, pid);
|
await requireLeaderOfTeam(req.teamleader, pid);
|
||||||
} else {
|
} else {
|
||||||
await requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid));
|
requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
const roles = await prisma.role.findMany({
|
const roles = await prisma.role.findMany({
|
||||||
@@ -84,7 +85,6 @@ export async function getRolesForTeam(req: Request<{ pid: string }>, res: Respon
|
|||||||
|
|
||||||
const AssignParticipantToRoleBody = z.object({
|
const AssignParticipantToRoleBody = z.object({
|
||||||
participantPid: z.string().uuid(),
|
participantPid: z.string().uuid(),
|
||||||
teamPid: z.string().uuid(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// requires: auth(leader of the team)
|
// requires: auth(leader of the team)
|
||||||
@@ -102,31 +102,29 @@ export async function assignParticipantToRole(req: Request<{ pid: string }>, res
|
|||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
requireResponsibleForParticipant(req.teamleader, participantPid);
|
requireResponsibleForParticipant(req.teamleader, participantPid);
|
||||||
} else {
|
} else {
|
||||||
await requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(participantPid));
|
requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(participantPid));
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = await prisma.role.findFirst({
|
try {
|
||||||
where: { pid, team: { participants: { some: { pid: participantPid } } } },
|
const schema = await prisma.role.update({ where: { pid }, data: { participant: { connect: { pid: participantPid } } }, select: detailedRole });
|
||||||
select: { participant: { select: { pid: true, firstName: true, lastName: true } } },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!schema) {
|
return res.status(200).json({
|
||||||
return res.status(404).json({
|
type: "success",
|
||||||
type: "error",
|
|
||||||
payload: {
|
payload: {
|
||||||
message: `No role with the ID '${pid}' could be found in the scope of the participant with the ID '${participantPid}'`,
|
message: `The participant with ID '${participantPid}' was successfully assigned to the role with ID '${pid}'`,
|
||||||
|
...(schema.participant ? { unassigned: schema.participant } : {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
|
return res.status(404).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: `No role with the ID '${pid}' could be found in the scope of the participant with the ID '${participantPid}'`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No error handling should be neccesary as the existence of the role and participant have already been checked above
|
|
||||||
await prisma.role.update({ where: { pid }, data: { participant: { connect: { pid: participantPid } } } });
|
|
||||||
|
|
||||||
return res.status(200).json({
|
|
||||||
type: "success",
|
|
||||||
payload: {
|
|
||||||
message: `The participant with ID '${participantPid}' was successfully assigned to the role with ID '${pid}'`,
|
|
||||||
...(schema.participant ? { unassigned: schema.participant } : {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import prisma from "../lib/prisma";
|
import prisma from "../lib/prisma";
|
||||||
import { DurationSchemaT, parseSchema, PointSchemaT } from "../lib/result_schema";
|
import { DurationSchema, parseSchema, PointSchema } from "../lib/result_schema";
|
||||||
import NotFoundError from "../Middleware/error/NotFoundError";
|
import NotFoundError from "../Middleware/error/NotFoundError";
|
||||||
import SchemaError from "../Middleware/error/SchemaError";
|
import SchemaError from "../Middleware/error/SchemaError";
|
||||||
import {
|
import {
|
||||||
@@ -19,7 +19,7 @@ require("express-async-errors");
|
|||||||
|
|
||||||
const RoleSchemaBody = z.object({
|
const RoleSchemaBody = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
schema: z.string(),
|
schema: z.string(PointSchema).or(z.string(DurationSchema)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const UpdateBody = RoleSchemaBody.partial();
|
const UpdateBody = RoleSchemaBody.partial();
|
||||||
@@ -138,7 +138,7 @@ export const createRoleSchema = async (
|
|||||||
|
|
||||||
export const UpdateRoleSchema = async (req: Request<{ pid: string }>, res: Response) => {
|
export const UpdateRoleSchema = async (req: Request<{ pid: string }>, res: Response) => {
|
||||||
if (req.auth?.permission_level !== "ELEVATED") {
|
if (req.auth?.permission_level !== "ELEVATED") {
|
||||||
res.status(403).json(createInsufficientPermissionsError());
|
return res.status(403).json(createInsufficientPermissionsError());
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pid } = req.params;
|
const { pid } = req.params;
|
||||||
@@ -150,7 +150,7 @@ export const UpdateRoleSchema = async (req: Request<{ pid: string }>, res: Respo
|
|||||||
generateInvalidBodyError(
|
generateInvalidBodyError(
|
||||||
{
|
{
|
||||||
name: DataType.STRING,
|
name: DataType.STRING,
|
||||||
schema: DataType.STRING,
|
schema: DataType.RESULT_SCHEMA,
|
||||||
},
|
},
|
||||||
result.error
|
result.error
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { Prisma } from "@prisma/client";
|
|||||||
import NotFoundError from "../Middleware/error/NotFoundError";
|
import NotFoundError from "../Middleware/error/NotFoundError";
|
||||||
import { requireResponsibleForGroups } from "../Middleware/auth/auth";
|
import { requireResponsibleForGroups } from "../Middleware/auth/auth";
|
||||||
import AuthError from "../Middleware/error/AuthError";
|
import AuthError from "../Middleware/error/AuthError";
|
||||||
|
import { runInNewContext } from "vm";
|
||||||
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
||||||
|
|
||||||
require("express-async-errors");
|
require("express-async-errors");
|
||||||
|
|
||||||
@@ -31,6 +33,9 @@ export const basicTeam = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getTeams = async (req: Request, res: Response) => {
|
export const getTeams = async (req: Request, res: Response) => {
|
||||||
|
if (req.auth?.permission_level == "STANDARD") {
|
||||||
|
throw new AuthError("A STANDARD Admin is not allowed to get all teams!");
|
||||||
|
}
|
||||||
const teams = await prisma.team.findMany({ select: basicTeam });
|
const teams = await prisma.team.findMany({ select: basicTeam });
|
||||||
|
|
||||||
res.status(200).json({ type: "success", payload: { teams } });
|
res.status(200).json({ type: "success", payload: { teams } });
|
||||||
@@ -41,8 +46,8 @@ export const getTeam = async (req: Request<{ pid: string }>, res: Response) => {
|
|||||||
|
|
||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireLeaderOfTeam(req.teamleader, pid);
|
await requireLeaderOfTeam(req.teamleader, pid);
|
||||||
} else {
|
} else if (req.auth?.permission_level == "STANDARD") {
|
||||||
await requireResponsibleForGroups(req.auth, pid);
|
requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
const team = await prisma.team.findUnique({
|
const team = await prisma.team.findUnique({
|
||||||
@@ -62,8 +67,8 @@ export const updateTeam = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
if (req.teamleader?.isAuthenticated) {
|
if (req.teamleader?.isAuthenticated) {
|
||||||
await requireLeaderOfTeam(req.teamleader, pid);
|
await requireLeaderOfTeam(req.teamleader, pid);
|
||||||
} else {
|
} else if (req.auth?.permission_level == "STANDARD") {
|
||||||
await requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid));
|
requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = TeamBody.omit({ partGroupId: true, partFirstName: true, partLastName: true }).safeParse(req.body);
|
const result = TeamBody.omit({ partGroupId: true, partFirstName: true, partLastName: true }).safeParse(req.body);
|
||||||
@@ -117,9 +122,17 @@ export const deleteTeam = async (req: Request, res: Response) => {
|
|||||||
throw new AuthError("STANDARD Admins are not allowed to delete Teams!")
|
throw new AuthError("STANDARD Admins are not allowed to delete Teams!")
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.team.delete({ where: { pid } });
|
try {
|
||||||
|
await prisma.team.delete({ where: { pid } });
|
||||||
|
|
||||||
res.status(204).json({ type: "success", payload: { message: "Sucesfully deleted team" } });
|
res.status(204).json({ type: "success", payload: { message: "Sucesfully deleted team" } });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
|
throw new NotFoundError("team", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function checkTeamExistence(teamPid: string) {
|
export async function checkTeamExistence(teamPid: string) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { DataType, generateError, generateInvalidBodyError } from "./common";
|
|||||||
import { generateTeamleaderJWT } from "../Middleware/auth/teamleaderAuth";
|
import { generateTeamleaderJWT } from "../Middleware/auth/teamleaderAuth";
|
||||||
import { createRolesForTeam } from "./role.controller";
|
import { createRolesForTeam } from "./role.controller";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
||||||
|
import NotFoundError from "../Middleware/error/NotFoundError";
|
||||||
|
|
||||||
require("express-async-errors");
|
require("express-async-errors");
|
||||||
|
|
||||||
@@ -49,50 +51,55 @@ export const register = async (req: Request<{}, {}, CreateTeamBody>, res: Respon
|
|||||||
|
|
||||||
const body = result.data;
|
const body = result.data;
|
||||||
|
|
||||||
const group = prisma.group.findUnique({ where: { pid: body.partGroupId } });
|
try {
|
||||||
|
const team = await prisma.team.create({
|
||||||
if (typeof group == null) {
|
data: {
|
||||||
return res.status(404).json(generateError("Specified group was not found!"));
|
leaderEmail: body.leaderEmail,
|
||||||
}
|
name: body.teamName,
|
||||||
|
roles: undefined,
|
||||||
const discipline = prisma.discipline.findUnique({ where: { pid: body.disciplineId } });
|
discipline: { connect: { pid: body.disciplineId } },
|
||||||
|
participants: {
|
||||||
if (typeof discipline == null) {
|
create: {
|
||||||
return res.status(404).json(generateError("Specified discipline was not found!"));
|
firstName: body.partFirstName,
|
||||||
}
|
lastName: body.partLastName,
|
||||||
|
relevance: "TEAMLEADER",
|
||||||
const team = await prisma.team.create({
|
group: { connect: { pid: body.partGroupId } },
|
||||||
data: {
|
},
|
||||||
leaderEmail: body.leaderEmail,
|
|
||||||
name: body.teamName,
|
|
||||||
roles: undefined,
|
|
||||||
discipline: { connect: { pid: body.disciplineId } },
|
|
||||||
participants: {
|
|
||||||
create: {
|
|
||||||
firstName: body.partFirstName,
|
|
||||||
lastName: body.partLastName,
|
|
||||||
relevance: "TEAMLEADER",
|
|
||||||
group: { connect: { pid: body.partGroupId } },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
select: {
|
||||||
select: {
|
pid: true,
|
||||||
pid: true,
|
name: true,
|
||||||
name: true,
|
discipline: { select: { pid: true } },
|
||||||
discipline: { select: { pid: true } },
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
await createRolesForTeam(team.pid);
|
await createRolesForTeam(team.pid);
|
||||||
|
|
||||||
const usid = nanoid();
|
const usid = nanoid();
|
||||||
|
|
||||||
|
(await mailClient).set(usid, team.pid);
|
||||||
|
|
||||||
|
const eventname = await prisma.discipline.findUnique({ where: { pid: body.disciplineId }, select: { event: { select: { name: true, } } } }).event.name;
|
||||||
|
verificationMail(req.body.leaderEmail, eventname, usid);
|
||||||
|
|
||||||
|
return res.status(201).json({ type: "success", payload: { team } });
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
|
return res.status(404).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: `Could not connect to discipline with the ID '${body.disciplineId}' or could not connect participant to group with the ID '${body.partGroupId}'`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
(await mailClient).set(usid, team.pid);
|
|
||||||
|
|
||||||
// TODO: fix "eventname"
|
|
||||||
verificationMail(req.body.leaderEmail, "eventname", usid);
|
|
||||||
|
|
||||||
res.status(201).json({ type: "success", payload: { team } });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requestToken = async (req: Request, res: Response) => {
|
export const requestToken = async (req: Request, res: Response) => {
|
||||||
@@ -117,6 +124,7 @@ export const requestToken = async (req: Request, res: Response) => {
|
|||||||
(await mailClient).set(usid, team.pid);
|
(await mailClient).set(usid, team.pid);
|
||||||
|
|
||||||
// TODO: fix "eventname"
|
// TODO: fix "eventname"
|
||||||
|
// let teamName = await prisma.team.findUnique({ where: { pid: teamId }, select: { discipline: { select: { event: { select: { name: true } } } } } });
|
||||||
verificationMail(team.leaderEmail, "eventname", usid);
|
verificationMail(team.leaderEmail, "eventname", usid);
|
||||||
|
|
||||||
res.status(200).json({ type: "sucess", payload: { message: "Email sent!" } });
|
res.status(200).json({ type: "sucess", payload: { message: "Email sent!" } });
|
||||||
@@ -145,23 +153,32 @@ export const verifyEmail = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const team = await prisma.team.update({
|
try {
|
||||||
where: {
|
const team = await prisma.team.update({
|
||||||
pid: acc,
|
where: {
|
||||||
},
|
pid: acc,
|
||||||
data: {
|
},
|
||||||
verified: true,
|
data: {
|
||||||
},
|
verified: true,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
mailClient.set(code, "");
|
mailClient.set(code, "");
|
||||||
|
|
||||||
const token = generateTeamleaderJWT(team);
|
const token = generateTeamleaderJWT(team);
|
||||||
|
|
||||||
res.cookie("teamLeaderToken", token, {
|
res.cookie("teamLeaderToken", token, {
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 1000 * 60 * 60 * 24 * 4,
|
maxAge: 1000 * 60 * 60 * 24 * 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.status(200).json({ type: "succes", payload: { token } });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
|
||||||
|
throw new NotFoundError("discipline", acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).json({ type: "succes", payload: { token } });
|
|
||||||
};
|
};
|
||||||
|
|||||||
+105
-105
@@ -18,42 +18,82 @@ export const getBearerToken = (authorization: string) => authorization.slice(7);
|
|||||||
|
|
||||||
const _requireAdminAuthentication =
|
const _requireAdminAuthentication =
|
||||||
(config: { optional?: Boolean; controlled?: Boolean } = { optional: false, controlled: false }) =>
|
(config: { optional?: Boolean; controlled?: Boolean } = { optional: false, controlled: false }) =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
if (!JWT_SECRET) {
|
if (!JWT_SECRET) {
|
||||||
throw new Error("JWT_SECRET not set");
|
throw new Error("JWT_SECRET not set");
|
||||||
}
|
|
||||||
|
|
||||||
const { authorization } = req.headers;
|
|
||||||
|
|
||||||
if (!authorization) {
|
|
||||||
if (config.optional) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(403).send({
|
const { authorization } = req.headers;
|
||||||
type: "error",
|
|
||||||
payload: {
|
|
||||||
message: "The requeset did not include the Authorization header",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verifyAuthorizationFormat(authorization)) {
|
if (!authorization) {
|
||||||
return res.status(400).send({
|
if (config.optional) {
|
||||||
type: "error",
|
return false;
|
||||||
payload: {
|
}
|
||||||
message: "Malformed Authorization header",
|
|
||||||
format: "Bearer <token>",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let token_payload_: string | JwtPayload;
|
return res.status(403).send({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "The requeset did not include the Authorization header",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
if (!verifyAuthorizationFormat(authorization)) {
|
||||||
token_payload_ = jwt.verify(getBearerToken(authorization), JWT_SECRET);
|
return res.status(400).send({
|
||||||
} catch (e) {
|
type: "error",
|
||||||
if (e instanceof JsonWebTokenError) {
|
payload: {
|
||||||
|
message: "Malformed Authorization header",
|
||||||
|
format: "Bearer <token>",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_payload_: string | JwtPayload;
|
||||||
|
|
||||||
|
try {
|
||||||
|
token_payload_ = jwt.verify(getBearerToken(authorization), JWT_SECRET);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof JsonWebTokenError) {
|
||||||
|
return res.status(403).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "Token could not be verified; It might be expired",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token_payload = token_payload_ as AuthJWTPayload;
|
||||||
|
|
||||||
|
if (!token_payload.permission_level || !token_payload.pid || !token_payload.revision) {
|
||||||
|
if (typeof (token_payload as unknown as TeamleaderJWTPayload).team === "string") {
|
||||||
|
if (config.controlled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new AuthError("Teamleader authentication is not supported for this operation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AuthError("The token did not include the required information!");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pid, revision } = token_payload;
|
||||||
|
|
||||||
|
let db_revision = await authClient.get(pid);
|
||||||
|
|
||||||
|
if (db_revision === null) {
|
||||||
|
// Load the revision ID from the main DB and cache it in redis
|
||||||
|
const user = await prisma.admin.findUnique({ where: { pid }, select: { revision: true } });
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
db_revision = user.revision.toISOString();
|
||||||
|
|
||||||
|
await authClient.set(pid, db_revision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (revision !== db_revision || !revision || !db_revision) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
type: "error",
|
type: "error",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -62,62 +102,22 @@ const _requireAdminAuthentication =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
req.auth = {
|
||||||
}
|
isAuthenticated: true,
|
||||||
|
pid: token_payload.pid,
|
||||||
|
name: token_payload.name,
|
||||||
|
permission_level: token_payload.permission_level,
|
||||||
|
groups: token_payload.groups,
|
||||||
|
revision: token_payload.revision,
|
||||||
|
};
|
||||||
|
|
||||||
const token_payload = token_payload_ as AuthJWTPayload;
|
if (!config.controlled) {
|
||||||
|
next();
|
||||||
if (!token_payload.permission_level || !token_payload.pid || !token_payload.revision) {
|
|
||||||
if (typeof (token_payload as unknown as TeamleaderJWTPayload).team === "string") {
|
|
||||||
if (config.controlled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw new AuthError("Teamleader authentication is not supported for this operation!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AuthError("The token did not include the required information!");
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
const { pid, revision } = token_payload;
|
|
||||||
|
|
||||||
let db_revision = await authClient.get(pid);
|
|
||||||
|
|
||||||
if (db_revision === null) {
|
|
||||||
// Load the revision ID from the main DB and cache it in redis
|
|
||||||
const user = await prisma.admin.findUnique({ where: { pid }, select: { revision: true } });
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
db_revision = user.revision.toISOString();
|
|
||||||
|
|
||||||
await authClient.set(pid, db_revision);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revision !== db_revision || !revision || !db_revision) {
|
|
||||||
return res.status(403).json({
|
|
||||||
type: "error",
|
|
||||||
payload: {
|
|
||||||
message: "Token could not be verified; It might be expired",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
req.auth = {
|
|
||||||
isAuthenticated: true,
|
|
||||||
pid: token_payload.pid,
|
|
||||||
name: token_payload.name,
|
|
||||||
permission_level: token_payload.permission_level,
|
|
||||||
groups: token_payload.groups,
|
|
||||||
revision: token_payload.revision,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!config.controlled) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const requireAuthentication = _requireAdminAuthentication({ optional: false, controlled: false });
|
export const requireAuthentication = _requireAdminAuthentication({ optional: false, controlled: false });
|
||||||
|
|
||||||
type AuthType = "admin" | "teamleader";
|
type AuthType = "admin" | "teamleader";
|
||||||
@@ -144,37 +144,37 @@ function getAuthTypes(type: AuthType | AuthTypeConfig): AuthType[] {
|
|||||||
|
|
||||||
export const requireConfiguredAuthentication =
|
export const requireConfiguredAuthentication =
|
||||||
(config: AuthConfiguration = { optional: false, type: "admin" }) =>
|
(config: AuthConfiguration = { optional: false, type: "admin" }) =>
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const types = getAuthTypes(config.type);
|
const types = getAuthTypes(config.type);
|
||||||
const optional = config.optional;
|
const optional = config.optional;
|
||||||
|
|
||||||
let adminFinished = false;
|
let adminFinished = false;
|
||||||
let teamleaderFinished = false;
|
let teamleaderFinished = false;
|
||||||
|
|
||||||
if (types.includes("admin")) {
|
if (types.includes("admin")) {
|
||||||
adminFinished = Boolean(await _requireAdminAuthentication({ optional: true, controlled: true })(req, res, next));
|
adminFinished = Boolean(await _requireAdminAuthentication({ optional: true, controlled: true })(req, res, next));
|
||||||
|
|
||||||
if (adminFinished) {
|
if (adminFinished) {
|
||||||
return next();
|
return next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (types.includes("teamleader")) {
|
if (types.includes("teamleader")) {
|
||||||
teamleaderFinished = Boolean(
|
teamleaderFinished = Boolean(
|
||||||
_requireTeamleaderAuthentication({ optional: true, controlled: true })(req, res, next)
|
_requireTeamleaderAuthentication({ optional: true, controlled: true })(req, res, next)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (teamleaderFinished) {
|
if (teamleaderFinished) {
|
||||||
return next();
|
return next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.optional) {
|
if (!config.optional) {
|
||||||
throw new AuthError("No sufficient authorization was provided for this operation");
|
throw new AuthError("No sufficient authorization was provided for this operation");
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
export function requireResponsibleForGroups(auth: AuthJWTPayload | undefined, groupPids: string[] | string) {
|
export function requireResponsibleForGroups(auth: AuthJWTPayload | undefined, groupPids: string[] | string) {
|
||||||
if (auth?.permission_level === "ELEVATED") {
|
if (auth?.permission_level === "ELEVATED") {
|
||||||
@@ -190,7 +190,7 @@ export function requireResponsibleForGroups(auth: AuthJWTPayload | undefined, gr
|
|||||||
throw new AuthError("The provided authorization is not valid for the requested operation!");
|
throw new AuthError("The provided authorization is not valid for the requested operation!");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (auth?.groups.includes(groupPids)) {
|
if (!auth?.groups.includes(groupPids)) {
|
||||||
throw new AuthError("The provided authorization is not valid for the requested operation!");
|
throw new AuthError("The provided authorization is not valid for the requested operation!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const SchemaVersion = z.enum(["1.0"]);
|
|||||||
|
|
||||||
const TimeUnit = z.enum(["days", "hours", "minutes", "seconds", "milliseconds"]);
|
const TimeUnit = z.enum(["days", "hours", "minutes", "seconds", "milliseconds"]);
|
||||||
|
|
||||||
const DurationSchema = z
|
export const DurationSchema = z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal("duration"),
|
type: z.literal("duration"),
|
||||||
min: z.number().int({ message: "min must be an integer (relative to smallestUnit)" }),
|
min: z.number().int({ message: "min must be an integer (relative to smallestUnit)" }),
|
||||||
@@ -17,7 +17,7 @@ const DurationSchema = z
|
|||||||
})
|
})
|
||||||
.refine(({ min, max }) => min < max, { message: "min must be smaller than max" });
|
.refine(({ min, max }) => min < max, { message: "min must be smaller than max" });
|
||||||
|
|
||||||
const PointSchema = z
|
export const PointSchema = z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal("points"),
|
type: z.literal("points"),
|
||||||
min: z.number(),
|
min: z.number(),
|
||||||
|
|||||||
Reference in New Issue
Block a user