diff --git a/package-lock.json b/package-lock.json index ee8a43b8f5571ee129407564515c01a959ee935f..cb959ebd88fee3935a9aa05f9aec5849b9dc0b65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1253,11 +1253,11 @@ "dev": true }, "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", "requires": { - "follow-redirects": "1.5.10" + "follow-redirects": "^1.10.0" } }, "babel-jest": { @@ -2640,27 +2640,9 @@ } }, "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", + "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" }, "for-in": { "version": "1.0.2", diff --git a/package.json b/package.json index 1461ed00f8f099a61d494df8fc45d8c0fe7e0706..c0674e34a95be2e5f1f03a367249d8aad06741dd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "@types/express-session": "^1.17.3", "@types/simple-oauth2": "^2.5.4", - "axios": "^0.19.2", + "axios": "^0.21.1", "body-parser": "^1.19.0", "dotenv": "^8.2.0", "express": "^4.17.1", diff --git a/src/index.ts b/src/index.ts index 0181a400f7831df7b7bec7e6a19aa454a988d008..6d24c07a1577ed37cb7fe5ffd1a93382e3045799 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,18 +1,12 @@ import "./utils/env"; -import { ErrorHandler, handleError } from "./middlewares/utils/ErrorHandler"; import express, { Application, NextFunction, Request, Response } from "express"; -import authRoute from "./routes/auth"; import bodyParser from "body-parser"; -import cardRoute from "./routes/card"; import expressSession from "express-session"; -import fileRoute from "./routes/file"; import mongoose from "mongoose"; import morgan from "morgan"; -import newsRoute from "./routes/news"; -import usersRoute from "./routes/user"; -import warningsRoute from "./routes/warning"; +import setRouters from "./routes/setRouters"; mongoose .connect("mongodb://localhost:27017/bodysch", { @@ -60,26 +54,6 @@ app.use((req: Request, res: Response, next: NextFunction) => { next(); }); -// Register routes - -authRoute(app); - -newsRoute(app); - -usersRoute(app); - -fileRoute(app); - -cardRoute(app); - -warningsRoute(app); - -app.use((err: any, req: Request, res: Response, next: NextFunction) => { - if (err instanceof ErrorHandler) return handleError(err, res); - - //Flush out the stack to the console - console.error(err.stack); - res.status(500).send("Houston, we have a problem!"); -}); +setRouters(app); app.listen(8000, () => console.log(`Example app listening on port 8000!`)); diff --git a/src/middlewares/auth/complete.ts b/src/middlewares/auth/complete.ts index d53beb27813200edd4f230e5dff1282c70cc5268..a9e1c91583d663f741a0a45fd37e79cab93cdd70 100644 --- a/src/middlewares/auth/complete.ts +++ b/src/middlewares/auth/complete.ts @@ -5,13 +5,6 @@ import Profile from "../../models/ProfileSchema"; import { authschResponse } from "../../utils/types/authschResponse"; import axios from "axios"; -/** - * Called after authentication. - * Uses the provided req.query.code from authsch - * to get the logged in Users data. - * If the user is registered, then the Profile object - * id from the database will be added to session.profile.id - */ const complete = () => async ( req: Request, res: Response, @@ -31,17 +24,33 @@ const complete = () => async ( ); req.session!.user = { - external_id: String(response.data.internal_id), + externalId: String(response.data.internal_id), email: String(response.data.mail), name: `${response.data.sn} ${response.data.givenName}`, token, }; const profile = await Profile.findOne({ - external_id: response.data.internal_id, + externalId: response.data.internal_id, }).lean(); - if (profile) req.session!.user.id = profile._id; + if (profile) { + req.session!.user.id = profile._id; + req.session!.user.role = profile.role; + + // Update user data if something changed in authSch + if ( + profile.name != req.session.user.name || + profile.email != req.session.user.email + ) { + await Profile.updateOne( + { externalId: response.data.internal_id }, + { name: req.session.user.name, email: req.session.user.email } + ) + .lean() + .exec(); + } + } return res.redirect(process.env.REDIRECT_URI || "/"); } catch (err) { diff --git a/src/middlewares/auth/isAdmin.ts b/src/middlewares/auth/isAdmin.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9f693f024deed3dbcd5f0a5ebf8b1a00cc39b00 --- /dev/null +++ b/src/middlewares/auth/isAdmin.ts @@ -0,0 +1,13 @@ +import { NextFunction, Request, Response } from "express"; + +import { Role } from "../../models/ProfileSchema"; + +const isAdmin = () => (req: Request, res: Response, next: NextFunction) => { + if (req.session.user?.role == Role.Admin) return next(); + + return res + .status(403) + .json({ message: "You have to be Admin to see this page!" }); +}; + +export default isAdmin; diff --git a/src/middlewares/auth/isAuthenticated.ts b/src/middlewares/auth/isAuthenticated.ts deleted file mode 100644 index 3cbf6fe16bebe21da098c4b65cfc72ae8bce7cb9..0000000000000000000000000000000000000000 --- a/src/middlewares/auth/isAuthenticated.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -/** - * Middleware to check if the user - * is authenticated and registered - */ -const isAuthenticated = () => ( - req: Request, - res: Response, - next: NextFunction -) => { - if (req.session!.user) { - if (req.session!.user.id) next(); - else { - res.status(403); - res.json({ message: "You have to register to see this page!" }); - } - } else { - res.status(401); - res.json({ message: "You have to login to see this page!" }); - } -}; - -export default isAuthenticated; diff --git a/src/middlewares/auth/isLoggedIn.ts b/src/middlewares/auth/isLoggedIn.ts index 713f192cea102e7820f39b42514ab62af6017919..5dc2df3b9feae7f62590dc45f60d90b21bdd3ac7 100644 --- a/src/middlewares/auth/isLoggedIn.ts +++ b/src/middlewares/auth/isLoggedIn.ts @@ -1,9 +1,5 @@ import { NextFunction, Request, Response } from "express"; -/** - * Middleware to check if the user - * is logged in. - */ const isLoggedIn = () => (req: Request, res: Response, next: NextFunction) => { if (req.session!.user) { next(); diff --git a/src/middlewares/auth/isNotRegistered.ts b/src/middlewares/auth/isNotRegistered.ts new file mode 100644 index 0000000000000000000000000000000000000000..c90aad76532c77df7d35441709ce4a8fa404e768 --- /dev/null +++ b/src/middlewares/auth/isNotRegistered.ts @@ -0,0 +1,19 @@ +import { NextFunction, Request, Response } from "express"; + +const isNotRegistered = () => ( + req: Request, + res: Response, + next: NextFunction +) => { + if (req.session!.user) { + if (!req.session!.user.id) return next(); + + return res.status(403).json({ message: "You are already registered!" }); + } + + return res + .status(401) + .json({ message: "You have to login to see this page!" }); +}; + +export default isNotRegistered; diff --git a/src/middlewares/auth/isRegistered.ts b/src/middlewares/auth/isRegistered.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab90fd534a8fef41ff7686f3e482aed5680098c2 --- /dev/null +++ b/src/middlewares/auth/isRegistered.ts @@ -0,0 +1,21 @@ +import { NextFunction, Request, Response } from "express"; + +const isRegistered = () => ( + req: Request, + res: Response, + next: NextFunction +) => { + if (req.session!.user) { + if (req.session!.user.id) return next(); + + return res + .status(403) + .json({ message: "You have to register to see this page!" }); + } + + return res + .status(401) + .json({ message: "You have to login to see this page!" }); +}; + +export default isRegistered; diff --git a/src/middlewares/auth/login.ts b/src/middlewares/auth/login.ts index 15fad1eb38a0b1a28059aed696d8cebea8d9feb9..82a622d0fb8ce9f099334c0b24cccaaebb6569bd 100644 --- a/src/middlewares/auth/login.ts +++ b/src/middlewares/auth/login.ts @@ -5,10 +5,8 @@ const authorizationUri = oauth2().authorizationCode.authorizeURL({ scope: scope, }); -/** - * Redirects to the authorization URL - */ -const login = () => (req: Request, res: Response) => +const login = () => (req: Request, res: Response) => { res.redirect(authorizationUri); +}; export default login; diff --git a/src/middlewares/auth/logout.ts b/src/middlewares/auth/logout.ts index c3a150c09c9e0fc891cbc8a303f47da369007dad..5394a755275a525bb5af511c02581960b469126d 100644 --- a/src/middlewares/auth/logout.ts +++ b/src/middlewares/auth/logout.ts @@ -1,15 +1,14 @@ import { NextFunction, Request, Response } from "express"; -/** - * Logs out the user by destroying the session - */ const logout = () => async ( req: Request, res: Response, next: NextFunction ) => { try { - await req.session!.destroy(() => console.log("user logged out.")); + await req.session!.destroy(() => { + // user logged out, do nothing + }); res.redirect("/"); } catch (err) { next(err); diff --git a/src/middlewares/auth/refreshToken.ts b/src/middlewares/auth/refreshToken.ts index bc7303be1cd5e25317dc21cc71a1cd023781a7d7..0ef3d75223ad939a0287a564d128b77661136b7b 100644 --- a/src/middlewares/auth/refreshToken.ts +++ b/src/middlewares/auth/refreshToken.ts @@ -2,9 +2,6 @@ import { NextFunction, Request, Response } from "express"; import { oauth2 } from "../../utils/auth"; -/** - * Refresh user accessToken - */ const refreshToken = () => async ( req: Request, res: Response, diff --git a/src/middlewares/cards/addCard.ts b/src/middlewares/cards/addCard.ts deleted file mode 100644 index 06c620304a0da9f2de122d126dafcc83f40618a7..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/addCard.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Card from "../../models/CardSchema"; -import { ErrorHandler } from "../utils/ErrorHandler"; -import Profile from "../../models/ProfileSchema"; -import { validateFields } from "../utils/validateFields"; - -const fields = [ - { name: "userId", required: true }, - { name: "expirationDate", required: true }, -]; - -/** - * Middleware that creates a Card - * from the request body and sets - * res.data.card to the created Card. - */ -const addCard = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - // Register - const newCard = new Card(); - - // Validate and set fields from request body - validateFields({ fields, reqBody: req.body }); - fields.forEach((field) => { - const value = req.body[field.name]; - if (value) newCard.set(field.name, req.body[field.name]); - }); - - // Find and remove old card - const oldCard = await Card.findOne({ userId: newCard.userId }) - .lean() - .exec(); - if (oldCard) await Card.deleteOne({ userId: newCard.userId }).lean().exec(); - - const profile = await Profile.findById(newCard.userId).lean().exec(); - - if (!profile) throw new ErrorHandler(400, "User doesn't exist"); - - newCard.fullName = profile!.name; - newCard.roomNumber = profile!.roomNumber; - - await newCard.save(); - res.data.card = newCard; - - next(); - } catch (err) { - next(err); - } -}; - -export default addCard; diff --git a/src/middlewares/cards/getCard.ts b/src/middlewares/cards/getCard.ts deleted file mode 100644 index 58106bdc38f65e1accb4e5bdd2772cc13406f682..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/getCard.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Card from "../../models/CardSchema"; - -/** - * Middleware to find the card with - * id = req.params.id - * and set res.data.card - */ -const getCard = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.card = await Card.findById(req.params.id).lean(); - - next(); - } catch (err) { - next(err); - } -}; - -export default getCard; diff --git a/src/middlewares/cards/getCardList.ts b/src/middlewares/cards/getCardList.ts deleted file mode 100644 index 4c76a26237ea947fb756b6d73ae93f1727044f1f..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/getCardList.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Card from "../../models/CardSchema"; - -/** - * Middleware to find all cards - * and set res.data.cards - */ -const getCardList = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.cards = await Card.find().lean().exec(); - next(); - } catch (err) { - next(err); - } -}; - -export default getCardList; diff --git a/src/middlewares/cards/getCardListValid.ts b/src/middlewares/cards/getCardListValid.ts deleted file mode 100644 index 42b912b725d7dd25f0c340fb36e5be0fca267c9e..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/getCardListValid.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Card from "../../models/CardSchema"; - -/** - * Middleware to find all - * not expired cards - * and set res.data.cards - */ -const getCardListValid = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.cards = await Card.find({ - expirationDate: { $gte: new Date() }, - }) - .lean() - .exec(); - next(); - } catch (err) { - next(err); - } -}; - -export default getCardListValid; diff --git a/src/middlewares/cards/responseCard.ts b/src/middlewares/cards/responseCard.ts deleted file mode 100644 index 32a6cb875306f5d690bbdd43b047cb280ed6f9ad..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/responseCard.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Return the found card from res.data.card - */ -const responseCard = (): any => (req: Request, res: Response) => { - if (!res.data.card) - return res.status(404).json({ message: "Card not found!" }); - - return res.json(res.data.card); -}; - -export default responseCard; diff --git a/src/middlewares/cards/responseCardList.ts b/src/middlewares/cards/responseCardList.ts deleted file mode 100644 index 9a516c153dd57790b142fb896e50b7c32d2dfce2..0000000000000000000000000000000000000000 --- a/src/middlewares/cards/responseCardList.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Return the found card from res.data.cards - */ -const responseCardList = (): any => (req: Request, res: Response) => { - return res.json(res.data.cards); -}; - -export default responseCardList; diff --git a/src/middlewares/example.ts b/src/middlewares/example.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff1579d19e8f9c61dd49174a48ce32771500cfe9 --- /dev/null +++ b/src/middlewares/example.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const example = () => (req: Request, res: Response) => { + res.status(200).json({ message: "Example" }); +}; + +export default example; diff --git a/src/middlewares/files/card/cardImageStorage.ts b/src/middlewares/files/card/cardImageStorage.ts deleted file mode 100644 index f9132f439581ca685bf465a114d719d1e6bc4a2b..0000000000000000000000000000000000000000 --- a/src/middlewares/files/card/cardImageStorage.ts +++ /dev/null @@ -1,18 +0,0 @@ -import multer from "multer"; - -export const cardImageStorage = multer.diskStorage({ - destination: function (req, file, callback) { - callback(null, "uploads/card_image/"); - }, - filename: function (req, file, callback) { - const fileExtension = file.originalname.substring( - file.originalname.lastIndexOf(".") - ); - callback( - null, - Date.now() + "-" + Math.random().toString(36).substring(7) + fileExtension - ); - }, -}); - -export default cardImageStorage; diff --git a/src/middlewares/files/card/getCardImage.ts b/src/middlewares/files/card/getCardImage.ts deleted file mode 100644 index 323292098f719a6ec4e935eab803eee5c0a707e0..0000000000000000000000000000000000000000 --- a/src/middlewares/files/card/getCardImage.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import CardImage from "../../../models/CardImageSchema"; - -/** - * Middleware to get the Entry card background image - * and set res.data.cardImage - */ -const getCardImage = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.cardImage = await CardImage.findOne().lean().exec(); - next(); - } catch (err) { - next(err); - } -}; - -export default getCardImage; diff --git a/src/middlewares/files/card/responseCardImage.ts b/src/middlewares/files/card/responseCardImage.ts deleted file mode 100644 index a5a52bb27733615d26df3f6e048d6b9f580b06c6..0000000000000000000000000000000000000000 --- a/src/middlewares/files/card/responseCardImage.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import File from "../../../models/FileSchema"; - -/** - * Return the Entry card background Image - */ -const responseCardImage = (): any => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - if (!res.data.cardImage) - return res.sendFile("src/utils/card/background.png", { root: "." }); - - const cardImage = await File.findById(res.data.cardImage.imageId) - .lean() - .exec(); - return res.sendFile(cardImage!.path, { root: "." }); - } catch (err) { - next(err); - } -}; - -export default responseCardImage; diff --git a/src/middlewares/files/card/uploadCardImage.ts b/src/middlewares/files/card/uploadCardImage.ts deleted file mode 100644 index 10fe5b55c96f9dff0c592368db1f352a0fe5d1d2..0000000000000000000000000000000000000000 --- a/src/middlewares/files/card/uploadCardImage.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import CardImage from "../../../models/CardImageSchema"; -import File from "../../../models/FileSchema"; -import fs from "fs"; - -/** - * Upload a new Card background Image - */ -const uploadCardImage = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - const oldCardImage = await CardImage.findOne().lean().exec(); - - if (oldCardImage) { - const oldFile = await File.findByIdAndRemove(oldCardImage.imageId) - .lean() - .exec(); - if (oldFile) await fs.unlinkSync(oldFile!.path); - } - - const newFile = new File(); - newFile.originalName = req.file.originalname; - newFile.uploadDate = new Date(); - newFile.path = req.file.path; - newFile.mimeType = req.file.mimetype; - - await newFile.save(); - - if (oldCardImage) { - await CardImage.updateOne( - { _id: oldCardImage?._id }, - { imageId: String(newFile._id) } - ).exec(); - } else { - const cardImage = new CardImage(); - cardImage.imageId = String(newFile._id); - - await cardImage.save(); - } - - return res.status(200).send(); - } catch (err) { - next(err); - } -}; - -export default uploadCardImage; diff --git a/src/middlewares/files/createFile.ts b/src/middlewares/files/createFile.ts new file mode 100644 index 0000000000000000000000000000000000000000..05aec9161b3d70b8e8a5fbd9cfb608b47c0f494d --- /dev/null +++ b/src/middlewares/files/createFile.ts @@ -0,0 +1,38 @@ +import { NextFunction, Request, Response } from "express"; + +import { ErrorHandler } from "../utils/ErrorHandler"; +import File from "../../models/FileSchema"; +import fs from "fs"; + +const createFile = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const newFile = new File(); + + newFile.originalName = req.file.originalname; + newFile.uploadDate = new Date(); + newFile.path = req.file.path; + newFile.mimeType = req.file.mimetype; + try { + await newFile.save(); + } catch (e) { + fs.unlinkSync(req.file.path); + throw new ErrorHandler( + 500, + "There was an error while recording the file in the Database!" + ); + } + + res.data.value = newFile._id; + res.data.newObjectId = newFile._id; + + next(); + } catch (err) { + next(err); + } +}; + +export default createFile; diff --git a/src/middlewares/files/deleteFile.ts b/src/middlewares/files/deleteFile.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e7f084542abca873da4d44c00127ee829a75533 --- /dev/null +++ b/src/middlewares/files/deleteFile.ts @@ -0,0 +1,23 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import fs from "fs"; + +const deleteFile = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + if (!res.data.value) return next(); + + const oldFile = await File.findByIdAndRemove(res.data.value).lean().exec(); + if (oldFile) fs.unlinkSync(oldFile!.path); + + next(); + } catch (err) { + next(err); + } +}; + +export default deleteFile; diff --git a/src/middlewares/files/fileFilter.ts b/src/middlewares/files/fileFilter.ts deleted file mode 100644 index 28c4e66ad3c45e98d5778db42d65be0df4f95968..0000000000000000000000000000000000000000 --- a/src/middlewares/files/fileFilter.ts +++ /dev/null @@ -1,21 +0,0 @@ -import multer, { FileFilterCallback, Multer } from "multer"; - -/** - * multer fileFilter that only lets png or jpeg to be uploaded - * - * @param {Express.Request} req - * @param {Express.Multer.File} file - * @param {FileFilterCallback} cb - */ -export function fileFilter( - req: Express.Request, - file: Express.Multer.File, - cb: FileFilterCallback -) { - if (file.mimetype !== "image/png" && file.mimetype !== "image/jpeg") { - req.fileValidationError = "Invalid file type!"; - return cb(null, false); - } else { - cb(null, true); - } -} diff --git a/src/middlewares/files/handleFileValidationError.ts b/src/middlewares/files/handleFileValidationError.ts deleted file mode 100644 index 0bdf0f873267ba1f3c6fd0e362c828a42654336d..0000000000000000000000000000000000000000 --- a/src/middlewares/files/handleFileValidationError.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -/** - * Middleware that handles errors after uploading a file - */ -const handleFileValidationError = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - if (req.fileValidationError) { - return res.status(400).send(req.fileValidationError); - } - if (!req.file) { - return res.status(400).send("File wasn't provided!"); - } - next(); -}; - -export default handleFileValidationError; diff --git a/src/middlewares/files/imageFilter.ts b/src/middlewares/files/imageFilter.ts new file mode 100644 index 0000000000000000000000000000000000000000..c8e2f40629a9e5f0235d4acfa130bd5993591edb --- /dev/null +++ b/src/middlewares/files/imageFilter.ts @@ -0,0 +1,14 @@ +import { ErrorHandler } from "../utils/ErrorHandler"; +import { FileFilterCallback } from "multer"; + +export function imageFilter( + req: Express.Request, + file: Express.Multer.File, + cb: FileFilterCallback +) { + if (file.mimetype !== "image/png" && file.mimetype !== "image/jpeg") { + return cb(new ErrorHandler(400, "Invalid file type!")); + } else { + cb(null, true); + } +} diff --git a/src/middlewares/files/imageStorage.ts b/src/middlewares/files/imageStorage.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4c4b4a60d83670756ed514698ddc904a1abf27b --- /dev/null +++ b/src/middlewares/files/imageStorage.ts @@ -0,0 +1,22 @@ +import multer from "multer"; + +export const imageStorage = (location: string) => + multer.diskStorage({ + destination: function (req, file, callback) { + callback(null, location); + }, + filename: function (req, file, callback) { + const fileExtension = file.originalname.substring( + file.originalname.lastIndexOf(".") + ); + callback( + null, + Date.now() + + "-" + + Math.random().toString(36).substring(7) + + fileExtension + ); + }, + }); + +export default imageStorage; diff --git a/src/middlewares/files/profile/getProfilePicture.ts b/src/middlewares/files/profile/getProfilePicture.ts deleted file mode 100644 index 660beab170d280d365f7530fe6319e611e9bf865..0000000000000000000000000000000000000000 --- a/src/middlewares/files/profile/getProfilePicture.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import File from "../../../models/FileSchema"; - -/** - * Get Profile picture file from res.data.profile - */ -const getProfilePicture = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - if (!res.data.profile!.pictureId) - return res - .status(400) - .send({ message: "You dont have a profile picture!" }); - - const file = await File.findById(res.data.profile!.pictureId).lean().exec(); - return res.sendFile(file!.path, { root: "." }); - } catch (err) { - next(err); - } -}; - -export default getProfilePicture; diff --git a/src/middlewares/files/profile/profilePictureStorage.ts b/src/middlewares/files/profile/profilePictureStorage.ts deleted file mode 100644 index 3ce61b9ee3e42f73925838a9760492c9c2bb48e1..0000000000000000000000000000000000000000 --- a/src/middlewares/files/profile/profilePictureStorage.ts +++ /dev/null @@ -1,19 +0,0 @@ -import multer from "multer"; -import path from "path"; - -export const profilePictureStorage = multer.diskStorage({ - destination: function (req, file, callback) { - callback(null, "uploads/profile_pictures/"); - }, - filename: function (req, file, callback) { - const fileExtension = file.originalname.substring( - file.originalname.lastIndexOf(".") - ); - callback( - null, - Date.now() + "-" + Math.random().toString(36).substring(7) + fileExtension - ); - }, -}); - -export default profilePictureStorage; diff --git a/src/middlewares/files/profile/uploadProfilePicture.ts b/src/middlewares/files/profile/uploadProfilePicture.ts deleted file mode 100644 index d18667298cf4cde82b167572556ab8c5ed4d0e1c..0000000000000000000000000000000000000000 --- a/src/middlewares/files/profile/uploadProfilePicture.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import { ErrorHandler } from "../../../middlewares/utils/ErrorHandler"; -import File from "../../../models/FileSchema"; -import Profile from "../../../models/ProfileSchema"; -import fs from "fs"; - -/** - * Upload a new profile picture to a given User in res.data.profile - */ -const uploadProfilePicture = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - if (!res.data.profile) throw new ErrorHandler(400, "User not found!"); - if (res.data.profile.pictureId) { - const oldFile = await File.findByIdAndRemove(res.data.profile.pictureId) - .lean() - .exec(); - if (oldFile) await fs.unlinkSync(oldFile!.path); - } - - const newFile = new File(); - - newFile.originalName = req.file.originalname; - newFile.uploadDate = new Date(); - newFile.path = req.file.path; - newFile.mimeType = req.file.mimetype; - - await newFile.save(); - - await Profile.updateOne( - { _id: res.data.profile!._id }, - { pictureId: newFile._id } - ).exec(); - - return res.status(200).send(); - } catch (err) { - next(err); - } -}; - -export default uploadProfilePicture; diff --git a/src/middlewares/files/sendFile.ts b/src/middlewares/files/sendFile.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1aa128a5ff356fbb47e3e60764b3300f13b31ae --- /dev/null +++ b/src/middlewares/files/sendFile.ts @@ -0,0 +1,22 @@ +import { NextFunction, Request, Response } from "express"; + +import { ErrorHandler } from "../utils/ErrorHandler"; +import File from "../../models/FileSchema"; + +const sendFile = (): any => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const file = await File.findById(res.data.value).lean().exec(); + + if (!file) throw new ErrorHandler(404, "File not found!"); + + return res.sendFile(file.path, { root: "." }); + } catch (err) { + next(err); + } +}; + +export default sendFile; diff --git a/src/middlewares/files/card/getUserCard.ts b/src/middlewares/files/sendUserCard.ts similarity index 54% rename from src/middlewares/files/card/getUserCard.ts rename to src/middlewares/files/sendUserCard.ts index e1e35121f6bfeced11f4abab806d3003bb73b5b3..4a98f43d8fc9e85e4baed7fd8e8f98cbf929b21d 100644 --- a/src/middlewares/files/card/getUserCard.ts +++ b/src/middlewares/files/sendUserCard.ts @@ -1,44 +1,39 @@ import { NextFunction, Request, Response } from "express"; -import Card from "../../../models/CardSchema"; -import File from "../../../models/FileSchema"; +import File from "../../models/FileSchema"; import fs from "fs"; -/** - * Generate Entry card svg from Users data and card - */ -const getUserCard = (): any => async ( +const sendUserCard = (): any => async ( req: Request, res: Response, next: NextFunction ) => { try { - const userCard = await Card.findOne({ userId: res.data.profile?._id }) - .lean() - .exec(); - if (!userCard) + if (res.data.term!.members!.length == 0) return res .status(404) - .json({ message: "The user doesn't have any card!" }); + .json({ message: "The given user is not a member" }); let profilePicture = "data:image/png;base64,"; - if (!res.data.profile!.pictureId) { + if (!res.data.profile!.acceptedPicture) { profilePicture += fs.readFileSync( "src/utils/card/profile_picture.png", "base64" ); } else { - const profilePicFile = await File.findById(res.data.profile!.pictureId) + const profilePicFile = await File.findById( + res.data.profile!.acceptedPicture + ) .lean() .exec(); profilePicture += fs.readFileSync(profilePicFile!.path, "base64"); } let bgPicture = "data:image/png;base64,"; - if (!res.data.cardImage) + if (!res.data.term!.backgroundFile) bgPicture += fs.readFileSync("src/utils/card/background.png", "base64"); else { - const bgFile = await File.findById(res.data.cardImage.imageId) + const bgFile = await File.findById(res.data.term!.backgroundFile) .lean() .exec(); bgPicture += fs.readFileSync(bgFile!.path, "base64"); @@ -49,12 +44,15 @@ const getUserCard = (): any => async ( const result = svg .replace(/{{profile_picture}}/g, profilePicture) .replace(/{{background_image}}/g, bgPicture) - .replace(/{{full_name}}/g, String(userCard?.fullName)) - .replace(/{{room_number}}/g, String(userCard?.roomNumber)) - .replace(/{{card_number}}/g, String(userCard?.cardId)) + .replace(/{{full_name}}/g, String(res.data.profile!.name)) + .replace(/{{room_number}}/g, String(res.data.profile!.roomNumber)) + .replace( + /{{card_number}}/g, + String(res.data.term!.members![0].cardNumber) + ) .replace( /{{expiration_date}}/g, - String(userCard?.expirationDate.toISOString().split("T")[0]) + String(res.data.term!.endDate!.toISOString().split("T")[0]) ); return res.type("svg").send(result); @@ -63,4 +61,4 @@ const getUserCard = (): any => async ( } }; -export default getUserCard; +export default sendUserCard; diff --git a/src/middlewares/files/updateProfilePicture.ts b/src/middlewares/files/updateProfilePicture.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7b038947bfa2316c2809d3a934d0f66cea5df96 --- /dev/null +++ b/src/middlewares/files/updateProfilePicture.ts @@ -0,0 +1,31 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Profile from "../../models/ProfileSchema"; +import fs from "fs"; + +const updateProfilePicture = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + if (res.data.profile!.newPicture) { + const oldFile = await File.findByIdAndRemove(res.data.profile!.newPicture) + .lean() + .exec(); + if (oldFile) fs.unlinkSync(oldFile!.path); + } + + await Profile.updateOne( + { _id: res.data.profile!._id }, + { newPicture: res.data.value! } + ).exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default updateProfilePicture; diff --git a/src/middlewares/files/updateTermCardBg.ts b/src/middlewares/files/updateTermCardBg.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f14dfc9de50c4b5377ec1f14aa7b6d51581231c --- /dev/null +++ b/src/middlewares/files/updateTermCardBg.ts @@ -0,0 +1,33 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Term from "../../models/TermSchema"; +import fs from "fs"; + +const updateTermCardBg = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + if (res.data.term!.backgroundFile) { + const oldFile = await File.findByIdAndRemove( + res.data.term!.backgroundFile + ) + .lean() + .exec(); + if (oldFile) fs.unlinkSync(oldFile!.path); + } + + await Term.updateOne( + { _id: res.data.term!._id }, + { backgroundFile: res.data.value! } + ).exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default updateTermCardBg; diff --git a/src/middlewares/news/addNews.ts b/src/middlewares/news/addNews.ts index 4be1fe97e8a6805c8a9059e674e63c6195dd4a9e..bbbed67f1a2797e35a382180800cdf2e43469c47 100644 --- a/src/middlewares/news/addNews.ts +++ b/src/middlewares/news/addNews.ts @@ -1,21 +1,11 @@ +import News, { INews } from "../../models/NewsSchema"; import { NextFunction, Request, Response } from "express"; -import News from "../../models/NewsSchema"; import { validateFields } from "../utils/validateFields"; -const fields = [ - { name: "title", required: true }, - { name: "text", required: true }, -]; - -/** - * Create a New Article - */ -const addNews = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { +const addNews = ( + fields: { name: Partial<keyof INews>; required: boolean }[] +) => async (req: Request, res: Response, next: NextFunction) => { try { const news = new News(); @@ -27,10 +17,11 @@ const addNews = () => async ( }); news.publishedAt = new Date(); + news.publishedBy = req.session.user!.id!; await news.save(); - res.data.newsObject = news; + res.data.newObjectId = news._id; next(); } catch (err) { next(err); diff --git a/src/middlewares/news/deleteNews.ts b/src/middlewares/news/deleteNews.ts index 406b13381c488d42e56e855de82fc5f5a7b4b51d..e9925c9a1047c7b4249d2331fdd3c5c25760f4ef 100644 --- a/src/middlewares/news/deleteNews.ts +++ b/src/middlewares/news/deleteNews.ts @@ -1,6 +1,5 @@ import { NextFunction, Request, Response } from "express"; -import { ErrorHandler } from "../utils/ErrorHandler"; import News from "../../models/NewsSchema"; const deleteNews = () => async ( @@ -9,8 +8,10 @@ const deleteNews = () => async ( next: NextFunction ) => { try { - const news = await News.findByIdAndRemove(req.params.id).lean().exec(); - if (!news) throw new ErrorHandler(404, "News not found!"); + const news = await News.findByIdAndRemove(req.params.newsId) + .select("_id") + .lean() + .exec(); next(); } catch (err) { next(err); diff --git a/src/middlewares/news/getNews.ts b/src/middlewares/news/getNews.ts deleted file mode 100644 index 2f8e60a6473aa2d2c688e65cd8b4c55196c75f8c..0000000000000000000000000000000000000000 --- a/src/middlewares/news/getNews.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import News from "../../models/NewsSchema"; - -/** - * Get one News with id = req.params.id - * and set res.data.newsObject - */ -const getNews = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.newsObject = await News.findById(req.params.id).lean().exec(); - next(); - } catch (err) { - next(err); - } -}; - -export default getNews; diff --git a/src/middlewares/news/getNewsList.ts b/src/middlewares/news/getNewsList.ts index 21b6f71e4d52d7fda3becf97b5bd32433eb4b396..c6b4aabd8b6e517a763e12f730420a44dce5304b 100644 --- a/src/middlewares/news/getNewsList.ts +++ b/src/middlewares/news/getNewsList.ts @@ -2,17 +2,13 @@ import { NextFunction, Request, Response } from "express"; import News from "../../models/NewsSchema"; -/** - * Get all news and set res.data.news - * - */ -const getNewsList = () => async ( +const getNewsList = (selectQuery: string) => async ( req: Request, res: Response, next: NextFunction ) => { try { - res.data.news = await News.find(); + res.data.news = await News.find().select(selectQuery).lean().exec(); next(); } catch (err) { next(err); diff --git a/src/middlewares/news/getOneNews.ts b/src/middlewares/news/getOneNews.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef7de250bdae376950e22c0dc09ea862b1627890 --- /dev/null +++ b/src/middlewares/news/getOneNews.ts @@ -0,0 +1,25 @@ +import { NextFunction, Request, Response } from "express"; + +import News from "../../models/NewsSchema"; + +const getOneNews = (selectQuery: string) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.oneNews = await News.findById(req.params.newsId) + .select(selectQuery) + .lean() + .exec(); + + if (!res.data.oneNews) + return res.status(404).json({ message: "News not found!" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default getOneNews; diff --git a/src/middlewares/news/responseNews.ts b/src/middlewares/news/responseNews.ts index d570a738ba7e7830e0e19dc2a43e0994dd5d5b23..ebea2e333b84a0c99f7a9df8ed5349b4395cc3ec 100644 --- a/src/middlewares/news/responseNews.ts +++ b/src/middlewares/news/responseNews.ts @@ -1,10 +1,7 @@ -import { NextFunction, Request, Response, response } from "express"; +import { Request, Response } from "express"; -/** - * Return the found news from res.data.news - */ const responseNews = () => (req: Request, res: Response) => { - res.json(res.data.news); + res.json(res.data.news || []); }; export default responseNews; diff --git a/src/middlewares/news/responseNewsObject.ts b/src/middlewares/news/responseNewsObject.ts deleted file mode 100644 index abcf8d595e0e04a29b1df9d03d3910c650ae0e95..0000000000000000000000000000000000000000 --- a/src/middlewares/news/responseNewsObject.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NextFunction, Request, Response, response } from "express"; - -/** - * Return the found user from res.data.profile - */ -const responseNewsObject = () => (req: Request, res: Response) => { - if (!res.data.newsObject) { - res.status(404).json({ message: "News not found!" }); - } else { - res.json(res.data.newsObject); - } -}; - -export default responseNewsObject; diff --git a/src/middlewares/news/responseOneNews.ts b/src/middlewares/news/responseOneNews.ts new file mode 100644 index 0000000000000000000000000000000000000000..51d04a05880f7a605537a0bc5fcdb08346325b23 --- /dev/null +++ b/src/middlewares/news/responseOneNews.ts @@ -0,0 +1,11 @@ +import { Request, Response } from "express"; + +const responseOneNews = () => (req: Request, res: Response) => { + if (!res.data.oneNews) { + res.status(404).json({ message: "News not found!" }); + } else { + res.json(res.data.oneNews); + } +}; + +export default responseOneNews; diff --git a/src/middlewares/news/updateNews.ts b/src/middlewares/news/updateNews.ts index 8be12bb9b85310d5818650019d513ab6b0341330..6c5d77c875a65050878ca47bf660392e7bd528b1 100644 --- a/src/middlewares/news/updateNews.ts +++ b/src/middlewares/news/updateNews.ts @@ -1,28 +1,26 @@ import News, { INews } from "../../models/NewsSchema"; import { NextFunction, Request, Response } from "express"; -// Valid fields to update -const validFields = ["title", "text"]; - -/** - * Update a News Object - */ -const updateNews = () => async ( +const updateNews = (fields: Partial<keyof INews>[]) => async ( req: Request, res: Response, next: NextFunction ) => { try { - const news = await News.findById(req.params.id).exec(); + let newFields: Partial<INews> = {}; + fields.forEach((field) => { + const value = req.body[field]; + if (value) newFields[field] = value; + }); + newFields["updatedBy"] = req.session.user!.id; - if (news) { - validFields.forEach((field) => { - const value = req.body[field]; - if (value) news.set(field, value); - }); - await news.save(); - } - res.data.newsObject = news; + await News.updateOne( + { _id: req.params.newsId }, + { $set: newFields }, + { upsert: true, runValidators: true } + ) + .lean() + .exec(); next(); } catch (err) { diff --git a/src/middlewares/term/addMember.ts b/src/middlewares/term/addMember.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee148f2ac9177085d868607515bb52c77375aa6f --- /dev/null +++ b/src/middlewares/term/addMember.ts @@ -0,0 +1,41 @@ +import { NextFunction, Request, Response } from "express"; +import Term, { CardState, IMember, MemberState } from "../../models/TermSchema"; + +import Counter from "../../models/CounterSchema"; + +const addMember = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const counter = await Counter.findByIdAndUpdate( + { _id: "cardId" }, + { $inc: { seq: 1 } }, + { upsert: true, new: true } + ) + .lean() + .exec(); + + const member: IMember = { + user: res.data.profile!._id, + memberState: MemberState.Applied, + cardState: CardState.Created, + cardNumber: Number(counter.seq), + }; + + await Term.updateOne( + { _id: res.data.term!._id }, + { $push: { members: member } }, + { runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default addMember; diff --git a/src/middlewares/term/addTerm.ts b/src/middlewares/term/addTerm.ts new file mode 100644 index 0000000000000000000000000000000000000000..542cdb0cde2397b3dd366dc702e5b1221cb3f1a7 --- /dev/null +++ b/src/middlewares/term/addTerm.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, Response } from "express"; +import Term, { ITerm } from "../../models/TermSchema"; + +import { validateFields } from "../utils/validateFields"; + +const addTerm = ( + fields: { name: Partial<keyof ITerm>; required: boolean }[] +) => async (req: Request, res: Response, next: NextFunction) => { + try { + const term = new Term(); + + // Validate and set fields from request body + validateFields({ fields, reqBody: req.body }); + fields.forEach((field) => { + const value = req.body[field.name]; + if (value) term.set(field.name, req.body[field.name]); + }); + term.createDate = new Date(); + await term.save(); + + res.data.newObjectId = term._id; + next(); + } catch (err) { + next(err); + } +}; + +export default addTerm; diff --git a/src/middlewares/term/deleteTerm.ts b/src/middlewares/term/deleteTerm.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3233d6d69e63b3e6fed8b811996d6f7f591767e --- /dev/null +++ b/src/middlewares/term/deleteTerm.ts @@ -0,0 +1,25 @@ +import { NextFunction, Request, Response } from "express"; + +import File from "../../models/FileSchema"; +import Term from "../../models/TermSchema"; + +const deleteTerm = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const removedTerm = await Term.findByIdAndDelete(req.params.termId) + .select("_id backgroundFile") + .lean() + .exec(); + if (!removedTerm) + return res.status(404).json({ message: "Term not found!" }); + await File.findByIdAndDelete(removedTerm?.backgroundFile); + next(); + } catch (err) { + next(err); + } +}; + +export default deleteTerm; diff --git a/src/middlewares/term/getTerm.ts b/src/middlewares/term/getTerm.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d9af82a58d22f21cdd826cef63b9ddb03fe1866 --- /dev/null +++ b/src/middlewares/term/getTerm.ts @@ -0,0 +1,39 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; + +/** + * termId -> term + */ +const getTerm = (sendBgFile = false) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.term = await Term.findOne( + { _id: req.params.termId }, + { + _id: 1, + name: 1, + startDate: 1, + endDate: 1, + deadline: 1, + createDate: 1, + backgroundFile: sendBgFile ? 1 : 0, + members: { $elemMatch: { user: req.params.userId } }, + } + ) + .lean() + .exec(); + + if (!res.data.term) + return res.status(404).json({ message: "Term not found" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default getTerm; diff --git a/src/middlewares/term/getTermMembers.ts b/src/middlewares/term/getTermMembers.ts new file mode 100644 index 0000000000000000000000000000000000000000..79ffe98c6c88a69005b29c5f9998d86e499d7874 --- /dev/null +++ b/src/middlewares/term/getTermMembers.ts @@ -0,0 +1,31 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; + +const getTermMembers = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const term = await Term.findOne( + { _id: req.params.termId }, + { + members: 1, + } + ) + .populate("members.user", { name: 1, warnings: { $size: "$warnings" } }) + .lean() + .exec(); + + if (!term) return res.status(404).json({ message: "Term not found" }); + + res.data.members = term?.members; + + next(); + } catch (err) { + next(err); + } +}; + +export default getTermMembers; diff --git a/src/middlewares/term/getTermWithQuery.ts b/src/middlewares/term/getTermWithQuery.ts new file mode 100644 index 0000000000000000000000000000000000000000..561e30417c5d1564f3c07972748a0e89530b2878 --- /dev/null +++ b/src/middlewares/term/getTermWithQuery.ts @@ -0,0 +1,25 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; + +const getTermWithQuery = (selectQuery: string) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.term = await Term.findById(req.params.termId) + .select(selectQuery) + .lean() + .exec(); + + if (!res.data.term) + return res.status(404).json({ message: "Term not found" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default getTermWithQuery; diff --git a/src/middlewares/term/getTermsList.ts b/src/middlewares/term/getTermsList.ts new file mode 100644 index 0000000000000000000000000000000000000000..c916feeed5fde205e13fa34048a87d62a14448b6 --- /dev/null +++ b/src/middlewares/term/getTermsList.ts @@ -0,0 +1,32 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; + +const getTermsList = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.terms = await Term.find( + {}, + { + _id: 1, + name: 1, + startDate: 1, + endDate: 1, + deadline: 1, + createDate: 1, + members: { $elemMatch: { user: req.params.userId } }, + } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default getTermsList; diff --git a/src/middlewares/term/getUserTermMemberships.ts b/src/middlewares/term/getUserTermMemberships.ts new file mode 100644 index 0000000000000000000000000000000000000000..49868f370ba4255d39518111752576f068382b18 --- /dev/null +++ b/src/middlewares/term/getUserTermMemberships.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, Response } from "express"; + +import Term from "../../models/TermSchema"; + +const getUserTermMemberships = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.terms = await Term.find( + { "members.user": req.params.userId }, + { + _id: 1, + name: 1, + members: { $elemMatch: { user: req.params.userId } }, + } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default getUserTermMemberships; diff --git a/src/middlewares/term/isNotMember.ts b/src/middlewares/term/isNotMember.ts new file mode 100644 index 0000000000000000000000000000000000000000..903262194dace009d6872dc4cf6bc5ecb0c99565 --- /dev/null +++ b/src/middlewares/term/isNotMember.ts @@ -0,0 +1,21 @@ +import { NextFunction, Request, Response } from "express"; + +const isNotMember = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + res.data.member = res.data.term?.members?.find( + (member) => member.user == req.params.userId + ); + if (res.data.member) + return res.status(400).json({ message: "Already a member!" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default isNotMember; diff --git a/src/middlewares/term/responseMembersList.ts b/src/middlewares/term/responseMembersList.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b2454844a5b67d158011ffa407c721b49435303 --- /dev/null +++ b/src/middlewares/term/responseMembersList.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseMembersList = () => (req: Request, res: Response) => { + return res.json(res.data.members); +}; + +export default responseMembersList; diff --git a/src/middlewares/term/responseTerm.ts b/src/middlewares/term/responseTerm.ts new file mode 100644 index 0000000000000000000000000000000000000000..d178cc7a7ad21e2e28a3b4213871ec72c929746c --- /dev/null +++ b/src/middlewares/term/responseTerm.ts @@ -0,0 +1,10 @@ +import { Request, Response } from "express"; + +const responseTerm = () => (req: Request, res: Response) => { + if (!res.data.term) + return res.status(404).json({ message: "Term not found!" }); + + return res.json(res.data.term); +}; + +export default responseTerm; diff --git a/src/middlewares/term/responseTermsList.ts b/src/middlewares/term/responseTermsList.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c478a3100d25d2d0d485604a94ada65dc1d59a7 --- /dev/null +++ b/src/middlewares/term/responseTermsList.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseTermsList = () => (req: Request, res: Response) => { + return res.json(res.data.terms || []); +}; + +export default responseTermsList; diff --git a/src/middlewares/term/updateTerm.ts b/src/middlewares/term/updateTerm.ts new file mode 100644 index 0000000000000000000000000000000000000000..8187677774c730d6d1518acd123e94984840c868 --- /dev/null +++ b/src/middlewares/term/updateTerm.ts @@ -0,0 +1,30 @@ +import { NextFunction, Request, Response } from "express"; +import Term, { ITerm } from "../../models/TermSchema"; + +const updateTerm = (fields: Partial<keyof ITerm>[]) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + let newFields: Partial<ITerm> = {}; + fields.forEach((field) => { + const value = req.body[field]; + if (value) newFields[field] = value; + }); + + await Term.updateOne( + { _id: req.params.termId }, + { $set: newFields }, + { upsert: true, runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default updateTerm; diff --git a/src/middlewares/term/updateTermMember.ts b/src/middlewares/term/updateTermMember.ts new file mode 100644 index 0000000000000000000000000000000000000000..da560d8b7c5424b3eb84d64947a27f9b8a09a40a --- /dev/null +++ b/src/middlewares/term/updateTermMember.ts @@ -0,0 +1,30 @@ +import { NextFunction, Request, Response } from "express"; +import Term, { IMember } from "../../models/TermSchema"; + +const updateTermMember = (fields: Partial<keyof IMember>[]) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + let newFields: Partial<any> = {}; + fields.forEach((field) => { + const value = req.body[field]; + if (value) newFields["members.$." + field] = value; + }); + + await Term.updateOne( + { _id: req.params.termId, "members.user": req.params.userId }, + { $set: newFields }, + { runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default updateTermMember; diff --git a/src/middlewares/user/acceptPicture.ts b/src/middlewares/user/acceptPicture.ts new file mode 100644 index 0000000000000000000000000000000000000000..182e8539aa9606d17bbf410c7dc56eb09cc7e9dc --- /dev/null +++ b/src/middlewares/user/acceptPicture.ts @@ -0,0 +1,37 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../models/ProfileSchema"; + +const acceptPicture = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + if (!res.data.profile?.newPicture) + return res.status(400).json({ + message: "The User doesn't have any unaccepted Profile picture!", + }); + + const newPic = res.data.profile.newPicture; + // TODO Notification + await Profile.updateOne( + { _id: req.params.userId }, + { + $set: { + newPicture: undefined, + acceptedPicture: newPic, + }, + }, + { upsert: true, runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default acceptPicture; diff --git a/src/middlewares/user/deleteUser.ts b/src/middlewares/user/deleteUser.ts index 00b61114dbd328f9dae3e3cc57257ea272b96292..1bd4a702c6f40daba33c03b0772d05e25b6ea5f8 100644 --- a/src/middlewares/user/deleteUser.ts +++ b/src/middlewares/user/deleteUser.ts @@ -2,16 +2,14 @@ import { NextFunction, Request, Response } from "express"; import Profile from "../../models/ProfileSchema"; -/** - * Middleware to delete User where id = req.params.id - */ +// TODO Delete from Terms const deleteUser = () => async ( req: Request, res: Response, next: NextFunction ) => { try { - await Profile.findByIdAndDelete(req.params.id); + await Profile.findByIdAndDelete(req.params.userId).lean().exec(); next(); } catch (err) { next(err); diff --git a/src/middlewares/user/getUser.ts b/src/middlewares/user/getUser.ts index 079233a6c62ff771d3d51b790bc903d7e6231999..8c1713ad4e35c703c592d171e1b69a95742b42b1 100644 --- a/src/middlewares/user/getUser.ts +++ b/src/middlewares/user/getUser.ts @@ -1,20 +1,22 @@ import { NextFunction, Request, Response } from "express"; -import { IWarning } from "../../models/WarningSchema"; import Profile from "../../models/ProfileSchema"; -import Warning from "../../models/WarningSchema"; -/** - * Middleware to get a User by req.params.id - * and set res.data.profile - */ -const getUser = () => async ( +// TODO remove from Term members +const getUser = (selectQuery: string) => async ( req: Request, res: Response, next: NextFunction ) => { try { - res.data.profile = await Profile.findById(req.params.id).lean().exec(); + res.data.profile = await Profile.findById(req.params.userId) + .select(selectQuery) + .lean() + .exec(); + + if (!res.data.profile) + return res.status(404).json({ message: "User not found" }); + next(); } catch (err) { next(err); diff --git a/src/middlewares/user/getUsersList.ts b/src/middlewares/user/getUsersList.ts index 3cc3e4cb1a91a0670b701bf412aa4fa872757b7a..cc1107e2efaad3bd09b0dee7390ed71c55006910 100644 --- a/src/middlewares/user/getUsersList.ts +++ b/src/middlewares/user/getUsersList.ts @@ -1,14 +1,17 @@ import { NextFunction, Request, Response } from "express"; +import Profile, { IProfile } from "../../models/ProfileSchema"; -import Profile from "../../models/ProfileSchema"; +import { FilterQuery } from "mongoose"; -const getUsersList = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { +const getUsersList = ( + selectQuery: string, + filterQuery: FilterQuery<IProfile> = {} +) => async (req: Request, res: Response, next: NextFunction) => { try { - res.data.profiles = await Profile.find(); + res.data.profiles = await Profile.find(filterQuery) + .select(selectQuery) + .lean() + .exec(); next(); } catch (err) { next(err); diff --git a/src/middlewares/user/notice/addNoticeToUser.ts b/src/middlewares/user/notice/addNoticeToUser.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d46e29eda04863fd899f64f24805eccf865dd2d --- /dev/null +++ b/src/middlewares/user/notice/addNoticeToUser.ts @@ -0,0 +1,34 @@ +import { NextFunction, Request, Response } from "express"; +import Profile, { INotice } from "../../../models/ProfileSchema"; + +import { Types } from "mongoose"; +import { validateFields } from "../../utils/validateFields"; + +const addNoticeToUser = ( + fields: { name: Partial<keyof INotice>; required: boolean }[] +) => async (req: Request, res: Response, next: NextFunction) => { + try { + validateFields({ fields, reqBody: req.body }); + + const notice: INotice = { + text: req.body.text, + redirect: req.body.redirect || "", + isSeen: false, + _id: String(new Types.ObjectId()), + }; + + await Profile.updateOne( + { _id: req.params.userId }, + { $push: { notices: notice } }, + { runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default addNoticeToUser; diff --git a/src/middlewares/user/notice/deleteNotice.ts b/src/middlewares/user/notice/deleteNotice.ts new file mode 100644 index 0000000000000000000000000000000000000000..a820add2e0bd123d2567f8b7644c7288f4ee66d1 --- /dev/null +++ b/src/middlewares/user/notice/deleteNotice.ts @@ -0,0 +1,24 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../../models/ProfileSchema"; + +const deleteNotice = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + await Profile.updateOne( + { _id: req.params.userId }, + { $pull: { notices: { _id: req.params.noticeId } } } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default deleteNotice; diff --git a/src/middlewares/user/notice/getUserNoticeNum.ts b/src/middlewares/user/notice/getUserNoticeNum.ts new file mode 100644 index 0000000000000000000000000000000000000000..516168180b4088ef8ea392ccd9b23e4163b5f8c6 --- /dev/null +++ b/src/middlewares/user/notice/getUserNoticeNum.ts @@ -0,0 +1,30 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../../models/ProfileSchema"; + +const getUserNoticeNum = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const profile = await Profile.findOne( + { _id: req.params.userId }, + { + noticesNum: { $size: { $ifNull: ["$notices", []] } }, + } + ) + .lean() + .exec(); + + if (!profile) return res.status(404).json({ message: "Profile not found" }); + + res.data.number = profile!.noticesNum; + + next(); + } catch (err) { + next(err); + } +}; + +export default getUserNoticeNum; diff --git a/src/middlewares/user/notice/getUserNotices.ts b/src/middlewares/user/notice/getUserNotices.ts new file mode 100644 index 0000000000000000000000000000000000000000..5907fe27a6b0a67ec3c379beca06929a528f9121 --- /dev/null +++ b/src/middlewares/user/notice/getUserNotices.ts @@ -0,0 +1,33 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../../models/ProfileSchema"; + +const getUserNotices = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const profile = await Profile.findOne( + { _id: req.params.userId }, + { + "notices._id": 1, + "notices.text": 1, + "notices.redirect": 1, + "notices.isSeen": 1, + } + ) + .lean() + .exec(); + + if (!profile) return res.status(404).json({ message: "Profile not found" }); + + res.data.notices = profile.notices; + + next(); + } catch (err) { + next(err); + } +}; + +export default getUserNotices; diff --git a/src/middlewares/user/notice/markNoticeAsSeen.ts b/src/middlewares/user/notice/markNoticeAsSeen.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd322d0700bbd27e1f4c29ac7c22daa1941f89a6 --- /dev/null +++ b/src/middlewares/user/notice/markNoticeAsSeen.ts @@ -0,0 +1,24 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../../models/ProfileSchema"; + +const markNoticeAsSeen = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + await Profile.updateOne( + { _id: req.params.userId, "notices._id": req.params.noticeId }, + { $set: { "notices.$.isSeen": true } } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default markNoticeAsSeen; diff --git a/src/middlewares/user/notice/responseNoticesList.ts b/src/middlewares/user/notice/responseNoticesList.ts new file mode 100644 index 0000000000000000000000000000000000000000..8de13450e82105d84dd619d73b5fbf96342552aa --- /dev/null +++ b/src/middlewares/user/notice/responseNoticesList.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseNoticesList = () => (req: Request, res: Response) => { + res.json(res.data.notices || []); +}; + +export default responseNoticesList; diff --git a/src/middlewares/user/notice/responseNoticesNum.ts b/src/middlewares/user/notice/responseNoticesNum.ts new file mode 100644 index 0000000000000000000000000000000000000000..e052ef2c1bc1c178d7ec1916648f43f2c3f6b334 --- /dev/null +++ b/src/middlewares/user/notice/responseNoticesNum.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseNoticesNum = () => (req: Request, res: Response) => { + res.json({ noticesNum: res.data.number || 0 }); +}; + +export default responseNoticesNum; diff --git a/src/middlewares/user/addUser.ts b/src/middlewares/user/registerOwnUser.ts similarity index 52% rename from src/middlewares/user/addUser.ts rename to src/middlewares/user/registerOwnUser.ts index a70507a722f7225b9a22288e498306aed43356c8..a69bb43db10c504019294bb3ca4bc4ec4abcaf33 100644 --- a/src/middlewares/user/addUser.ts +++ b/src/middlewares/user/registerOwnUser.ts @@ -1,32 +1,19 @@ import { NextFunction, Request, Response } from "express"; -import Profile, { IProfile, Role } from "../../models/ProfileSchema"; +import Profile from "../../models/ProfileSchema"; import { validateFields } from "../utils/validateFields"; const fields = [ { name: "studentCardNumber", required: true }, { name: "roomNumber", required: true }, - { name: "picture", required: false }, ]; -/** - * Middleware to create own User if doesn't exist - * and set res.data.profile to it - */ -const addUser = () => async ( +const registerOwnUser = () => async ( req: Request, res: Response, next: NextFunction ) => { try { - // Already registered - if (req.session?.user?.id) { - res.data.profile = await Profile.findById(req.session.user.id) - .lean() - .exec(); - return next(); - } - // Register const newProfile = new Profile(); @@ -37,18 +24,19 @@ const addUser = () => async ( if (value) newProfile.set(field.name, req.body[field.name]); }); - newProfile.external_id = req.session!.user!.external_id; - newProfile.email = String(req.session?.user?.email); - newProfile.name = String(req.session?.user?.name); + newProfile.externalId = req.session.user!.externalId; + newProfile.email = String(req.session.user!.email); + newProfile.name = String(req.session.user!.name); await newProfile.save(); - res.data.profile = newProfile; + res.data.newObjectId = newProfile._id; req.session!.user!.id = newProfile._id; + req.session!.user!.role = newProfile.role; next(); } catch (err) { next(err); } }; -export default addUser; +export default registerOwnUser; diff --git a/src/middlewares/user/rejectPicture.ts b/src/middlewares/user/rejectPicture.ts new file mode 100644 index 0000000000000000000000000000000000000000..606161f1a876dd8096bad408823f8600d7ad3d0a --- /dev/null +++ b/src/middlewares/user/rejectPicture.ts @@ -0,0 +1,30 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../models/ProfileSchema"; + +const rejectPicture = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + // TODO Notification + await Profile.updateOne( + { _id: req.params.userId }, + { + $set: { + picture: undefined, + }, + }, + { upsert: true, runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default rejectPicture; diff --git a/src/middlewares/user/responseUser.ts b/src/middlewares/user/responseUser.ts index 7df3e826930cc39fc6090cfcbc55d8bc4b63a416..0cb05e5ec04cf3a05bf8ece9d0b071be01d3f650 100644 --- a/src/middlewares/user/responseUser.ts +++ b/src/middlewares/user/responseUser.ts @@ -1,8 +1,5 @@ -import { NextFunction, Request, Response, response } from "express"; +import { Request, Response } from "express"; -/** - * Return the found user from res.data.profile - */ const responseUser = () => (req: Request, res: Response) => { if (!res.data.profile) { res.status(404).json({ message: "User not found!" }); diff --git a/src/middlewares/user/responseUserList.ts b/src/middlewares/user/responseUserList.ts deleted file mode 100644 index cdab625ef8f214eea31704eb76152bc56dce571e..0000000000000000000000000000000000000000 --- a/src/middlewares/user/responseUserList.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NextFunction, Request, Response, response } from "express"; - -/** - * Return the found users from res.data.profiles - */ -const responseUserList = () => (req: Request, res: Response) => { - res.json(res.data.profiles); -}; - -export default responseUserList; diff --git a/src/middlewares/user/responseUsersList.ts b/src/middlewares/user/responseUsersList.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e8c2b3044efba8c07adf37356cd943b8430f0c1 --- /dev/null +++ b/src/middlewares/user/responseUsersList.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseUsersList = () => (req: Request, res: Response) => { + res.json(res.data.profiles || []); +}; + +export default responseUsersList; diff --git a/src/middlewares/user/setOwnUserId.ts b/src/middlewares/user/setOwnUserId.ts index 160663fc53881f399acb1f85b42523665a539030..ef20e229305b6a322d6575ece2f5c378b25e8731 100644 --- a/src/middlewares/user/setOwnUserId.ts +++ b/src/middlewares/user/setOwnUserId.ts @@ -1,19 +1,11 @@ import { NextFunction, Request, Response } from "express"; -import Profile, { IProfile } from "../../models/ProfileSchema"; -import { IWarning } from "../../models/WarningSchema"; -import Warning from "../../models/WarningSchema"; - -/** - * Middleware to set req.params.id to the current users id. - * getUser() middleware should be called after this. - */ const setOwnUserId = () => ( req: Request, res: Response, next: NextFunction ) => { - req.params.id = req.session.user!.id!; + req.params.userId = req.session.user!.id!; next(); }; export default setOwnUserId; diff --git a/src/middlewares/user/updateUser.ts b/src/middlewares/user/updateUser.ts index d0c8d98048778f3e746e2f66fd9a065465165706..ff146d905d15a3e272a6dafdea9a6351f71f8834 100644 --- a/src/middlewares/user/updateUser.ts +++ b/src/middlewares/user/updateUser.ts @@ -1,29 +1,25 @@ import { NextFunction, Request, Response } from "express"; +import Profile, { IProfile } from "../../models/ProfileSchema"; -import Profile from "../../models/ProfileSchema"; - -// Valid fields to update -const validFields = ["studentCardNumber", "roomNumber", "picture"]; - -/** - * Update a user - */ -const updateUser = () => async ( +const updateUser = (fields: Partial<keyof IProfile>[]) => async ( req: Request, res: Response, next: NextFunction ) => { try { - const profile = await Profile.findById(req.params.id).exec(); + let newFields: Partial<IProfile> = {}; + fields.forEach((field) => { + const value = req.body[field]; + if (value) newFields[field] = value; + }); - if (profile) { - validFields.forEach((field) => { - const value = req.body[field]; - if (value) profile.set(field, value); - }); - await profile.save(); - } - res.data.profile = profile; + await Profile.updateOne( + { _id: req.params.userId }, + { $set: newFields }, + { upsert: true, runValidators: true } + ) + .lean() + .exec(); next(); } catch (err) { diff --git a/src/middlewares/user/userIsStaff.ts b/src/middlewares/user/userIsStaff.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd730d7b30b49007145dfa262d4066ad3430bb54 --- /dev/null +++ b/src/middlewares/user/userIsStaff.ts @@ -0,0 +1,18 @@ +import { NextFunction, Request, Response } from "express"; + +const userIsStaff = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + if (!res.data.profile!.isStaffMember) + return res.status(400).json({ message: "User is not a staff member!" }); + + next(); + } catch (err) { + next(err); + } +}; + +export default userIsStaff; diff --git a/src/middlewares/user/warning/addWarning.ts b/src/middlewares/user/warning/addWarning.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9b9858995a8963cbf931daf6129318593ccbf62 --- /dev/null +++ b/src/middlewares/user/warning/addWarning.ts @@ -0,0 +1,34 @@ +import { NextFunction, Request, Response } from "express"; +import Profile, { IWarning } from "../../../models/ProfileSchema"; + +import { Types } from "mongoose"; +import { validateFields } from "../../utils/validateFields"; + +const addWarning = ( + fields: { name: Partial<keyof IWarning>; required: boolean }[] +) => async (req: Request, res: Response, next: NextFunction) => { + try { + validateFields({ fields, reqBody: req.body }); + + const warning: IWarning = { + text: req.body.text, + date: new Date(), + givenBy: String(req.session.user!.id), + _id: String(new Types.ObjectId()), + }; + + await Profile.updateOne( + { _id: req.params.userId }, + { $push: { warnings: warning } }, + { runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default addWarning; diff --git a/src/middlewares/warning/deleteWarning.ts b/src/middlewares/user/warning/deleteWarning.ts similarity index 51% rename from src/middlewares/warning/deleteWarning.ts rename to src/middlewares/user/warning/deleteWarning.ts index 6d1b633809db11a55a779f8fa2d95c7291a942fe..49d613be180ce61b21359803aa522f279b89c54f 100644 --- a/src/middlewares/warning/deleteWarning.ts +++ b/src/middlewares/user/warning/deleteWarning.ts @@ -1,7 +1,6 @@ import { NextFunction, Request, Response } from "express"; -import Profile from "../../models/ProfileSchema"; -import Warning from "../../models/WarningSchema"; +import Profile from "../../../models/ProfileSchema"; const deleteWarning = () => async ( req: Request, @@ -9,12 +8,13 @@ const deleteWarning = () => async ( next: NextFunction ) => { try { - const warning = await Warning.findByIdAndRemove(req.params.warningId); - await Profile.updateOne( - { _id: warning?.receiver }, - { $pull: { warningIds: warning?._id } } - ); + { _id: req.params.userId }, + { $pull: { warnings: { _id: req.params.warningId } } } + ) + .lean() + .exec(); + next(); } catch (err) { next(err); diff --git a/src/middlewares/user/warning/getUserWarningsList.ts b/src/middlewares/user/warning/getUserWarningsList.ts new file mode 100644 index 0000000000000000000000000000000000000000..80945dc68920ecc13b49a70d1a08fcccacbbf4a2 --- /dev/null +++ b/src/middlewares/user/warning/getUserWarningsList.ts @@ -0,0 +1,32 @@ +import { NextFunction, Request, Response } from "express"; + +import Profile from "../../../models/ProfileSchema"; + +const getUserWarnings = () => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const profile = await Profile.findOne( + { _id: req.params.userId }, + { + "warnings._id": 1, + "warnings.text": 1, + "warnings.date": 1, + } + ) + .lean() + .exec(); + + if (!profile) return res.status(404).json({ message: "Profile not found" }); + + res.data.warnings = profile.warnings; + + next(); + } catch (err) { + next(err); + } +}; + +export default getUserWarnings; diff --git a/src/middlewares/user/warning/responseWarningsList.ts b/src/middlewares/user/warning/responseWarningsList.ts new file mode 100644 index 0000000000000000000000000000000000000000..0fad27db1b1b8ea6432f55b1b5b0badbcdb303d8 --- /dev/null +++ b/src/middlewares/user/warning/responseWarningsList.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const responseWarningsList = () => (req: Request, res: Response) => { + res.json(res.data.warnings || []); +}; + +export default responseWarningsList; diff --git a/src/middlewares/user/warning/updateWarning.ts b/src/middlewares/user/warning/updateWarning.ts new file mode 100644 index 0000000000000000000000000000000000000000..b75286acfb753ab86e7913c362c9d007c70bfd10 --- /dev/null +++ b/src/middlewares/user/warning/updateWarning.ts @@ -0,0 +1,30 @@ +import { NextFunction, Request, Response } from "express"; +import Profile, { IWarning } from "../../../models/ProfileSchema"; + +const updateWarning = (fields: Partial<keyof IWarning>[]) => async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + let newFields: Partial<any> = {}; + fields.forEach((field) => { + const value = req.body[field]; + if (value) newFields["warnings.$." + field] = value; + }); + + await Profile.updateOne( + { _id: req.params.userId, "warnings._id": req.params.warningId }, + { $set: newFields }, + { runValidators: true } + ) + .lean() + .exec(); + + next(); + } catch (err) { + next(err); + } +}; + +export default updateWarning; diff --git a/src/middlewares/utils/createdEmptyResponse.ts b/src/middlewares/utils/createdEmptyResponse.ts new file mode 100644 index 0000000000000000000000000000000000000000..13cf0f4829696ff44fdea75466742d8daa7d934f --- /dev/null +++ b/src/middlewares/utils/createdEmptyResponse.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const createdEmptyResponse = () => (req: Request, res: Response) => { + res.status(201).json({}); +}; + +export default createdEmptyResponse; diff --git a/src/middlewares/utils/createdResponse.ts b/src/middlewares/utils/createdResponse.ts new file mode 100644 index 0000000000000000000000000000000000000000..88c19ebb150c3eef40cb6c69054762ba9fbfd64c --- /dev/null +++ b/src/middlewares/utils/createdResponse.ts @@ -0,0 +1,7 @@ +import { Request, Response } from "express"; + +const createdResponse = () => (req: Request, res: Response) => { + res.status(201).json({ _id: res.data.newObjectId }); +}; + +export default createdResponse; diff --git a/src/middlewares/utils/emptyResponse.ts b/src/middlewares/utils/emptyResponse.ts deleted file mode 100644 index 73508b7e4b90888ce05ebca11ca2c16aba63aa69..0000000000000000000000000000000000000000 --- a/src/middlewares/utils/emptyResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Empty response - */ -const emptyResponse = () => (req: Request, res: Response) => res.json({}); - -export default emptyResponse; diff --git a/src/middlewares/utils/getFieldValue.ts b/src/middlewares/utils/getFieldValue.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2e90a765a768d5731db88cc3e3077e8b3366d40 --- /dev/null +++ b/src/middlewares/utils/getFieldValue.ts @@ -0,0 +1,19 @@ +import { NextFunction, Request, Response } from "express"; + +const getFieldValue = ( + objectName: "profile" | "term", + fieldName: "newPicture" | "acceptedPicture" | "backgroundFile" +) => async (req: Request, res: Response, next: NextFunction) => { + try { + const object: Record<string, any> = res.data[objectName]!; + if (object) { + res.data.value = object[fieldName]; + } + + next(); + } catch (err) { + next(err); + } +}; + +export default getFieldValue; diff --git a/src/middlewares/utils/noContentResponse.ts b/src/middlewares/utils/noContentResponse.ts new file mode 100644 index 0000000000000000000000000000000000000000..091a60442a3d31d0e1a05a8121420b5f3f6310f2 --- /dev/null +++ b/src/middlewares/utils/noContentResponse.ts @@ -0,0 +1,6 @@ +import { Request, Response } from "express"; + +const noContentResponse = () => (req: Request, res: Response) => + res.status(204).send(); + +export default noContentResponse; diff --git a/src/middlewares/warning/addWarning.ts b/src/middlewares/warning/addWarning.ts deleted file mode 100644 index 58704d6448ce99b52b25be255b080069e39e3d4d..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/addWarning.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import { ErrorHandler } from "../utils/ErrorHandler"; -import Profile from "../../models/ProfileSchema"; -import Warning from "../../models/WarningSchema"; -import { validateFields } from "../utils/validateFields"; -import warningsRoute from "../../routes/warning"; - -const fields = [{ name: "text", required: true }]; - -const addWarning = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - const warningReceiver = await Profile.findById(req.params.id).exec(); - if (!warningReceiver) throw new ErrorHandler(404, "User not found!"); - - const warning = new Warning(); - - // Validate and set fields from request body - validateFields({ fields, reqBody: req.body }); - fields.forEach((field) => { - const value = req.body[field.name]; - if (value) warning.set(field.name, req.body[field.name]); - }); - - warning.receiver = warningReceiver._id!; - warning.date = new Date(); - warning.given_by = { - _id: req.session.user!.id!, - name: req.session.user!.name!, - }; - - await warning.save(); - - warningReceiver.warningIds = [ - ...warningReceiver.warningIds, - String(warning._id), - ]; - await warningReceiver.save(async (err) => { - if (err) { - await warning.remove(); - throw err; - } - }); - res.data.warning = warning; - next(); - } catch (err) { - next(err); - } -}; - -export default addWarning; diff --git a/src/middlewares/warning/getUserWarningsList.ts b/src/middlewares/warning/getUserWarningsList.ts deleted file mode 100644 index fdca929ffa57433b1b8dd779184e3e9baf1fdc27..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/getUserWarningsList.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Profile from "../../models/ProfileSchema"; -import Warning from "../../models/WarningSchema"; - -const getUserWarningsList = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - if (!res.data.profile) - return res.status(404).json({ message: "User not found" }); - - res.data.warnings = await Warning.find({ - _id: { $in: res.data.profile?.warningIds }, - }); - - next(); - } catch (err) { - next(err); - } -}; - -export default getUserWarningsList; diff --git a/src/middlewares/warning/getWarning.ts b/src/middlewares/warning/getWarning.ts deleted file mode 100644 index f1d59c34f7a3262c0127d6efbdf4beea624cb95c..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/getWarning.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Warning from "../../models/WarningSchema"; - -const getWarning = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - res.data.warning = await Warning.findById(req.params.warningId) - .lean() - .exec(); - next(); - } catch (err) { - next(err); - } -}; - -export default getWarning; diff --git a/src/middlewares/warning/responseWarning.ts b/src/middlewares/warning/responseWarning.ts deleted file mode 100644 index e4bbb7c0343081e4e4323188705a2a997df889b7..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/responseWarning.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NextFunction, Request, Response, response } from "express"; - -const responseWarning = () => (req: Request, res: Response) => { - if (!res.data.warning) { - return res.status(404).json({ message: "Warning not found!" }); - } - res.json(res.data.warning); -}; - -export default responseWarning; diff --git a/src/middlewares/warning/responseWarningList.ts b/src/middlewares/warning/responseWarningList.ts deleted file mode 100644 index 432a60a5577704941c63ac23436ca8c6f3e13b85..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/responseWarningList.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NextFunction, Request, Response, response } from "express"; - -const responseWarningList = () => (req: Request, res: Response) => { - return res.json(res.data.warnings); -}; - -export default responseWarningList; diff --git a/src/middlewares/warning/updateWarning.ts b/src/middlewares/warning/updateWarning.ts deleted file mode 100644 index 5d819d499181577bc6fd9ed73928bd4b1e6cf9f8..0000000000000000000000000000000000000000 --- a/src/middlewares/warning/updateWarning.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NextFunction, Request, Response } from "express"; - -import Warning from "../../models/WarningSchema"; - -// Valid fields to update -const validFields = ["text"]; - -const updateWarning = () => async ( - req: Request, - res: Response, - next: NextFunction -) => { - try { - const warning = await Warning.findById(req.params.warnmingId).exec(); - - if (warning) { - validFields.forEach((field) => { - const value = req.body[field]; - if (value) warning.set(field, value); - }); - await warning.save(); - } - res.data.warning = warning; - - next(); - } catch (err) { - next(err); - } -}; - -export default updateWarning; diff --git a/src/models/ApplicantSchema.ts b/src/models/ApplicantSchema.ts deleted file mode 100644 index 98395cfd521d8183ffb7d24b689d6945f7fb9eda..0000000000000000000000000000000000000000 --- a/src/models/ApplicantSchema.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Document, Schema, model } from "mongoose"; -import { IProfile } from "./ProfileSchema"; - -export enum CardStatus { - Requested, - Printed, - Received, -} - -export interface IApplicant extends Document { - cardStatus: CardStatus; - profile: { - name: string; - id: IProfile["_id"]; - }; -} - -const ApplicationSchema = new Schema({ - cardStatus: { type: String, required: true }, - profile: { - id: { type: Schema.Types.ObjectId, required: true }, - name: { type: String, required: true }, - }, -}); - -export default model<IApplicant>("Applicant", ApplicationSchema); diff --git a/src/models/ApplicationPeriodSchema.ts b/src/models/ApplicationPeriodSchema.ts deleted file mode 100644 index ce974cbabdd47d5a10436312ffa5e79086268c53..0000000000000000000000000000000000000000 --- a/src/models/ApplicationPeriodSchema.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Document, Schema, model } from "mongoose"; -import ApplicantSchema, { IApplicant } from "./ApplicantSchema"; - -export interface IApplicationPeriod extends Document { - startAt: Date; - endAt: Date; - deadline: Date; - name: string; - applicants: IApplicant[]; -} - -const ApplicationPeriodSchema = new Schema({ - startedAt: { type: Date, required: true }, - endAt: { type: Date, required: true }, - deadline: { type: Date, required: true }, - name: { type: String, required: true }, - applicants: [ApplicantSchema], -}); - -export default model<IApplicationPeriod>( - "Application period", - ApplicationPeriodSchema -); diff --git a/src/models/CardImageSchema.ts b/src/models/CardImageSchema.ts deleted file mode 100644 index 17bfdb35a20d76ee72e7e4beea1b0d6d68b65071..0000000000000000000000000000000000000000 --- a/src/models/CardImageSchema.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Document, Schema, model } from "mongoose"; - -export interface ICardImage extends Document { - imageId: String; -} - -const CardImageSchema = new Schema({ - imageId: { type: Schema.Types.ObjectId, ref: "File", required: true }, -}); - -export default model<ICardImage>("CardImage", CardImageSchema); diff --git a/src/models/CardSchema.ts b/src/models/CardSchema.ts deleted file mode 100644 index aaec08da2909f654b739b7a4909a3d873f7abe1f..0000000000000000000000000000000000000000 --- a/src/models/CardSchema.ts +++ /dev/null @@ -1,40 +0,0 @@ -import CounterSchema, { ICounter } from "./CounterSchema"; -import { Document, Schema, model } from "mongoose"; - -import ProfileSchema from "./ProfileSchema"; - -export interface ICard extends Document { - userId: string; - fullName: string; - roomNumber: Number; - cardId: Number; - createDate: Date; - expirationDate: Date; - isTaken: boolean; -} - -const CardSchema = new Schema({ - userId: { type: Schema.Types.ObjectId, ref: "User" }, - fullName: { type: String, required: true }, - roomNumber: { type: Number, required: true }, - cardId: { type: Number }, - createDate: { type: Date, required: true, default: new Date() }, - expirationDate: { type: Date, required: true }, - isTaken: { type: Boolean, required: true, default: false }, -}); - -CardSchema.pre("save", function (this: ICard, next) { - var doc = this; - CounterSchema.findByIdAndUpdate( - { _id: "cardId" }, - { $inc: { seq: 1 } }, - { upsert: true, new: true }, - function (error, counter: ICounter | null) { - if (error) return next(error); - doc.cardId = counter!.seq; - next(); - } - ); -}); - -export default model<ICard>("Card", CardSchema); diff --git a/src/models/FileSchema.ts b/src/models/FileSchema.ts index fa6679432e7e646cfbd5a2eeab62514ef56ff892..268514c62f9c86758e653e11a99cf5573d7562d3 100644 --- a/src/models/FileSchema.ts +++ b/src/models/FileSchema.ts @@ -3,7 +3,6 @@ import { Document, Schema, model } from "mongoose"; export interface IFile extends Document { path: string; originalName: string; - encoding: string; mimeType: string; uploadDate: Date; } diff --git a/src/models/NewsSchema.ts b/src/models/NewsSchema.ts index e82064d5e6817af472547b8b25a518b3bf285f5f..72d32c23ea500a05c3402f1dc2426a2b7c9a6419 100644 --- a/src/models/NewsSchema.ts +++ b/src/models/NewsSchema.ts @@ -4,12 +4,16 @@ export interface INews extends Document { title: string; text: string; publishedAt: Date; + publishedBy: string; + updatedBy?: string; } const NewsSchema = new Schema({ title: { type: String, required: true }, text: { type: String, required: true }, publishedAt: { type: Date, required: true }, + publishedBy: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, + updatedBy: { type: Schema.Types.ObjectId, ref: "Profile", required: false }, }); export default model<INews>("News", NewsSchema); diff --git a/src/models/ProfileSchema.ts b/src/models/ProfileSchema.ts index e01b02d010952d14475116aa0d02ef42bdb72a80..011a4a4d7b203dcbac12c70f2c4a7bc8c976ae61 100644 --- a/src/models/ProfileSchema.ts +++ b/src/models/ProfileSchema.ts @@ -1,41 +1,76 @@ import { Document, Schema, model } from "mongoose"; -import { IWarning, WarningSchema } from "./WarningSchema"; - -import { Admin } from "mongodb"; export enum Role { - Admin, - Staff, - User, + Admin = "ADMIN", + User = "USER", +} + +export interface INotice { + _id: string; + text: string; + redirect?: string; + isSeen: boolean; +} + +export interface IWarning { + _id: string; + text: string; + date: Date; + givenBy: string; } export interface IProfile extends Document { - external_id: string; - studentCardNumber: string; - roomNumber: Number; - pictureId: string; + externalId: string; + studentCardNumber?: string; + roomNumber?: Number; + newPicture?: string; + acceptedPicture?: string; role: Role; email: string; name: string; - warningIds: string[] | []; + isStaffMember?: boolean; + staffMemberText?: string; + warnings: IWarning[]; + notices: INotice[]; } const ProfileSchema = new Schema({ - external_id: { type: String, required: true, unique: true, dropDups: true }, + externalId: { type: String, required: true, unique: true, dropDups: true }, studentCardNumber: { type: String, required: false }, - roomNumber: { type: Number }, - pictureId: { type: Schema.Types.ObjectId, ref: "File", required: false }, + roomNumber: { type: Number, required: false }, + newPicture: { type: Schema.Types.ObjectId, ref: "File", required: false }, + acceptedPicture: { + type: Schema.Types.ObjectId, + ref: "File", + required: false, + }, role: { type: String, - enum: Object.keys(Role).map((k) => Role[k as any]), + enum: Object.keys(Role).map((k) => Role[k as keyof typeof Role]), required: true, default: Role.User, }, email: { type: String, required: true }, name: { type: String, required: true }, - warningIds: [ - { type: Schema.Types.ObjectId, ref: "Warning", required: false }, + isStaffMember: { type: Boolean, required: false, default: false }, + staffMemberText: { type: String, required: false }, + notices: [ + { + text: { type: String, required: true }, + redirect: { type: String, required: false }, + isSeen: { type: Boolean, required: true, default: false }, + }, + ], + warnings: [ + { + text: { type: String, required: true }, + date: { type: Date, required: true }, + givenBy: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, + }, ], }); -export default model<IProfile>("Profile", ProfileSchema); +export default model<IProfile & { noticesNum: number }>( + "Profile", + ProfileSchema +); diff --git a/src/models/TermSchema.ts b/src/models/TermSchema.ts new file mode 100644 index 0000000000000000000000000000000000000000..a71858c11aab39933ac98eb2ee9679d5547c33e0 --- /dev/null +++ b/src/models/TermSchema.ts @@ -0,0 +1,65 @@ +import { Document, Schema, model } from "mongoose"; + +export enum MemberState { + Accepted = "ACCEPTED", + Applied = "APPLIED", + Rejected = "REJECTED", +} + +export enum CardState { + Created = "CREATED", + Printed = "PRINTD", + Given = "GIVEN", +} + +export interface IMember { + user: string; + memberState: MemberState; + cardState: CardState; + cardNumber: number; + cardReceiveDate?: Date; +} + +export interface ITerm extends Document { + backgroundFile?: string; + name: string; + createDate?: Date; + startDate: Date; + endDate: Date; + deadline: Date; + members: IMember[]; +} + +const TermSchema = new Schema({ + backgroundFile: { type: Schema.Types.ObjectId, ref: "File", required: false }, + name: { type: String, required: true }, + createDate: { type: Date, required: true }, + startDate: { type: Date, required: true }, + endDate: { type: Date, required: true }, + deadline: { type: Date, required: true }, + members: [ + { + user: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, + memberState: { + type: String, + enum: Object.keys(MemberState).map( + (k) => MemberState[k as keyof typeof MemberState] + ), + required: true, + default: MemberState.Applied, + }, + cardState: { + type: String, + enum: Object.keys(CardState).map( + (k) => CardState[k as keyof typeof CardState] + ), + required: true, + default: CardState.Created, + }, + cardNumber: { type: Number, required: true }, + cardReceiveDate: { type: Date, required: false }, + }, + ], +}); + +export default model<ITerm>("Term", TermSchema); diff --git a/src/models/WarningSchema.ts b/src/models/WarningSchema.ts deleted file mode 100644 index 61dfef1c0a6ec1b8a562afd0e33550c8e852edbd..0000000000000000000000000000000000000000 --- a/src/models/WarningSchema.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Document, Schema, model } from "mongoose"; - -export interface IWarning extends Document { - receiver: string; - text: string; - date: Date; - given_by: { - _id: string; - name: string; - }; -} - -export const WarningSchema = new Schema({ - receiver: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, - text: { type: String, required: true }, - date: { type: Date, required: true }, - given_by: { - required: true, - type: { - _id: { type: Schema.Types.ObjectId, ref: "Profile", required: true }, - name: { String, required: true }, - }, - }, -}); - -export default model<IWarning>("Warning", WarningSchema); diff --git a/src/models/user.interface.ts b/src/models/user.interface.ts index 1ec9320854428325a3fde3455b3891aa152ed57b..2981e326b9dde5134f7c6c93f1ef60dd0e14cf50 100644 --- a/src/models/user.interface.ts +++ b/src/models/user.interface.ts @@ -1,9 +1,11 @@ +import { Role } from "./ProfileSchema"; import { Token } from "simple-oauth2"; export interface User { email: string; name: string; id?: string; - external_id: string; + externalId: string; token: Token; + role?: Role; } diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 1202f644070fe274bffa1637f1ce052217406781..f05d3027b540d058e570d96ec6879604bed99baf 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -1,16 +1,15 @@ import { Application } from "express"; import complete from "../middlewares/auth/complete"; -import isAuthenticated from "../middlewares/auth/isAuthenticated"; import isLoggedIn from "../middlewares/auth/isLoggedIn"; import login from "../middlewares/auth/login"; import logout from "../middlewares/auth/logout"; -const authRoute = (app: Application): void => { - app.get("/api/v1/login", login()); +const authRoute = (prefix: string, app: Application): void => { + app.get(`${prefix}/login`, login()); - app.get("/api/v1/complete", complete()); + app.get(`${prefix}/complete`, complete()); - app.get("/api/v1/logout", isLoggedIn(), logout()); + app.get(`${prefix}/logout`, isLoggedIn(), logout()); }; export default authRoute; diff --git a/src/routes/card.ts b/src/routes/card.ts deleted file mode 100644 index 5532d735ba013e921b8b2e0c5b7ca963e261e9fa..0000000000000000000000000000000000000000 --- a/src/routes/card.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Application } from "express"; -import addCard from "../middlewares/cards/addCard"; -import getCard from "../middlewares/cards/getCard"; -import getCardList from "../middlewares/cards/getCardList"; -import getCardListValid from "../middlewares/cards/getCardListValid"; -import isAuthenticated from "../middlewares/auth/isAuthenticated"; -import responseCard from "../middlewares/cards/responseCard"; -import responseCardList from "../middlewares/cards/responseCardList"; - -const cardRoute = (app: Application): void => { - app.get("/api/v1/card/:id", isAuthenticated(), getCard(), responseCard()); - - app.get( - "/api/v1/cards", - isAuthenticated(), - getCardList(), - responseCardList() - ); - - app.get( - "/api/v1/cards/valid", - isAuthenticated(), - getCardListValid(), - responseCardList() - ); - - app.post("/api/v1/cards", isAuthenticated(), addCard(), responseCard()); -}; - -export default cardRoute; diff --git a/src/routes/file.ts b/src/routes/file.ts deleted file mode 100644 index 720de10bb00cf6f8dbb837870fd3a98207afc9d9..0000000000000000000000000000000000000000 --- a/src/routes/file.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Application } from "express"; -import cardImageStorage from "../middlewares/files/card/cardImageStorage"; -import { fileFilter } from "../middlewares/files/fileFilter"; -import getCardImage from "../middlewares/files/card/getCardImage"; -import getProfilePicture from "../middlewares/files/profile/getProfilePicture"; -import getUser from "../middlewares/user/getUser"; -import handleFileValidationError from "../middlewares/files/handleFileValidationError"; -import isAuthenticated from "../middlewares/auth/isAuthenticated"; -import multer from "multer"; -import profilePictureStorage from "../middlewares/files/profile/profilePictureStorage"; -import responseCardImage from "../middlewares/files/card/responseCardImage"; -import setOwnUserId from "../middlewares/user/setOwnUserId"; -import uploadCardImage from "../middlewares/files/card/uploadCardImage"; -import uploadProfilePicture from "../middlewares/files/profile/uploadProfilePicture"; - -const profilePictureUpload = multer({ - storage: profilePictureStorage, - fileFilter, -}); - -const CardImageUpload = multer({ - storage: cardImageStorage, - fileFilter, -}); - -const fileRoute = (app: Application): void => { - app.post( - "/api/v1/files/profile/picture/me", - isAuthenticated(), - profilePictureUpload.single("profile_picture"), - handleFileValidationError(), - setOwnUserId(), - getUser(), - uploadProfilePicture() - ); - app.post( - "/api/v1/files/profile/picture/:id", - isAuthenticated(), - profilePictureUpload.single("profile_picture"), - handleFileValidationError(), - getUser(), - uploadProfilePicture() - ); - app.get( - "/api/v1/files/profile", - isAuthenticated(), - setOwnUserId(), - getUser(), - getProfilePicture() - ); - - app.post( - "/api/v1/files/card", - isAuthenticated(), - CardImageUpload.single("card_image"), - handleFileValidationError(), - uploadCardImage() - ); - app.get( - "/api/v1/files/card", - isAuthenticated(), - getCardImage(), - responseCardImage() - ); -}; - -export default fileRoute; diff --git a/src/routes/files.ts b/src/routes/files.ts new file mode 100644 index 0000000000000000000000000000000000000000..3083e6f7133c77bd0bd1a4d8de6229a2d0d90948 --- /dev/null +++ b/src/routes/files.ts @@ -0,0 +1,155 @@ +import { Application } from "express"; +import createFile from "../middlewares/files/createFile"; +import createdResponse from "../middlewares/utils/createdResponse"; +import getFieldValue from "../middlewares/utils/getFieldValue"; +import getTerm from "../middlewares/term/getTerm"; +import getTermWithQuery from "../middlewares/term/getTermImage"; +import getUser from "../middlewares/user/getUser"; +import { imageFilter } from "../middlewares/files/imageFilter"; +import { imageStorage } from "../middlewares/files/imageStorage"; +import isAdmin from "../middlewares/auth/isAdmin"; +import isRegistered from "../middlewares/auth/isRegistered"; +import multer from "multer"; +import sendFile from "../middlewares/files/sendFile"; +import sendUserCard from "../middlewares/files/sendUserCard"; +import setOwnUserId from "../middlewares/user/setOwnUserId"; +import updateProfilePicture from "../middlewares/files/updateProfilePicture"; +import updateTermCardBg from "../middlewares/files/updateTermCardBg"; +import userIsStaff from "../middlewares/user/userIsStaff"; + +const profilePictureUpload = multer({ + storage: imageStorage("uploads/profile_pictures/"), + fileFilter: imageFilter, +}); + +const CardImageUpload = multer({ + storage: imageStorage("uploads/card_image/"), + fileFilter: imageFilter, +}); + +// TODO in Future: OwnTermCardPreview(termId) + +const filesRoute = (prefix: string, app: Application): void => { + /** + * Get Own New Profile picture + * Role: Normal + */ + app.get( + `${prefix}/me/picture/new`, + isRegistered(), + setOwnUserId(), + getUser("_id newPicture"), + getFieldValue("profile", "newPicture"), + sendFile() + ); + + /** + * Upload Own New Profile picture + * Role: Normal + */ + app.post( + `${prefix}/me/picture/new`, + isRegistered(), + setOwnUserId(), + getUser("_id newPicture"), + profilePictureUpload.single("profile_picture"), + createFile(), + updateProfilePicture(), + createdResponse() + ); + + /** + * Get Own Accepted Profile picture + * Role: Normal + */ + app.get( + `${prefix}/me/picture/accepted`, + isRegistered(), + setOwnUserId(), + getUser("_id acceptedPicture"), + getFieldValue("profile", "acceptedPicture"), + sendFile() + ); + + /** + * Get a Users New Profile picture + * Role: Admin + */ + app.get( + `${prefix}/user/:userId/picture/new`, + isRegistered(), + isAdmin(), + getUser("_id newPicture"), + getFieldValue("profile", "newPicture"), + sendFile() + ); + + /** + * Get a Users Accepted Profile picture + * Role: Admin + */ + app.get( + `${prefix}/user/:userId/picture/accepted`, + isRegistered(), + isAdmin(), + getUser("_id acceptedPicture"), + getFieldValue("profile", "acceptedPicture"), + sendFile() + ); + + /** + * Get Staff members picture + * Role: Normal + */ + app.get( + `${prefix}/staff/:userId/picture`, + isRegistered(), + getUser("_id acceptedPicture isStaffMember staffMemberText"), + userIsStaff(), + getFieldValue("profile", "acceptedPicture"), + sendFile() + ); + + /** + * Get Card SVG of User + * Role: Admin + */ + app.get( + `${prefix}/term/:termId/user/:userId/card`, + isRegistered(), + isAdmin(), + getUser("_id acceptedPicture name roomNumber"), + getTerm(true), + sendUserCard() + ); + + /** + * Get Term Card Image + * Role: Admin + */ + app.get( + `${prefix}/term/:termId/card`, + isRegistered(), + isAdmin(), + getTermWithQuery("_id backgroundFile"), + getFieldValue("term", "backgroundFile"), + sendFile() + ); + + /** + * Upload Term Card Image + * Role: Admin + */ + app.post( + `${prefix}/term/:termId/card`, + isRegistered(), + isAdmin(), + getTermWithQuery("_id backgroundFile"), + CardImageUpload.single("card_image"), + createFile(), + updateTermCardBg(), + createdResponse() + ); +}; + +export default filesRoute; diff --git a/src/routes/news.ts b/src/routes/news.ts index a5030463e4229ca6e9e845f11b6fb685dccab9fa..77fb7aa80f776b0a3e10baceb3cbc0fd3f9f75e3 100644 --- a/src/routes/news.ts +++ b/src/routes/news.ts @@ -1,22 +1,79 @@ -import { Application, Response } from "express"; - +import { Application } from "express"; import addNews from "../middlewares/news/addNews"; +import createdResponse from "../middlewares/utils/createdResponse"; import deleteNews from "../middlewares/news/deleteNews"; -import emptyResponse from "../middlewares/utils/emptyResponse"; -import getNews from "../middlewares/news/getNews"; import getNewsList from "../middlewares/news/getNewsList"; +import getOneNews from "../middlewares/news/getOneNews"; +import isAdmin from "../middlewares/auth/isAdmin"; +import isRegistered from "../middlewares/auth/isRegistered"; +import noContentResponse from "../middlewares/utils/noContentResponse"; import responseNews from "../middlewares/news/responseNews"; -import responseNewsObject from "../middlewares/news/responseNewsObject"; +import responseOneNews from "../middlewares/news/responseOneNews"; import updateNews from "../middlewares/news/updateNews"; -export default (app: Application): void => { - app.get("/api/v1/news", getNewsList(), responseNews()); +const newsRoute = (prefix: string, app: Application): void => { + /** + * Get All News + * Role: None + */ + app.get( + `${prefix}/`, + getNewsList("_id title text publishedAt"), + responseNews() + ); - app.post("/api/v1/news", addNews(), responseNewsObject()); + /** + * Get one News + * Role: Admin + */ + app.get( + `${prefix}/one/:newsId`, + isRegistered(), + isAdmin(), + getOneNews("_id title text publishedAt publishedBy updatedBy"), + responseOneNews() + ); - app.get("/api/v1/news/:id", getNews(), responseNewsObject()); + /** + * Create one new News + * Role: Admin + */ + app.post( + `${prefix}/`, + isRegistered(), + isAdmin(), + addNews([ + { name: "title", required: true }, + { name: "text", required: true }, + ]), + createdResponse() + ); - app.put("/api/v1/news/:id", getNews(), updateNews(), responseNewsObject()); + /** + * Update one News + * Role: Admin + */ + app.put( + `${prefix}/one/:newsId`, + isRegistered(), + isAdmin(), + getOneNews("_id"), + updateNews(["title", "text"]), + noContentResponse() + ); - app.delete("/api/v1/news/:id", deleteNews(), emptyResponse()); + /** + * Delete one News + * Role: Admin + */ + app.delete( + `${prefix}/one/:newsId`, + isRegistered(), + isAdmin(), + getOneNews("_id"), + deleteNews(), + noContentResponse() + ); }; + +export default newsRoute; diff --git a/src/routes/setErrorHandler.ts b/src/routes/setErrorHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..00f4f36551ae47a4455ed6a0baf03a96228facec --- /dev/null +++ b/src/routes/setErrorHandler.ts @@ -0,0 +1,23 @@ +import { Application, NextFunction, Request, Response } from "express"; +import { ErrorHandler, handleError } from "../middlewares/utils/ErrorHandler"; + +import { Error as MongooseError } from "mongoose"; +import multer from "multer"; + +const setErrorHandler = (app: Application): void => { + app.use((err: any, req: Request, res: Response, next: NextFunction) => { + if (err instanceof ErrorHandler) return handleError(err, res); + if ( + err instanceof MongooseError.CastError || + err instanceof MongooseError.ValidationError || + err instanceof multer.MulterError + ) + return handleError(new ErrorHandler(400, err.message), res); + + //Flush out the stack to the console + console.error(err.stack); + res.status(500).send("Houston, we have a problem!"); + }); +}; + +export default setErrorHandler; diff --git a/src/routes/setRouters.ts b/src/routes/setRouters.ts new file mode 100644 index 0000000000000000000000000000000000000000..e70329384931388ef7ece8534e0574a58bcd2e32 --- /dev/null +++ b/src/routes/setRouters.ts @@ -0,0 +1,31 @@ +import { Application } from "express"; +import authRoute from "./auth"; +import filesRoute from "./files"; +import newsRoute from "./news"; +import setErrorHandler from "./setErrorHandler"; +import termsRoute from "./terms"; +import userMembershipRoute from "./users/membership"; +import userNotificeRoute from "./users/notice"; +import userWarningsRoute from "./users/warning"; +import usersRoute from "./users/user"; + +const prefix = "/api/v1"; + +const setRouters = (app: Application): void => { + authRoute(prefix, app); + + filesRoute(`${prefix}/files`, app); + + newsRoute(`${prefix}/news`, app); + + termsRoute(`${prefix}/terms`, app); + + usersRoute(`${prefix}/users`, app); + userMembershipRoute(`${prefix}/users`, app); + userWarningsRoute(`${prefix}/users`, app); + userNotificeRoute(`${prefix}/users`, app); + + setErrorHandler(app); +}; + +export default setRouters; diff --git a/src/routes/terms.ts b/src/routes/terms.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a22bf5ae4430688d1c3f82179b2abcfd9361f84 --- /dev/null +++ b/src/routes/terms.ts @@ -0,0 +1,135 @@ +import { Application } from "express"; +import addMember from "../middlewares/term/addMember"; +import addTerm from "../middlewares/term/addTerm"; +import createdEmptyResponse from "../middlewares/utils/createdEmptyResponse"; +import createdResponse from "../middlewares/utils/createdResponse"; +import deleteFile from "../middlewares/files/deleteFile"; +import deleteTerm from "../middlewares/term/deleteTerm"; +import getFieldValue from "../middlewares/utils/getFieldValue"; +import getTerm from "../middlewares/term/getTerm"; +import getTermMembers from "../middlewares/term/getTermMembers"; +import getTermsList from "../middlewares/term/getTermsList"; +import getUser from "../middlewares/user/getUser"; +import isAdmin from "../middlewares/auth/isAdmin"; +import isNotMember from "../middlewares/term/isNotMember"; +import isRegistered from "../middlewares/auth/isRegistered"; +import noContentResponse from "../middlewares/utils/noContentResponse"; +import responseMembersList from "../middlewares/term/responseMembersList"; +import responseTerm from "../middlewares/term/responseTerm"; +import responseTermsList from "../middlewares/term/responseTermsList"; +import setOwnUserId from "../middlewares/user/setOwnUserId"; +import updateTerm from "../middlewares/term/updateTerm"; +import updateTermMember from "../middlewares/term/updateTermMember"; + +const termsRoute = (prefix: string, app: Application): void => { + /** + * Get All terms + * Role: NORMAL + */ + app.get( + `${prefix}/`, + isRegistered(), + setOwnUserId(), + getTermsList(), + responseTermsList() + ); + + /** + * Get a Term + * Role: NORMAL + */ + app.get( + `${prefix}/term/:termId`, + isRegistered(), + setOwnUserId(), + getTerm(), + responseTerm() + ); + + /** + * Apply Own User to a Term + * Role: NORMAL + */ + app.post( + `${prefix}/apply/me/term/:termId`, + isRegistered(), + setOwnUserId(), + getUser("_id"), + getTerm(), + isNotMember(), + addMember(), + createdEmptyResponse() + ); + + /** + * Get term members + * Role: ADMIN + */ + app.get( + `${prefix}/term/:termId/members`, + isRegistered(), + isAdmin(), + getTermMembers(), + responseMembersList() + ); + + /** + * Create a Term + * Role: ADMIN + */ + app.post( + `${prefix}/`, + isRegistered(), + isAdmin(), + addTerm([ + { name: "name", required: true }, + { name: "createDate", required: false }, + { name: "startDate", required: true }, + { name: "endDate", required: true }, + { name: "deadline", required: true }, + ]), + createdResponse() + ); + + /** + * Delete a Term + * Role: ADMIN + */ + app.delete( + `${prefix}/term/:termId`, + isRegistered(), + isAdmin(), + getFieldValue("term", "backgroundFile"), + deleteFile(), + deleteTerm(), + noContentResponse() + ); + + /** + * Update a Term + * Role: ADMIN + */ + app.put( + `${prefix}/term/:termId`, + isRegistered(), + isAdmin(), + getTerm(), + updateTerm(["name", "startDate", "endDate", "deadline", "createDate"]), + noContentResponse() + ); + + /** + * Update Term member + * Role: ADMIN + */ + app.put( + `${prefix}/term/:termId/user/:userId`, + isRegistered(), + isAdmin(), + getTerm(), + updateTermMember(["memberState", "cardState", "cardReceiveDate"]), + noContentResponse() + ); +}; + +export default termsRoute; diff --git a/src/routes/user.ts b/src/routes/user.ts deleted file mode 100644 index c24d0eab45a7191c1b4da01d687c8a4e719f0403..0000000000000000000000000000000000000000 --- a/src/routes/user.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Application } from "express"; -import addUser from "../middlewares/user/addUser"; -import addWarning from "../middlewares/warning/addWarning"; -import deleteUser from "../middlewares/user/deleteUser"; -import deleteWarning from "../middlewares/warning/deleteWarning"; -import emptyResponse from "../middlewares/utils/emptyResponse"; -import getCardImage from "../middlewares/files/card/getCardImage"; -import getUser from "../middlewares/user/getUser"; -import getUserCard from "../middlewares/files/card/getUserCard"; -import getUsersList from "../middlewares/user/getUsersList"; -import getWarning from "../middlewares/warning/getWarning"; -import getWarningsList from "../middlewares/warning/getUserWarningsList"; -import isAuthenticated from "../middlewares/auth/isAuthenticated"; -import isLoggedIn from "../middlewares/auth/isLoggedIn"; -import responseUser from "../middlewares/user/responseUser"; -import responseUserList from "../middlewares/user/responseUserList"; -import setOwnUserId from "../middlewares/user/setOwnUserId"; -import updateUser from "../middlewares/user/updateUser"; -import updateWarning from "../middlewares/warning/updateWarning"; - -const usersRoute = (app: Application): void => { - app.get( - "/api/v1/users", - isAuthenticated(), - getUsersList(), - responseUserList() - ); - - app.post("/api/v1/users", isLoggedIn(), addUser(), responseUser()); - - app.get( - "/api/v1/users/me", - isAuthenticated(), - setOwnUserId(), - getUser(), - responseUser() - ); - - app.get( - "/api/v1/users/me/card", - isAuthenticated(), - setOwnUserId(), - getUser(), - getCardImage(), - getUserCard() - ); - - app.get("/api/v1/users/:id", getUser(), responseUser()); - - app.get( - "/api/v1/users/:id/card", - isAuthenticated(), - getUser(), - getCardImage(), - getUserCard() - ); - - app.get("/api/v1/users/:id/warnings", getWarningsList()); - - app.get("/api/v1/users/:userId/warnings/:warningId", getWarning()); - - app.post("/api/v1/users/:id/warnings", addWarning()); - - app.put("/api/v1/users/:id", updateUser(), responseUser()); - - app.put("/api/v1/users/:userId/warnings/:warningId", updateWarning()); - - app.delete("/api/v1/users/:id", deleteUser(), emptyResponse()); - - app.delete("/api/v1/users/:userId/warnings/:warningId", deleteWarning()); -}; - -export default usersRoute; diff --git a/src/routes/users/membership.ts b/src/routes/users/membership.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a57b9d1c1acd5c8713e4d95888ddfa61f95de41 --- /dev/null +++ b/src/routes/users/membership.ts @@ -0,0 +1,34 @@ +import { Application } from "express"; +import getUserTermMemberships from "../../middlewares/term/getUserTermMemberships"; +import isAdmin from "../../middlewares/auth/isAdmin"; +import isRegistered from "../../middlewares/auth/isRegistered"; +import responseTermsList from "../../middlewares/term/responseTermsList"; +import setOwnUserId from "../../middlewares/user/setOwnUserId"; + +const userMembershipRoute = (prefix: string, app: Application): void => { + /** + * Get list of User Term memberships + * Role: ADMIN + */ + app.get( + `${prefix}/user/:userId/terms`, + isRegistered(), + isAdmin(), + getUserTermMemberships(), + responseTermsList() + ); + + /** + * Get list of own Term memberships + * Role: NORMAL + */ + app.get( + `${prefix}/me/terms`, + isRegistered(), + setOwnUserId(), + getUserTermMemberships(), + responseTermsList() + ); +}; + +export default userMembershipRoute; diff --git a/src/routes/users/notice.ts b/src/routes/users/notice.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3401a4b30323e3a4b45711663b8d4a7e32162a1 --- /dev/null +++ b/src/routes/users/notice.ts @@ -0,0 +1,92 @@ +import { Application } from "express"; +import addNoticeToUser from "../../middlewares/user/notice/addNoticeToUser"; +import deleteNotice from "../../middlewares/user/notice/deleteNotice"; +import example from "../../middlewares/example"; +import getUserNoticeNum from "../../middlewares/user/notice/getUserNoticeNum"; +import getUserNotices from "../../middlewares/user/notice/getUserNotices"; +import isRegistered from "../../middlewares/auth/isRegistered"; +import markNoticeAsSeen from "../../middlewares/user/notice/markNoticeAsSeen"; +import noContentResponse from "../../middlewares/utils/noContentResponse"; +import responseNoticesList from "../../middlewares/user/notice/responseNoticesList"; +import responseNoticesNum from "../../middlewares/user/notice/responseNoticesNum"; +import setOwnUserId from "../../middlewares/user/setOwnUserId"; + +const userNotificeRoute = (prefix: string, app: Application): void => { + /** + * Get own notifications + * Role: NORMAL + */ + app.get( + `${prefix}/me/notices`, + isRegistered(), + setOwnUserId(), + getUserNotices(), + responseNoticesList() + ); + + /** + * Get own notification number + * Role: NORMAL + */ + app.get( + `${prefix}/me/notices/number`, + isRegistered(), + setOwnUserId(), + getUserNoticeNum(), + responseNoticesNum() + ); + + /** + * Add notification to User + * Role: ADMIN + */ + app.post( + `${prefix}/user/:userId/notify`, + isRegistered(), + addNoticeToUser([ + { name: "text", required: true }, + { name: "redirect", required: false }, + ]), + noContentResponse() + ); + + /** + * Remove own notification + * Role: NORMAL + */ + app.delete( + `${prefix}/me/notice/:noticeId`, + isRegistered(), + setOwnUserId(), + deleteNotice(), + noContentResponse() + ); + + /** + * Add notification to Term members + * Role: ADMIN + */ + // TODO addTermMembersNotification (termId) { text, redirectUrl} + app.post(`${prefix}/term/:termId/notify`, example()); + + /** + * Add notification to all Users + * Role: ADMIN + */ + // TODO addAllusersNotification { text, redirectUrl} + app.post(`${prefix}/notify`, example()); + + /** + * Mark Notice as Seen + * Role: NORMAL + */ + app.put( + `${prefix}/me/notice/:noticeId`, + isRegistered(), + setOwnUserId(), + markNoticeAsSeen(), + noContentResponse() + ); +}; + +export default userNotificeRoute; diff --git a/src/routes/users/user.ts b/src/routes/users/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..b54277fd44de97a5f29e70b91c1e211697989eff --- /dev/null +++ b/src/routes/users/user.ts @@ -0,0 +1,197 @@ +import { Application } from "express"; +import acceptPicture from "../../middlewares/user/acceptPicture"; +import createdResponse from "../../middlewares/utils/createdResponse"; +import deleteFile from "../../middlewares/files/deleteFile"; +import deleteUser from "../../middlewares/user/deleteUser"; +import getFieldValue from "../../middlewares/utils/getFieldValue"; +import getUser from "../../middlewares/user/getUser"; +import getUserTermMemberships from "../../middlewares/term/getUserTermMemberships"; +import getUsersList from "../../middlewares/user/getUsersList"; +import isAdmin from "../../middlewares/auth/isAdmin"; +import isLoggedIn from "../../middlewares/auth/isLoggedIn"; +import isNotRegistered from "../../middlewares/auth/isNotRegistered"; +import isRegistered from "../../middlewares/auth/isRegistered"; +import noContentResponse from "../../middlewares/utils/noContentResponse"; +import registerOwnUser from "../../middlewares/user/registerOwnUser"; +import rejectPicture from "../../middlewares/user/rejectPicture"; +import responseTermsList from "../../middlewares/term/responseTermsList"; +import responseUser from "../../middlewares/user/responseUser"; +import responseUsersList from "../../middlewares/user/responseUsersList"; +import setOwnUserId from "../../middlewares/user/setOwnUserId"; +import updateUser from "../../middlewares/user/updateUser"; + +const usersRoute = (prefix: string, app: Application): void => { + /** + * Register Own User + * Role: NORMAL + */ + app.post( + `${prefix}/me`, + isLoggedIn(), + isNotRegistered(), + registerOwnUser(), + createdResponse() + ); + + /** + * Get list of users + * Role: ADMIN + */ + app.get( + `${prefix}/`, + isRegistered(), + isAdmin(), + getUsersList("_id name newPicture"), + responseUsersList() + ); + + /** + * Get One User + * Role: ADMIN + */ + app.get( + `${prefix}/user/:userId`, + isRegistered(), + isAdmin(), + getUser( + "_id name role email externalId studentCardNumber roomNumber isStaffMember staffMemberText" + ), + responseUser() + ); + + /** + * Get Own User + * Role: NORMAL + */ + app.get( + `${prefix}/me`, + isRegistered(), + setOwnUserId(), + getUser("id name role email studentCardNumber roomNumber"), + responseUser() + ); + + /** + * Get Own User Short + * Role: NORMAL + */ + app.get( + `${prefix}/me/short`, + isRegistered(), + setOwnUserId(), + getUser("id name role"), + responseUser() + ); + + /** + * Update Own User + * Role: NORMAL + */ + app.put( + `${prefix}/me`, + isRegistered(), + setOwnUserId(), + updateUser(["studentCardNumber", "roomNumber"]), + noContentResponse() + ); + + /** + * Update User + * Role: ADMIN + */ + app.put( + `${prefix}/user/:userId`, + isRegistered(), + isAdmin(), + getUser("_id"), + updateUser([ + "studentCardNumber", + "roomNumber", + "isStaffMember", + "staffMemberText", + ]), + noContentResponse() + ); + + /** + * Get list of staff Members + * Role: NORMAL + */ + app.get( + `${prefix}/staff`, + isRegistered(), + getUsersList("_id name email staffMemberText", { isStaffMember: true }), + responseUsersList() + ); + + /** + * Accept a users Picture + * Role: ADMIN + */ + app.post( + `${prefix}/user/:userId/picture/accept`, + isRegistered(), + isAdmin(), + getUser("acceptedPicture newPicture"), + getFieldValue("profile", "acceptedPicture"), + acceptPicture(), + deleteFile(), + noContentResponse() + ); + /** + * Reject a users Picture + * Role: ADMIN + */ + app.post( + `${prefix}/user/:userId/picture/reject`, + isRegistered(), + isAdmin(), + getUser("acceptedPicture newPicture"), + getFieldValue("profile", "newPicture"), + rejectPicture(), + deleteFile(), + noContentResponse() + ); + + /** + * Delete a User + * Role: ADMIN + */ + app.delete( + `${prefix}/user/:userId`, + isRegistered(), + isAdmin(), + getFieldValue("profile", "newPicture"), + deleteFile(), + getFieldValue("profile", "acceptedPicture"), + deleteFile(), + deleteUser(), + noContentResponse() + ); + + /** + * Get list of User Term memberships + * Role: ADMIN + */ + app.get( + `${prefix}/user/:userId/terms`, + isRegistered(), + isAdmin(), + getUserTermMemberships(), + responseTermsList() + ); + + /** + * Get list of own Term memberships + * Role: NORMAL + */ + app.get( + `${prefix}/me/terms`, + isRegistered(), + setOwnUserId(), + getUserTermMemberships(), + responseTermsList() + ); +}; + +export default usersRoute; diff --git a/src/routes/users/warning.ts b/src/routes/users/warning.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f4cb21c8ba006bdc218175a897cd927b19d63cb --- /dev/null +++ b/src/routes/users/warning.ts @@ -0,0 +1,71 @@ +import { Application } from "express"; +import addWarning from "../../middlewares/user/warning/addWarning"; +import deleteWarning from "../../middlewares/user/warning/deleteWarning"; +import getUserWarningsList from "../../middlewares/user/warning/getUserWarningsList"; +import isAdmin from "../../middlewares/auth/isAdmin"; +import isRegistered from "../../middlewares/auth/isRegistered"; +import noContentResponse from "../../middlewares/utils/noContentResponse"; +import responseWarningsList from "../../middlewares/user/warning/responseWarningsList"; +import setOwnUserId from "../../middlewares/user/setOwnUserId"; +import updateWarning from "../../middlewares/user/warning/updateWarning"; + +const userWarningsRoute = (prefix: string, app: Application): void => { + /** + * Add new warning + * Role: Admin + */ + app.post( + `${prefix}/user/:userId/warnings`, + isRegistered(), + isAdmin(), + addWarning([{ name: "text", required: true }]), + noContentResponse() + ); + + /** + * Get user warnings + * Role: Admin + */ + app.get( + `${prefix}/user/:userId/warnings`, + isRegistered(), + isAdmin(), + getUserWarningsList(), + responseWarningsList() + ); + /** + * Get Own warnings + * Role: Normal + */ + app.get( + `${prefix}/me/warnings`, + isRegistered(), + setOwnUserId(), + getUserWarningsList(), + responseWarningsList() + ); + /** + * Delete Warning + * Role: Admin + */ + app.delete( + `${prefix}/user/:userId/warning/:warningId`, + isRegistered(), + isAdmin(), + deleteWarning(), + noContentResponse() + ); + /** + * Update Warning + * Role: Admin + */ + app.put( + `${prefix}/user/:userId/warning/:warningId`, + isRegistered(), + isAdmin(), + updateWarning(["text"]), + noContentResponse() + ); +}; + +export default userWarningsRoute; diff --git a/src/routes/warning.ts b/src/routes/warning.ts deleted file mode 100644 index be2afb970128c66f8bb150b29ff7ded0e2c1e975..0000000000000000000000000000000000000000 --- a/src/routes/warning.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Application } from "express"; -import addWarning from "../middlewares/warning/addWarning"; -import deleteWarning from "../middlewares/warning/deleteWarning"; -import emptyResponse from "../middlewares/utils/emptyResponse"; -import getUser from "../middlewares/user/getUser"; -import getUserWarningsList from "../middlewares/warning/getUserWarningsList"; -import getWarning from "../middlewares/warning/getWarning"; -import isAuthenticated from "../middlewares/auth/isAuthenticated"; -import responseWarning from "../middlewares/warning/responseWarning"; -import responseWarningList from "../middlewares/warning/responseWarningList"; -import setOwnUserId from "../middlewares/user/setOwnUserId"; -import updateWarning from "../middlewares/warning/updateWarning"; - -const warningsRoute = (app: Application): void => { - app.get( - "/api/v1/warnings/warning/:warningId", - isAuthenticated(), - getWarning(), - responseWarning() - ); - - app.get( - "/api/v1/warnings/me", - isAuthenticated(), - setOwnUserId(), - getUser(), - getUserWarningsList(), - responseWarningList() - ); - - app.get( - "/api/v1/warnings/user/:id", - isAuthenticated(), - getUser(), - getUserWarningsList(), - responseWarningList() - ); - - app.post( - "/api/v1/warnings/user/:id", - isAuthenticated(), - addWarning(), - responseWarning() - ); - - app.put( - "/api/v1/warnings/warning/:warningId", - isAuthenticated(), - updateWarning(), - responseWarning() - ); - - app.delete( - "/api/v1/warnings/warning/:warningId", - isAuthenticated(), - deleteWarning(), - emptyResponse() - ); -}; - -export default warningsRoute; diff --git a/src/utils/declarations/response.d.ts b/src/utils/declarations/response.d.ts index 9c759230da7b0a3bde9f9393862b23ba0f34d185..4b8f59d33f95e7075394fe2bba28222ce6244847 100644 --- a/src/utils/declarations/response.d.ts +++ b/src/utils/declarations/response.d.ts @@ -1,25 +1,26 @@ import { INews } from "../../models/NewsSchema"; -import { IProfile } from "../../models/ProfileSchema"; -import { IFile } from "../../models/FileSchema"; -import { ICard } from "../../models/CardSchema"; -import { ICardImage } from "../../models/CardImageSchema"; -import { IWarning } from "../../models/WarningSchema"; +import { INotice, IProfile, IWarning } from "../../models/ProfileSchema"; +import { IMember, ITerm } from "../../models/TermSchema"; declare global { namespace Express { export interface Response { data: { - news?: Partial<INews>[] | null; - newsObject?: Partial<INews> | null; profile?: Partial<IProfile> | null; - card?: Partial<ICard> | null; - cards?: Partial<ICard>[] | null; - cardImage?: Partial<ICardImage> | null; profiles?: Partial<IProfile>[] | null; - error?: string | null; - files?: Partial<IFile>[] | null; warning?: Partial<IWarning> | null; warnings?: Partial<IWarning>[] | null; + newObjectId?: string | null; + term?: Partial<ITerm> | null; + terms?: Partial<ITerm>[] | null; + member?: Partial<IMember> | null; + members?: Partial<IMember>[] | null; + news?: Partial<INews>[] | null; + oneNews?: Partial<INews> | null; + notice?: Partial<INotice> | null; + notices?: Partial<INotice>[] | null; + value?: string | null; + number?: number | null; }; } } diff --git a/src/utils/declarations/session.d.ts b/src/utils/declarations/session.d.ts index be2d691845e23779a3da67227a58a65203b56045..e25eb62242301c3c340071088687aa712f93c8bd 100644 --- a/src/utils/declarations/session.d.ts +++ b/src/utils/declarations/session.d.ts @@ -1,4 +1,3 @@ -import { AccessToken } from "simple-oauth2"; import { User } from "../../models/user.interface"; declare module "express-session" { diff --git a/src/utils/strings/.gitignore b/src/utils/strings/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a64fc4577cfc05bff6e0695ba24b61311a2e453b --- /dev/null +++ b/src/utils/strings/.gitignore @@ -0,0 +1 @@ +notifications.ts \ No newline at end of file diff --git a/src/utils/strings/notifications.example.ts b/src/utils/strings/notifications.example.ts new file mode 100644 index 0000000000000000000000000000000000000000..df893c4fe420b1d3421c2f3c2b0b934608965079 --- /dev/null +++ b/src/utils/strings/notifications.example.ts @@ -0,0 +1,6 @@ +export const REGISTER = "Please register"; +export const MISSING_PROFILE_PICTURE = "Add a profile picture"; +export const NEW_WARNING = "You got a new warning"; +export const DELETED_WARNING = "One of your warning was deleted"; +export const TERM_MEMBER_ACCEPTED = "You've been accepted to a Term"; +export const TERM_MEMBER_REJECTED = "You've been rejected to a Term"; diff --git a/tsconfig.json b/tsconfig.json index 004e8b06f8bc7edeed072b9a0bcfdd7a227721c6..a5ae5c396b413363d471a74581e4a7765f653f82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,6 @@ "compilerOptions": { "module": "commonjs", "strict": true, - "baseUrl": "./", "outDir": "build", "removeComments": true, "experimentalDecorators": true, diff --git a/tslint.json b/tslint.json index e9628e637c18ef66e5a79f765fcd60e2f2e984ef..8ed07bbc1b9f8b8e995eee67133b39cc0ef0a6e1 100644 --- a/tslint.json +++ b/tslint.json @@ -9,6 +9,16 @@ "object-literal-sort-keys": false, "ordered-imports": false, "quotemark": [true, "single"], - "variable-name": [true, "allow-leading-underscore"] + "variable-name": [true, "allow-leading-underscore"], + "no-unused-declaration": { + "options": [ + { + "declarations": true, + "ignored": {}, + "imports": true + } + ], + "severity": "error" + } } }