From a9ceecdc8476997f3bdc70719552f3e095bb62ad Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Fri, 22 Sep 2023 13:39:26 +0100 Subject: [PATCH] Added the ability to generate a user code on the admin panel --- package.json | 1 + src/constants/userPermissions.ts | 11 +++++ src/pages/admin.tsx | 69 ++++++++++++++++++++++++++++++++ src/pages/api/code.ts | 33 +++++++++++++++ yarn.lock | 5 +++ 5 files changed, 119 insertions(+) create mode 100644 src/constants/userPermissions.ts create mode 100644 src/pages/api/code.ts diff --git a/package.json b/package.json index 3bdfcf9a..63bd6c08 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-string-replace": "^1.1.0", "react-toastify": "^9.1.2", "react-xarrows": "^2.0.2", + "short-unique-id": "^5.0.2", "swr": "^2.1.3", "typescript": "4.9.5", "uuid": "^9.0.0", diff --git a/src/constants/userPermissions.ts b/src/constants/userPermissions.ts new file mode 100644 index 00000000..426fdc47 --- /dev/null +++ b/src/constants/userPermissions.ts @@ -0,0 +1,11 @@ +import {Type} from "@/interfaces/user"; + +export const PERMISSIONS = { + generateCode: { + student: ["teacher", "admin", "developer", "owner"], + teacher: ["admin", "developer", "owner"], + admin: ["owner", "developer"], + owner: ["developer", "owner"], + developer: ["developer"], + }, +}; diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx index 8d576576..2a9793af 100644 --- a/src/pages/admin.tsx +++ b/src/pages/admin.tsx @@ -16,6 +16,9 @@ import Button from "@/components/Low/Button"; import {getExamById} from "@/utils/exams"; import useExamStore from "@/stores/examStore"; import {useRouter} from "next/router"; +import ShortUniqueId from "short-unique-id"; +import {Type} from "@/interfaces/user"; +import axios from "axios"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -111,6 +114,71 @@ const ExamLoader = () => { ); }; +const CodeGenerator = () => { + const [generatedCode, setGeneratedCode] = useState(); + + const generateCode = (type: Type) => { + const uid = new ShortUniqueId(); + const code = uid.randomUUID(6); + + axios + .post("/api/code", {type, code}) + .then(({data, status}) => { + if (data.ok) { + toast.success(`Successfully generated a ${capitalize(type)} code!`, {toastId: "success"}); + setGeneratedCode(code); + return; + } + + if (status === 403) { + toast.error(`You do not have permission to generate a ${capitalize(type)} code!`, {toastId: "forbidden"}); + } + }) + .catch(({response: {status}}) => { + if (status === 403) { + toast.error(`You do not have permission to generate a ${capitalize(type)} code!`, {toastId: "forbidden"}); + return; + } + + toast.error(`Something went wrong, please try again later!`, {toastId: "error"}); + }); + }; + + return ( +
+ +
+ + + + +
+ +
{ + if (generatedCode) navigator.clipboard.writeText(generatedCode); + }}> + {generatedCode} +
+ {generatedCode && Give this code to the user to complete their registration} +
+ ); +}; + export default function Admin() { const {user} = useUser({redirectTo: "/login"}); @@ -130,6 +198,7 @@ export default function Admin() {
+
)} diff --git a/src/pages/api/code.ts b/src/pages/api/code.ts new file mode 100644 index 00000000..75500735 --- /dev/null +++ b/src/pages/api/code.ts @@ -0,0 +1,33 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type {NextApiRequest, NextApiResponse} from "next"; +import {app} from "@/firebase"; +import {getFirestore, setDoc, doc} from "firebase/firestore"; +import {withIronSessionApiRoute} from "iron-session/next"; +import {sessionOptions} from "@/lib/session"; +import {Type} from "@/interfaces/user"; +import {PERMISSIONS} from "@/constants/userPermissions"; +import {uuidv4} from "@firebase/util"; + +const db = getFirestore(app); + +export default withIronSessionApiRoute(handler, sessionOptions); + +async function handler(req: NextApiRequest, res: NextApiResponse) { + if (!req.session.user) { + res.status(401).json({ok: false}); + return; + } + + const {type, code} = req.body as {type: Type; code: string}; + const permission = PERMISSIONS.generateCode[type]; + + if (!permission.includes(req.session.user.type)) { + res.status(403).json({ok: false}); + return; + } + + const codeRef = doc(db, "codes", uuidv4()); + await setDoc(codeRef, {type, code}); + + res.status(200).json({ok: true}); +} diff --git a/yarn.lock b/yarn.lock index df14879d..29d57528 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3284,6 +3284,11 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +short-unique-id@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/short-unique-id/-/short-unique-id-5.0.2.tgz#b09821d8fc1ed89220acce3800013ebce21436dd" + integrity sha512-4wZq1VLV4hsEx8guP5bN7XnY8UDsVXtdUDWFMP1gvEieAXolq5fWGKpuua21PRXaLn3OybTKFQNm7JGcHSWu/Q== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"