From 645e050091b33b3ec812407cd849417a401435a1 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 24 Mar 2022 18:59:27 +0100 Subject: [PATCH 001/827] feat: added project by id --- backend/orm_functions/project.ts | 14 ++++++++++++++ backend/tests/orm_tests/project.test.ts | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/orm_functions/project.ts b/backend/orm_functions/project.ts index efd516b5..13e2a3ed 100644 --- a/backend/orm_functions/project.ts +++ b/backend/orm_functions/project.ts @@ -28,6 +28,20 @@ export async function getAllProjects() { return result } +/** + * + * @param projectId: this is the id of the project we are looking up in the database + * @returns: object with all the info about this project + */ + export async function getProjectById(projectId: number) { + const result = prisma.project.findUnique({ + where: { + project_id: projectId + }, + }); + return result; +} + /** * * @param projectName: this is the name of the project we are looking up in the database diff --git a/backend/tests/orm_tests/project.test.ts b/backend/tests/orm_tests/project.test.ts index 97b96783..2d08b464 100644 --- a/backend/tests/orm_tests/project.test.ts +++ b/backend/tests/orm_tests/project.test.ts @@ -5,7 +5,7 @@ import {createProject, getProjectByName, getAllProjects, getProjectsByStartDate, getProjectsStartedBeforeDate, getProjectsByEndDate, getProjectsEndedBeforeDate, getProjectsEndedAfterDate, getProjectsByNumberPositions, getProjectsLessPositions, getProjectsMorePositions, deleteProject, updateProject, - deleteProjectByOsocEdition, deleteProjectByPartner} + deleteProjectByOsocEdition, deleteProjectByPartner, getProjectById} from "../../orm_functions/project"; const returnValue = { @@ -38,6 +38,11 @@ test("should return all projects in the db", async () => { await expect(getAllProjects()).resolves.toEqual([returnValue]); }); +test("should return the project with the given project id", async () => { + prismaMock.project.findUnique.mockResolvedValue(returnValue); + await expect(getProjectById(0)).resolves.toEqual(returnValue); +}); + test("should return all the projects with the given name", async () => { prismaMock.project.findMany.mockResolvedValue([returnValue]); await expect(getProjectByName("Test project")).resolves.toEqual([returnValue]); From 071268bb4039f8562dee43f547c2485a89a4741f Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Thu, 24 Mar 2022 21:02:14 +0100 Subject: [PATCH 002/827] feat: routes --- backend/orm_functions/orm_types.ts | 12 +- backend/request.ts | 20 +-- backend/routes/admin.ts | 107 ++++++------ backend/routes/coach.ts | 265 ++++++++++++++--------------- backend/routes/project.ts | 141 ++++++++++----- backend/routes/student.ts | 101 ++++++----- backend/types.ts | 16 +- 7 files changed, 365 insertions(+), 297 deletions(-) diff --git a/backend/orm_functions/orm_types.ts b/backend/orm_functions/orm_types.ts index ea53d545..fd1590d4 100644 --- a/backend/orm_functions/orm_types.ts +++ b/backend/orm_functions/orm_types.ts @@ -371,27 +371,27 @@ export interface UpdateProject { /** * undefined if unchanged or new project name */ - name: string, + name?: string, /** * undefined if unchanged or the new osoc id */ - osocId: number, + osocId?: number, /** * undefined if unchanged or the new partner of the project */ - partner: string, + partner?: string, /** * undefined if unchanged or the new start date of the project */ - startDate: Date + startDate?: Date /** * undefined if unchanged or the new end date of the project */ - endDate: Date, + endDate?: Date, /** * undefined if unchanged or the new number of positions of the project */ - positions: number + positions?: number } /** diff --git a/backend/request.ts b/backend/request.ts index 0ab7af3b..881b6d57 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -274,16 +274,16 @@ export async function parseRequestCoachRequest(req: express.Request): */ export async function parseNewProjectRequest(req: express.Request): Promise { - return hasFields(req, [ "name", "partner", "start", "end", "positions" ], - types.key) - .then(() => Promise.resolve({ - sessionkey : req.body.sessionkey, - name : req.body.name, - partner : req.body.partner, - start : req.body.start, - end : req.body.end, - positions : req.body.positions - })); + return hasFields(req, [ "name", "partner", "start", "end", "positions", "osocId" ], types.key) + .then(() => Promise.resolve({ + sessionkey : req.body.sessionkey, + name : req.body.name, + partner : req.body.partner, + start : req.body.start, + end : req.body.end, + osocId: req.body.osocId, + positions : req.body.positions + })); } /** diff --git a/backend/routes/admin.ts b/backend/routes/admin.ts index dc2ffb06..4f0c278d 100644 --- a/backend/routes/admin.ts +++ b/backend/routes/admin.ts @@ -13,25 +13,21 @@ import * as util from '../utility'; * `Promise.resolve`, failures using `Promise.reject`. */ async function listAdmins(req: express.Request): Promise { - return rq.parseAdminAllRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then( - async parsed => - ormL.searchAllCoachLoginUsers(true) - .then(obj => - obj.map(val => ({ - person_data : { - id : val.person.person_id, - name : val.person.firstname + " " + - val.person.lastname - }, - coach : val.is_coach, - admin : val.is_admin, - activated : val.account_status as string - }))) - .then( - obj => Promise.resolve( - {sessionkey : parsed.data.sessionkey, data : obj}))); + return rq.parseAdminAllRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then( + async parsed => + ormL.searchAllCoachLoginUsers(true) + .then(obj => obj.map(val => ({ + person_data : { + id : val.person.person_id, + name : val.person.firstname + " " + val.person.lastname + }, + coach : val.is_coach, + admin : val.is_admin, + activated : val.account_status as string + }))) + .then(obj => Promise.resolve({sessionkey : parsed.data.sessionkey, data : obj}))); } /** @@ -41,32 +37,31 @@ async function listAdmins(req: express.Request): Promise { * `Promise.resolve`, failures using `Promise.reject`. */ async function getAdmin(req: express.Request): Promise { - return rq.parseSingleAdminRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(() => - Promise.reject({http : 410, reason : 'Deprecated endpoint.'})); + return rq.parseSingleAdminRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(() => Promise.reject({http : 410, reason : 'Deprecated endpoint.'})); } async function modAdmin(req: express.Request): Promise { - return rq.parseUpdateAdminRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then(async parsed => { - return ormL - .updateLoginUser({ - loginUserId : parsed.data.id, - password : parsed.data.pass, - isAdmin : parsed.data.isAdmin, - isCoach : parsed.data.isCoach, - accountStatus : parsed.data.accountStatus as account_status_enum - }) - .then(res => Promise.resolve({ - sessionkey : parsed.data.sessionkey, - data : { - id : res.person_id, - name : res.person.firstname + " " + res.person.lastname - } - })); - }); + return rq.parseUpdateAdminRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(async parsed => { + return ormL + .updateLoginUser({ + loginUserId : parsed.data.id, + password : parsed.data.pass, + isAdmin : parsed.data.isAdmin, + isCoach : parsed.data.isCoach, + accountStatus : parsed.data.accountStatus as account_status_enum + }) + .then(res => Promise.resolve({ + sessionkey : parsed.data.sessionkey, + data : { + id : res.person_id, + name : res.person.firstname + " " + res.person.lastname + } + })); + }); } /** @@ -76,12 +71,12 @@ async function modAdmin(req: express.Request): Promise { * `Promise.resolve`, failures using `Promise.reject`. */ async function deleteAdmin(req: express.Request): Promise { - return rq.parseDeleteAdminRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(async parsed => { - return ormL.deleteLoginUserByPersonId(parsed.data.id) - .then(() => Promise.resolve({sessionkey : parsed.data.sessionkey})); - }); + return rq.parseDeleteAdminRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => { + return ormL.deleteLoginUserByPersonId(parsed.data.id) + .then(() => Promise.resolve({sessionkey : parsed.data.sessionkey})); + }); } /** @@ -90,17 +85,17 @@ async function deleteAdmin(req: express.Request): Promise { * endpoints. */ export function getRouter(): express.Router { - const router: express.Router = express.Router(); + const router: express.Router = express.Router(); - util.setupRedirect(router, '/admin'); - util.route(router, "get", "/all", listAdmins); - util.route(router, "get", "/:id", getAdmin); + util.setupRedirect(router, '/admin'); + util.route(router, "get", "/all", listAdmins); + util.route(router, "get", "/:id", getAdmin); - util.route(router, "post", "/:id", modAdmin); - router.delete('/:id', (req, res) => + util.route(router, "post", "/:id", modAdmin); + router.delete('/:id', (req, res) => util.respOrErrorNoReinject(res, deleteAdmin(req))); - util.addAllInvalidVerbs(router, [ "/", "/all", "/:id" ]); + util.addAllInvalidVerbs(router, [ "/", "/all", "/:id" ]); - return router; + return router; } diff --git a/backend/routes/coach.ts b/backend/routes/coach.ts index 291300c2..df98bbd8 100644 --- a/backend/routes/coach.ts +++ b/backend/routes/coach.ts @@ -14,25 +14,23 @@ import * as util from '../utility'; * `Promise.resolve`, failures using `Promise.reject`. */ async function listCoaches(req: express.Request): Promise { - return rq.parseCoachAllRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then( - async parsed => - ormLU.searchAllCoachLoginUsers(true) - .then(obj => - obj.map(val => ({ - person_data : { - id : val.person.person_id, - name : val.person.firstname + " " + - val.person.lastname - }, - coach : val.is_coach, - admin : val.is_admin, - activated : val.account_status as string - }))) - .then( - obj => Promise.resolve( - {sessionkey : parsed.data.sessionkey, data : obj}))); + return rq.parseCoachAllRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(async parsed => + ormLU.searchAllCoachLoginUsers(true) + .then(obj => + obj.map(val => ({ + person_data : { + id : val.person.person_id, + name : val.person.firstname + " " + val.person.lastname + }, + coach : val.is_coach, + admin : val.is_admin, + activated : val.account_status as string + }))) + .then( + obj => Promise.resolve( + {sessionkey : parsed.data.sessionkey, data : obj}))); } /** @@ -42,11 +40,11 @@ async function listCoaches(req: express.Request): Promise { * `Promise.resolve`, failures using `Promise.reject`. */ async function getCoach(req: express.Request): Promise { - return rq.parseSingleCoachRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then(() => { - return Promise.reject({http : 410, reason : 'Deprecated endpoint.'}); - }); + return rq.parseSingleCoachRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(() => { + return Promise.reject({http : 410, reason : 'Deprecated endpoint.'}); + }); } /** @@ -57,25 +55,25 @@ async function getCoach(req: express.Request): Promise { */ async function modCoach(req: express.Request): Promise> { - return rq.parseUpdateCoachRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then(async parsed => { - return ormLU - .updateLoginUser({ - loginUserId : parsed.data.id, - password : parsed.data.pass, - isAdmin : parsed.data.isAdmin, - isCoach : parsed.data.isCoach, - accountStatus : parsed.data.accountStatus as account_status_enum - }) - .then(res => Promise.resolve({ - sessionkey : parsed.data.sessionkey, - data : { - id : res.person_id, - name : res.person.firstname + " " + res.person.lastname - } - })); - }); + return rq.parseUpdateCoachRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(async parsed => { + return ormLU + .updateLoginUser({ + loginUserId : parsed.data.id, + password : parsed.data.pass, + isAdmin : parsed.data.isAdmin, + isCoach : parsed.data.isCoach, + accountStatus : parsed.data.accountStatus as account_status_enum + }) + .then(res => Promise.resolve({ + sessionkey : parsed.data.sessionkey, + data : { + id : res.person_id, + name : res.person.firstname + " " + res.person.lastname + } + })); + }); } /** @@ -85,12 +83,12 @@ async function modCoach(req: express.Request): * `Promise.resolve`, failures using `Promise.reject`. */ async function deleteCoach(req: express.Request): Promise { - return rq.parseDeleteCoachRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(async parsed => { - return ormLU.deleteLoginUserByPersonId(parsed.data.id) - .then(() => Promise.resolve({sessionkey : parsed.data.sessionkey})); - }); + return rq.parseDeleteCoachRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => { + return ormLU.deleteLoginUserByPersonId(parsed.data.id) + .then(() => Promise.resolve({sessionkey : parsed.data.sessionkey})); + }); } /** @@ -101,25 +99,22 @@ async function deleteCoach(req: express.Request): Promise { */ async function getCoachRequests(req: express.Request): Promise { - return rq.parseGetAllCoachRequestsRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(async parsed => { - return ormLU.getAllLoginUsers() - .then(obj => obj.filter(v => v.is_coach && - v.account_status == 'PENDING') - .map(v => ({ - person_data : { - id : v.person.person_id, - name : v.person.firstname + " " + - v.person.lastname - }, - coach : v.is_coach, - admin : v.is_admin, - activated : v.account_status as string - }))) - .then(arr => Promise.resolve( - {sessionkey : parsed.data.sessionkey, data : arr})); - }); + return rq.parseGetAllCoachRequestsRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => { + return ormLU.getAllLoginUsers() + .then(obj => obj.filter(v => v.is_coach && v.account_status == 'PENDING') + .map(v => ({ + person_data : { + id : v.person.person_id, + name : v.person.firstname + " " + v.person.lastname + }, + coach : v.is_coach, + admin : v.is_admin, + activated : v.account_status as string + }))) + .then(arr => Promise.resolve({sessionkey : parsed.data.sessionkey, data : arr})); + }); } /** @@ -130,33 +125,33 @@ async function getCoachRequests(req: express.Request): */ async function createCoachRequest(req: express.Request): Promise { - return rq.parseRequestCoachRequest(req).then(async parsed => { - if (parsed.pass == undefined) { - console.log(" -> WARNING coach request without password - " + - "currently only accepting email-based applications."); - return Promise.reject(util.errors.cookArgumentError()); - } - return ormP - .createPerson({ - firstname : parsed.firstName, - lastname : parsed.lastName, - email : parsed.emailOrGithub - }) - .then(person => { - console.log("Created a person: " + person); - return ormLU.createLoginUser({ - personId : person.person_id, - password : parsed.pass, - isAdmin : false, - isCoach : true, - accountStatus : 'PENDING' - }) - }) - .then(user => { - console.log("Attached a login user: " + user); - return Promise.resolve({id : user.login_user_id}); - }); - }); + return rq.parseRequestCoachRequest(req).then(async parsed => { + if (parsed.pass == undefined) { + console.log(" -> WARNING coach request without password - " + + "currently only accepting email-based applications."); + return Promise.reject(util.errors.cookArgumentError()); + } + return ormP + .createPerson({ + firstname : parsed.firstName, + lastname : parsed.lastName, + email : parsed.emailOrGithub + }) + .then(person => { + console.log("Created a person: " + person); + return ormLU.createLoginUser({ + personId : person.person_id, + password : parsed.pass, + isAdmin : false, + isCoach : true, + accountStatus : 'PENDING' + }) + }) + .then(user => { + console.log("Attached a login user: " + user); + return Promise.resolve({id : user.login_user_id}); + }); + }); } /** @@ -167,31 +162,31 @@ async function createCoachRequest(req: express.Request): */ async function getCoachRequest(req: express.Request): Promise> { - return rq.parseGetCoachRequestRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(() => { - return Promise.reject({http : 410, reason : 'Deprecated endpoint.'}); - }); + return rq.parseGetCoachRequestRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(() => { + return Promise.reject({http : 410, reason : 'Deprecated endpoint.'}); + }); } async function setAccountStatus(lu_id: number, stat: account_status_enum, key: string): Promise> { - return ormLU.searchLoginUserByPerson(lu_id) - .then(obj => obj == null ? Promise.reject(util.errors.cookInvalidID()) - : ormLU.updateLoginUser({ - loginUserId : obj.login_user_id, - isAdmin : obj.is_admin, - isCoach : obj.is_coach, - accountStatus : stat - })) - .then(res => Promise.resolve({ - sessionkey : key, - data : { - id : res.person_id, - name : res.person.firstname + " " + res.person.lastname - } - })); + return ormLU.searchLoginUserByPerson(lu_id) + .then(obj => obj == null ? Promise.reject(util.errors.cookInvalidID()) + : ormLU.updateLoginUser({ + loginUserId : obj.login_user_id, + isAdmin : obj.is_admin, + isCoach : obj.is_coach, + accountStatus : stat + })) + .then(res => Promise.resolve({ + sessionkey : key, + data : { + id : res.person_id, + name : res.person.firstname + " " + res.person.lastname + } + })); } /** @@ -202,10 +197,9 @@ async function setAccountStatus(lu_id: number, stat: account_status_enum, */ async function createCoachAcceptance(req: express.Request): Promise> { - return rq.parseAcceptNewCoachRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(async parsed => setAccountStatus(parsed.data.id, 'ACTIVATED', - parsed.data.sessionkey)); + return rq.parseAcceptNewCoachRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => setAccountStatus(parsed.data.id, 'ACTIVATED', parsed.data.sessionkey)); } /** @@ -216,10 +210,9 @@ async function createCoachAcceptance(req: express.Request): */ async function deleteCoachRequest(req: express.Request): Promise { - return rq.parseAcceptNewCoachRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(async parsed => setAccountStatus(parsed.data.id, 'DISABLED', - parsed.data.sessionkey)); + return rq.parseAcceptNewCoachRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => setAccountStatus(parsed.data.id, 'DISABLED', parsed.data.sessionkey)); } /** @@ -228,27 +221,27 @@ async function deleteCoachRequest(req: express.Request): * endpoints. */ export function getRouter(): express.Router { - const router: express.Router = express.Router({strict : true}); - util.setupRedirect(router, '/coach'); - util.route(router, "get", "/all", listCoaches); + const router: express.Router = express.Router({strict : true}); + util.setupRedirect(router, '/coach'); + util.route(router, "get", "/all", listCoaches); - util.route(router, "get", "/request", getCoachRequests); - router.post('/request', (req, res) => util.respOrErrorNoReinject( + util.route(router, "get", "/request", getCoachRequests); + router.post('/request', (req, res) => util.respOrErrorNoReinject( res, createCoachRequest(req))); - util.route(router, "get", "/request/:id", getCoachRequest); + util.route(router, "get", "/request/:id", getCoachRequest); - util.route(router, "post", "/request/:id", createCoachAcceptance); - router.delete('/request/:id', (req, res) => util.respOrErrorNoReinject( + util.route(router, "post", "/request/:id", createCoachAcceptance); + router.delete('/request/:id', (req, res) => util.respOrErrorNoReinject( res, deleteCoachRequest(req))); - util.route(router, "get", "/:id", getCoach); + util.route(router, "get", "/:id", getCoach); - util.route(router, "post", "/:id", modCoach); - router.delete('/:id', (req, res) => + util.route(router, "post", "/:id", modCoach); + router.delete('/:id', (req, res) => util.respOrErrorNoReinject(res, deleteCoach(req))); - util.addAllInvalidVerbs(router, + util.addAllInvalidVerbs(router, [ "/", "/all", "/:id", "/request", "/request/:id" ]); - return router; + return router; } diff --git a/backend/routes/project.ts b/backend/routes/project.ts index 696a72ae..bff6ecfc 100644 --- a/backend/routes/project.ts +++ b/backend/routes/project.ts @@ -3,6 +3,8 @@ import express from 'express'; import * as rq from '../request'; import {Responses} from '../types'; import * as util from '../utility'; +import * as ormPr from '../orm_functions/project'; +import {errors} from "../utility"; /** * Attempts to create a new project in the system. @@ -11,14 +13,29 @@ import * as util from '../utility'; * `Promise.resolve`, failures using `Promise.reject`. */ async function createProject(req: express.Request): - Promise> { - return rq.parseNewProjectRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(parsed => { - // INSERTION LOGIC - return Promise.resolve( - {data : '', sessionkey : parsed.data.sessionkey}); - }); + Promise { + return rq.parseNewProjectRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => { + return ormPr.createProject({ + name: parsed.data.name, + partner: parsed.data.partner, + startDate: parsed.data.start, + endDate: parsed.data.end, + positions: parsed.data.positions, + osocId: parsed.data.osocId + }) + .then(project => Promise.resolve({sessionkey : parsed.data.sessionkey, + data: { + id: project.project_id, + name: project.name, + partner: project.partner, + start_date: project.start_date.toString(), + end_date: project.end_date.toString(), + positions: project.positions, + osoc_id: project.osoc_id + }})); + }); } /** @@ -27,15 +44,22 @@ async function createProject(req: express.Request): * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function listProjects(req: express.Request): - Promise { - return rq.parseProjectAllRequest(req) +async function listProjects(req: express.Request): Promise { + return rq.parseProjectAllRequest(req) .then(parsed => util.checkSessionKey(parsed)) - .then(parsed => { - // INSERTION LOGIC - return Promise.resolve( - {data : [], sessionkey : parsed.data.sessionkey}); - }); + .then(async parsed => + ormPr.getAllProjects() + .then(obj => + obj.map(val => ({ + id: Number(val.project_id), + name: val.name, + partner: val.partner, + start_date: val.start_date.toString(), + end_date: val.end_date.toString(), + positions: val.positions, + osoc_id: val.osoc_id + }))) + .then(obj => Promise.resolve({sessionkey:parsed.data.sessionkey, data: obj}))); } /** @@ -47,22 +71,52 @@ async function listProjects(req: express.Request): async function getProject(req: express.Request): Promise { return rq.parseSingleProjectRequest(req) .then(parsed => util.isAdmin(parsed)) - .then(parsed => { - // INSERTION LOGIC - return Promise.resolve( - {data : '', sessionkey : parsed.data.sessionkey}); - }); + .then(async parsed => + ormPr.getProjectById(parsed.data.id) + .then(obj => { + if (obj !== null) { + return Promise.resolve({ + sessionkey: parsed.data.sessionkey, data: { + id: Number(obj.project_id), + name: obj.name, + partner: obj.partner, + start_date: obj.start_date.toString(), + end_date: obj.end_date.toString(), + positions: obj.positions, + osoc_id: obj.osoc_id + } + }); + } else { + return Promise.reject(errors.cookInvalidID()); + } + }) + ); } async function modProject(req: express.Request): - Promise> { - return rq.parseUpdateProjectRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(parsed => { + Promise { + return rq.parseUpdateProjectRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(async parsed => { // UPDATING LOGIC - return Promise.resolve( - {data : '', sessionkey : parsed.data.sessionkey}); - }); + return ormPr.updateProject({ + projectId: parsed.data.id, + name: parsed.data.name, + partner: parsed.data.partner, + startDate: parsed.data.start, + endDate: parsed.data.end, + positions: parsed.data.positions, + osocId: parsed.data.osocId + }).then(project => Promise.resolve({sessionkey : parsed.data.sessionkey, data : { + id: project.project_id, + name: project.name, + partner: project.partner, + start_date: project.start_date.toString(), + end_date: project.end_date.toString(), + positions: project.positions, + osoc_id: project.osoc_id + }})); + }); } /** @@ -72,16 +126,16 @@ async function modProject(req: express.Request): * `Promise.resolve`, failures using `Promise.reject`. */ async function deleteProject(req: express.Request): Promise { - return rq.parseDeleteProjectRequest(req) - .then(parsed => util.isAdmin(parsed)) - .then(parsed => { - // REMOVING LOGIC - return Promise.resolve({sessionkey : parsed.data.sessionkey}); - }); + return rq.parseDeleteProjectRequest(req) + .then(parsed => util.isAdmin(parsed)) + .then(parsed => { + return ormPr.deleteProject(parsed.data.id) + .then(() => Promise.resolve({sessionkey : parsed.data.sessionkey})); + }); } /** - * Attempts to get all data for the requests of coaches in the system. + * Attempts to get all drafted students in the system. * @param req The Express.js request to extract all required data from. * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. @@ -89,15 +143,15 @@ async function deleteProject(req: express.Request): Promise { async function getDraftedStudents(req: express.Request): Promise { - return rq.parseGetDraftedStudentsRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then(parsed => { + return rq.parseGetDraftedStudentsRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(parsed => { // INSERTION LOGIC - return Promise.resolve({ - data : {id : 0, name : '', students : []}, - sessionkey : parsed.data.sessionkey + return Promise.resolve({ + data : {id : 0, name : '', students : []}, + sessionkey : parsed.data.sessionkey + }); }); - }); } async function modProjectStudent(req: express.Request): @@ -138,7 +192,8 @@ export function getRouter(): express.Router { util.setupRedirect(router, '/project'); util.route(router, "get", "/all", listProjects); - util.route(router, "post", "/:id", createProject); + router.post('/:id', (req, res) => util.respOrErrorNoReinject( + res, createProject(req))); util.route(router, "get", "/:id", getProject); util.route(router, "post", "/:id", modProject); diff --git a/backend/routes/student.ts b/backend/routes/student.ts index c3e4d0fa..64994111 100644 --- a/backend/routes/student.ts +++ b/backend/routes/student.ts @@ -45,7 +45,7 @@ async function listStudents(req: express.Request): firstname : student.person.firstname, lastname : student.person.lastname, email : student.person.email, - // gender : student.gender, + gender : student.gender, pronouns : student.pronouns, phoneNumber : student.phone_number, nickname : student.nickname, @@ -76,7 +76,6 @@ async function getStudent(req: express.Request): Promise { .then(parsed => util.checkSessionKey(parsed)) .then(parsed => util.isValidID(parsed.data, "student")) .then(async parsed => { - // FETCHING LOGIC return ormSt.getStudent(parsed.id).then(async student => { if (student !== null) { return ormJo @@ -166,10 +165,10 @@ async function deleteStudent(req: express.Request): Promise { return rq.parseDeleteStudentRequest(req) .then(parsed => util.isAdmin(parsed)) .then(parsed => util.isValidID(parsed.data, 'student')) - .then(async parsed => {// DELETE LOGIC - return ormSt.deleteStudent(parsed.id).then( - () => {return Promise.resolve( - {sessionkey : parsed.sessionkey})})}); + .then(async parsed => { + return ormSt.deleteStudent(parsed.id).then( + () => {return Promise.resolve( + {sessionkey : parsed.sessionkey})})}); } /** @@ -180,36 +179,32 @@ async function deleteStudent(req: express.Request): Promise { */ async function createStudentSuggestion(req: express.Request): Promise { - return rq.parseSuggestStudentRequest(req) - .then(parsed => util.checkSessionKey(parsed)) - .then(async parsed => { - // SUGGESTING LOGIC - return ormSt.getStudent(parsed.data.id).then(async student => { - if (student !== null) { + return rq.parseSuggestStudentRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(async parsed => { + return ormSt.getStudent(parsed.data.id).then(async student => { + if (student !== null) { return ormJo .getLatestJobApplicationOfStudent(student.student_id) .then(async jobApplication => { - if (jobApplication !== null) { - return ormEv - .createEvaluationForStudent({ - loginUserId : parsed.userId, - jobApplicationId : - jobApplication.job_application_id, - decision : parsed.data.suggestion, - motivation : parsed.data.reason, - isFinal : true - }) - .then( - () => {return Promise.resolve( - {sessionkey : parsed.data.sessionkey})}) - } else { - return Promise.reject(errors.cookInvalidID()); - } + if (jobApplication !== null) { + return ormEv + .createEvaluationForStudent({ + loginUserId : parsed.userId, + jobApplicationId : jobApplication.job_application_id, + decision : parsed.data.suggestion, + motivation : parsed.data.reason, + isFinal : true + }) + .then(() => {return Promise.resolve({sessionkey : parsed.data.sessionkey})}) + } else { + return Promise.reject(errors.cookInvalidID()); + } }) - } else { + } else { return Promise.reject(errors.cookInvalidID()); - } - })}); + } + })}); } /** @@ -224,25 +219,41 @@ async function getStudentSuggestions(req: express.Request): .then(parsed => util.checkSessionKey(parsed)) .then(parsed => util.isValidID(parsed.data, 'student')) .then(parsed => { - // FETCHING LOGIC /*let suggestionsList : InternalTypes.SuggestionInfo[] = []; ormSt.getStudent(parsed.id) .then(student => { if (student !== null) { - ormJo.getLatestJobApplicationOfStudent(student.student_id).then(jobApplication - => { if(jobApplication !== null) { - ormJo.getStudentEvaluationsTemp(student.student_id).then(suggestions - => { suggestions.forEach(suggestion => { suggestionsList.push({ - suggestion: suggestion, - sender: - ormEv.getLoginUserByEvaluationId(suggestion.evaluation.evaluation_id) + ormJo.getLatestJobApplicationOfStudent(student.student_id) + .then(jobApplication => { + if (jobApplication !== null) { + ormJo.getStudentEvaluationsTemp(student.student_id) + .then(suggestions => { + suggestions.forEach(suggestion => + suggestion.evaluation.forEach(sug => { + ormEv.getLoginUserByEvaluationId(sug.evaluation_id) + .then(loginUser => { + if(loginUser !== null) { + suggestionsList.push({ + suggestion: sug.decision, + sender: { + name: loginUser.login_user.person.firstname + loginUser.login_user.person.lastname, + id: loginUser.login_user_id + }, + reason: sug.motivation + })) + } + }) + ) + suggestions.forEach(suggestion => + suggestion.evaluation.forEach() + ormEv.getLoginUserByEvaluationId() + ) }) - }) - }) - } else { - return Promise.reject(errors.cookInvalidID()); - } - }) + + } else { + return Promise.reject(errors.cookInvalidID()); + } + }) } else { return Promise.reject(errors.cookInvalidID()); } diff --git a/backend/types.ts b/backend/types.ts index a22d59f2..eb1c2523 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -194,7 +194,14 @@ export interface Admin {} /** * Represents a project, with all associated data. */ -export interface Project {} +export interface Project { + id: number; + name: string; + partner: string; + start_date: string; + end_date: string; + positions: number; +} /** * Represents the drafted students of a project. Usually these will only @@ -341,6 +348,11 @@ export interface AdminList extends Keyed {} */ export interface Project extends Keyed {} +/** + * A project list response is the keyed version of a list of projects + */ +export interface ProjectList extends Keyed {} + /** * An admin list response is the keyed version of the list of admins. */ @@ -424,6 +436,7 @@ export interface CoachRequest { } export interface Project extends KeyRequest { + osocId: number; name: string; partner: string; start: Date; @@ -437,6 +450,7 @@ export interface ModProject extends IdRequest { start?: Date; end?: Date; positions?: number; + osocId?: number; } export interface Draft extends IdRequest { From 199c0550f09423ed3c74fc25b6db23293455837f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 04:40:27 +0000 Subject: [PATCH 003/827] npm-frontend(deps-dev): bump @types/react in /frontend Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.42 to 17.0.43. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 21504e5d..5f923564 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,7 +21,7 @@ "@types/bcrypt": "^5.0.0", "@types/node": "17.0.23", "@types/nodemailer": "^6.4.4", - "@types/react": "17.0.42", + "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.11.0", "eslint-config-next": "12.1.0", @@ -396,9 +396,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "17.0.42", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz", - "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==", + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -3822,9 +3822,9 @@ "dev": true }, "@types/react": { - "version": "17.0.42", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz", - "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==", + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", "dev": true, "requires": { "@types/prop-types": "*", diff --git a/frontend/package.json b/frontend/package.json index 1e6e2917..abd1d8da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "@types/bcrypt": "^5.0.0", "@types/node": "17.0.23", "@types/nodemailer": "^6.4.4", - "@types/react": "17.0.42", + "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.11.0", "eslint-config-next": "12.1.0", From c70fcbd27c25183b58dddd3e7989b8d7b131ec8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 04:40:52 +0000 Subject: [PATCH 004/827] npm-backend(deps): bump prisma from 3.11.0 to 3.11.1 in /backend Bumps [prisma](https://github.com/prisma/prisma/tree/HEAD/packages/cli) from 3.11.0 to 3.11.1. - [Release notes](https://github.com/prisma/prisma/releases) - [Commits](https://github.com/prisma/prisma/commits/3.11.1/packages/cli) --- updated-dependencies: - dependency-name: prisma dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index fc166a1c..46255b47 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1099,9 +1099,9 @@ } }, "node_modules/@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==", + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==", "hasInstallScript": true }, "node_modules/@prisma/engines-version": { @@ -5418,12 +5418,12 @@ } }, "node_modules/prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" }, "bin": { "prisma": "build/index.js", @@ -7587,9 +7587,9 @@ } }, "@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==" + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==" }, "@prisma/engines-version": { "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", @@ -10869,11 +10869,11 @@ } }, "prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "requires": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" } }, "prompts": { From 0024326c11e355d960a09021b76765d3849ba09d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 04:41:15 +0000 Subject: [PATCH 005/827] npm-backend(deps): bump ts-jest from 27.1.3 to 27.1.4 in /backend Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 27.1.3 to 27.1.4. - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/v27.1.4/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v27.1.3...v27.1.4) --- updated-dependencies: - dependency-name: ts-jest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index fc166a1c..d5d26bfe 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -6180,9 +6180,9 @@ } }, "node_modules/ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", @@ -6203,7 +6203,6 @@ "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", - "esbuild": "~0.14.0", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, @@ -11415,9 +11414,9 @@ "requires": {} }, "ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "requires": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", From 9c71c5a71e4307fd9498ac5a7302f06f17ecd5e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 08:39:13 +0000 Subject: [PATCH 006/827] npm-root(deps-dev): bump eslint-config-next from 12.1.0 to 12.1.1 Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.0 to 12.1.1. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.1/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 430 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 326 insertions(+), 106 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a1fc850..e485c209 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", - "eslint-config-next": "^12.1.0", + "eslint-config-next": "^12.1.1", "eslint-plugin-jest": "^26.1.2", "husky": "^7.0.4", "lint-staged": "^12.3.7", @@ -457,9 +457,9 @@ "peer": true }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz", - "integrity": "sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.1.tgz", + "integrity": "sha512-5hd1VFWZzECADhvA+OE+g0CnrRBFZbPm03HbiUtpk7XeluNn7xVxBU6XvNQA+YrQ7qe5jCK9q7R8MbI9R55Y/Q==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -671,9 +671,9 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", - "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", + "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, "node_modules/@tsconfig/node10": { @@ -1798,20 +1798,20 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.0.tgz", - "integrity": "sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.1.tgz", + "integrity": "sha512-+Ql9F07Pafs+cDgy8Zp0F8FxCBq7ke02ZyC2/MMEiGAX+WlnuUCrboBDnfzmHJpAAkcBPjUthunu6LBnF9KWIQ==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.0", - "@rushstack/eslint-patch": "^1.0.8", - "@typescript-eslint/parser": "^5.0.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-import-resolver-typescript": "^2.4.0", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.0", - "eslint-plugin-react-hooks": "^4.3.0" + "@next/eslint-plugin-next": "12.1.1", + "@rushstack/eslint-patch": "1.0.8", + "@typescript-eslint/parser": "5.10.1", + "eslint-import-resolver-node": "0.3.4", + "eslint-import-resolver-typescript": "2.4.0", + "eslint-plugin-import": "2.25.2", + "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-react": "7.29.1", + "eslint-plugin-react-hooks": "4.3.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0", @@ -1824,35 +1824,142 @@ } } }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/scope-manager": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/types": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.10.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" + "debug": "^2.6.9", + "resolve": "^1.13.1" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.0.0" } }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/eslint-import-resolver-typescript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", - "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", + "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", "dev": true, "dependencies": { - "debug": "^4.3.1", - "glob": "^7.1.7", + "debug": "^4.1.1", + "glob": "^7.1.6", "is-glob": "^4.0.1", - "resolve": "^1.20.0", + "resolve": "^1.17.0", "tsconfig-paths": "^3.9.0" }, "engines": { @@ -1944,9 +2051,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", + "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -1954,14 +2061,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.0", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.7.0", "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "tsconfig-paths": "^3.11.0" }, "engines": { "node": ">=4" @@ -1991,6 +2098,31 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2048,9 +2180,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz", - "integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz", + "integrity": "sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -4636,18 +4768,18 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" }, "funding": { @@ -4874,14 +5006,14 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -5570,9 +5702,9 @@ "peer": true }, "@next/eslint-plugin-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz", - "integrity": "sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.1.tgz", + "integrity": "sha512-5hd1VFWZzECADhvA+OE+g0CnrRBFZbPm03HbiUtpk7XeluNn7xVxBU6XvNQA+YrQ7qe5jCK9q7R8MbI9R55Y/Q==", "dev": true, "requires": { "glob": "7.1.7" @@ -5685,9 +5817,9 @@ } }, "@rushstack/eslint-patch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", - "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", + "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, "@tsconfig/node10": { @@ -6504,53 +6636,114 @@ } }, "eslint-config-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.0.tgz", - "integrity": "sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA==", - "dev": true, - "requires": { - "@next/eslint-plugin-next": "12.1.0", - "@rushstack/eslint-patch": "^1.0.8", - "@typescript-eslint/parser": "^5.0.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-import-resolver-typescript": "^2.4.0", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.0", - "eslint-plugin-react-hooks": "^4.3.0" + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.1.tgz", + "integrity": "sha512-+Ql9F07Pafs+cDgy8Zp0F8FxCBq7ke02ZyC2/MMEiGAX+WlnuUCrboBDnfzmHJpAAkcBPjUthunu6LBnF9KWIQ==", + "dev": true, + "requires": { + "@next/eslint-plugin-next": "12.1.1", + "@rushstack/eslint-patch": "1.0.8", + "@typescript-eslint/parser": "5.10.1", + "eslint-import-resolver-node": "0.3.4", + "eslint-import-resolver-typescript": "2.4.0", + "eslint-plugin-import": "2.25.2", + "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-react": "7.29.1", + "eslint-plugin-react-hooks": "4.3.0" + }, + "dependencies": { + "@typescript-eslint/parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" + } + }, + "@typescript-eslint/types": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.10.1", + "eslint-visitor-keys": "^3.0.0" + } + } } }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" + "debug": "^2.6.9", + "resolve": "^1.13.1" }, "dependencies": { "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "eslint-import-resolver-typescript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", - "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", + "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", "dev": true, "requires": { - "debug": "^4.3.1", - "glob": "^7.1.7", + "debug": "^4.1.1", + "glob": "^7.1.6", "is-glob": "^4.0.1", - "resolve": "^1.20.0", + "resolve": "^1.17.0", "tsconfig-paths": "^3.9.0" } }, @@ -6619,9 +6812,9 @@ } }, "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", + "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -6629,14 +6822,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.0", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.7.0", "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "tsconfig-paths": "^3.11.0" }, "dependencies": { "debug": { @@ -6657,6 +6850,33 @@ "esutils": "^2.0.2" } }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -6695,9 +6915,9 @@ } }, "eslint-plugin-react": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz", - "integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz", + "integrity": "sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -8546,18 +8766,18 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" } }, @@ -8704,14 +8924,14 @@ } }, "tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, diff --git a/package.json b/package.json index 110a3af9..93582a53 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", - "eslint-config-next": "^12.1.0", + "eslint-config-next": "^12.1.1", "eslint-plugin-jest": "^26.1.2", "husky": "^7.0.4", "lint-staged": "^12.3.7", From 342576e32ac40689fe0b5f2e4476ab4ffc62cdc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 12:57:40 +0000 Subject: [PATCH 007/827] npm-backend(deps): bump @prisma/client from 3.11.0 to 3.11.1 in /backend Bumps [@prisma/client](https://github.com/prisma/prisma/tree/HEAD/packages/client) from 3.11.0 to 3.11.1. - [Release notes](https://github.com/prisma/prisma/releases) - [Commits](https://github.com/prisma/prisma/commits/3.11.1/packages/client) --- updated-dependencies: - dependency-name: "@prisma/client" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 69 +++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index fc166a1c..64b3784b 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1079,12 +1079,12 @@ } }, "node_modules/@prisma/client": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.11.0.tgz", - "integrity": "sha512-d42o/tlalaWMmNOR4r5BiR6YYTYEV82eZ2lNKOm5ht3WyYwI9e+zy2MyZnNO4Fx5e08RAhW+GRVcEgKl5faUaQ==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.11.1.tgz", + "integrity": "sha512-B3C7zQG4HbjJzUr2Zg9UVkBJutbqq9/uqkl1S138+keZCubJrwizx3RuIvGwI+s+pm3qbsyNqXiZgL3Ir0fSng==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines-version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" }, "engines": { "node": ">=12.6" @@ -1099,15 +1099,15 @@ } }, "node_modules/@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==", + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==", "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-bhMW1XybXZyqCf+9QqjP7Oi7xgVHcISVyOZNMm51qeZsy12M1RtHaCcXUFeMMV0JOCZZuPFVr3+0KVpQqK35CQ==" + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-HkcsDniA4iNb/gi0iuyOJNAM7nD/LwQ0uJm15v360O5dee3TM4lWdSQiTYBMK6FF68ACUItmzSur7oYuUZ2zkQ==" }, "node_modules/@rauschma/stringio": { "version": "1.4.0", @@ -5418,12 +5418,12 @@ } }, "node_modules/prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" }, "bin": { "prisma": "build/index.js", @@ -6180,9 +6180,9 @@ } }, "node_modules/ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", @@ -6203,7 +6203,6 @@ "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", - "esbuild": "~0.14.0", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, @@ -7579,22 +7578,22 @@ } }, "@prisma/client": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.11.0.tgz", - "integrity": "sha512-d42o/tlalaWMmNOR4r5BiR6YYTYEV82eZ2lNKOm5ht3WyYwI9e+zy2MyZnNO4Fx5e08RAhW+GRVcEgKl5faUaQ==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.11.1.tgz", + "integrity": "sha512-B3C7zQG4HbjJzUr2Zg9UVkBJutbqq9/uqkl1S138+keZCubJrwizx3RuIvGwI+s+pm3qbsyNqXiZgL3Ir0fSng==", "requires": { - "@prisma/engines-version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines-version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" } }, "@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==" + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==" }, "@prisma/engines-version": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-bhMW1XybXZyqCf+9QqjP7Oi7xgVHcISVyOZNMm51qeZsy12M1RtHaCcXUFeMMV0JOCZZuPFVr3+0KVpQqK35CQ==" + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-HkcsDniA4iNb/gi0iuyOJNAM7nD/LwQ0uJm15v360O5dee3TM4lWdSQiTYBMK6FF68ACUItmzSur7oYuUZ2zkQ==" }, "@rauschma/stringio": { "version": "1.4.0", @@ -10869,11 +10868,11 @@ } }, "prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "requires": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" } }, "prompts": { @@ -11415,9 +11414,9 @@ "requires": {} }, "ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "requires": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", From 4154ba62f2749ea5443887aff6bedf54579e0a05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 12:57:51 +0000 Subject: [PATCH 008/827] npm-backend(deps-dev): bump dotenv-cli from 5.0.0 to 5.1.0 in /backend Bumps [dotenv-cli](https://github.com/entropitor/dotenv-cli) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/entropitor/dotenv-cli/releases) - [Commits](https://github.com/entropitor/dotenv-cli/compare/v5.0.0...v5.1.0) --- updated-dependencies: - dependency-name: dotenv-cli dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 53 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index fc166a1c..c74cd2cf 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1099,9 +1099,9 @@ } }, "node_modules/@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==", + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==", "hasInstallScript": true }, "node_modules/@prisma/engines-version": { @@ -2519,9 +2519,9 @@ } }, "node_modules/dotenv-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-5.0.0.tgz", - "integrity": "sha512-0Cb2WMDJ805hTD7m43gXXFLraoE5KwrKmGW2dAzYvSEB96tlKI2hmcJ/9In4s2FfvkAFk3SjNQcLeKLoRSXhKA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-5.1.0.tgz", + "integrity": "sha512-NoEZAlKo9WVrG0b3i9mBxdD6INdDuGqdgR74t68t8084QcI077/1MnPerRW1odl+9uULhcdnQp2U0pYVppKHOA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -5418,12 +5418,12 @@ } }, "node_modules/prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" }, "bin": { "prisma": "build/index.js", @@ -6180,9 +6180,9 @@ } }, "node_modules/ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", @@ -6203,7 +6203,6 @@ "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", - "esbuild": "~0.14.0", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, @@ -7587,9 +7586,9 @@ } }, "@prisma/engines": { - "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b.tgz", - "integrity": "sha512-m9iZd5F5vP6A2IvKWfHpOO/qK8OOO9nbsV/pdyEkF/1WNe0E8SIWFBKb+HcMLkG9OFbDDBy8QItXmp/mIULuwQ==" + "version": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9.tgz", + "integrity": "sha512-MILbsGnvmnhCbFGa2/iSnsyGyazU3afzD7ldjCIeLIGKkNBMSZgA2IvpYsAXl+6qFHKGrS3B2otKfV31dwMSQw==" }, "@prisma/engines-version": { "version": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b", @@ -8703,9 +8702,9 @@ "dev": true }, "dotenv-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-5.0.0.tgz", - "integrity": "sha512-0Cb2WMDJ805hTD7m43gXXFLraoE5KwrKmGW2dAzYvSEB96tlKI2hmcJ/9In4s2FfvkAFk3SjNQcLeKLoRSXhKA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-5.1.0.tgz", + "integrity": "sha512-NoEZAlKo9WVrG0b3i9mBxdD6INdDuGqdgR74t68t8084QcI077/1MnPerRW1odl+9uULhcdnQp2U0pYVppKHOA==", "dev": true, "requires": { "cross-spawn": "^7.0.3", @@ -10869,11 +10868,11 @@ } }, "prisma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.0.tgz", - "integrity": "sha512-8SdsLPhKR3mOfoo2o73h9mNn3v5kA/RqGA26Sv6qDS78Eh2uepPqt5e8/nwj5EOblYm5HEGuitaXQrOCLb6uTw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.11.1.tgz", + "integrity": "sha512-aYn8bQwt1xwR2oSsVNHT4PXU7EhsThIwmpNB/MNUaaMx5OPLTro6VdNJe/sJssXFLxhamfWeMjwmpXjljo6xkg==", "requires": { - "@prisma/engines": "3.11.0-48.b371888aaf8f51357c7457d836b86d12da91658b" + "@prisma/engines": "3.11.1-1.1a2506facaf1a4727b7c26850735e88ec779dee9" } }, "prompts": { @@ -11415,9 +11414,9 @@ "requires": {} }, "ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", "requires": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", From 8868233b4c7bcfec4aa7470256033ce61b9183e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 12:58:02 +0000 Subject: [PATCH 009/827] npm-frontend(deps): bump next from 12.1.0 to 12.1.1 in /frontend Bumps [next](https://github.com/vercel/next.js) from 12.1.0 to 12.1.1. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v12.1.0...v12.1.1) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 263 ++++++++++++++++++++----------------- frontend/package.json | 4 +- 2 files changed, 145 insertions(+), 122 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 21504e5d..98d7c4f1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.0", + "next": "12.1.1", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", @@ -21,7 +21,7 @@ "@types/bcrypt": "^5.0.0", "@types/node": "17.0.23", "@types/nodemailer": "^6.4.4", - "@types/react": "17.0.42", + "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.11.0", "eslint-config-next": "12.1.0", @@ -132,9 +132,9 @@ } }, "node_modules/@next/env": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz", - "integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==" + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.1.tgz", + "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "12.1.0", @@ -145,10 +145,25 @@ "glob": "7.1.7" } }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.1.tgz", + "integrity": "sha512-phV9H6d1eK1oVC7nmKKcCXvgOWT4K7aLC/beyO6yvbFC4XtBLE21vPwVl7B4ybz5xjSa6TXoR3TMR6vkW6Mv+A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@next/swc-android-arm64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz", - "integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.1.tgz", + "integrity": "sha512-X5qEz0YeeYT0Gz2wXPAEtRKEuAsLUIEgC/DDfS98t/5Idjv0S4aqIX+TQdzoXP5bwQkIr+mSg+MBIdLtbtnCsA==", "cpu": [ "arm64" ], @@ -161,9 +176,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz", - "integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.1.tgz", + "integrity": "sha512-bKKSNaTdnO3XPnfaR4NSpPcbs80fdbtOYC2lgtqLzA0bOMioupixMP5GrA/gfJHwh7GRH+A+sbgKQWsqSsYAqQ==", "cpu": [ "arm64" ], @@ -176,9 +191,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz", - "integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.1.tgz", + "integrity": "sha512-2VOsA6WLDuDBA6935djohWGGeUIKeQhXwDwu1CKx1b8+6YMMIvFr/y2dpPWoct+5/IjFz84a2MnbABwpoNB9YA==", "cpu": [ "x64" ], @@ -191,9 +206,9 @@ } }, "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz", - "integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.1.tgz", + "integrity": "sha512-1urXtWwqjqbbpJBWeJYz5ATgelKacVNdKIdhfahbsmW+DZGoK5TYovgieyHFYUCyHdTuKeLTVR62ahIRUBv1YA==", "cpu": [ "arm" ], @@ -206,9 +221,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz", - "integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.1.tgz", + "integrity": "sha512-CDD9yFuknDvTOzzDnvfmb58USI5Vu6FUyzw96udKj7KA/n1YrNQ4K8X7KsDCRZoqfRWYceAyj1EpwHkfdiB7bg==", "cpu": [ "arm64" ], @@ -221,9 +236,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz", - "integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.1.tgz", + "integrity": "sha512-nxyjgmbOpZm7gGPj9EV5Cqosoujt+ih/8SO2XG+BetgfAk0+c15793DHVAljNuc8GF9wpzqQnjMMUZ211VmQsg==", "cpu": [ "arm64" ], @@ -236,9 +251,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz", - "integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.1.tgz", + "integrity": "sha512-L8Cu8kH3Vn2dnRpvcvGGA1TlmDP2WXJ+qDwvjb/ikDXLdRdmFvJwHh45JUGiW2FHed3lGseOgNsuYiDvnT8Cdw==", "cpu": [ "x64" ], @@ -251,9 +266,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz", - "integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.1.tgz", + "integrity": "sha512-4RAb7L69MoRSggBqUfF3OrtBCUN2zPDi7asfL7bfxEhH10LGzyfil8dT0GVjPOPFz/SyLx3ORd6avGij2IlJUA==", "cpu": [ "x64" ], @@ -266,9 +281,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz", - "integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.1.tgz", + "integrity": "sha512-zvkuNIgOxkAU3RbzWRGCcFasDxWJdhONt2YeRGe39dJERHhEFA1u4HgaZw/SFE/kfrNRUZbXjJNAg3OU/EpPZw==", "cpu": [ "arm64" ], @@ -281,9 +296,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz", - "integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.1.tgz", + "integrity": "sha512-GsNDtZ//uKWNVjiwv3YKQYsDXuRWTz8jTmxopf5Ws3dK+zA77hn4o46LBQg0JPCNqTUO6eIOlUBjqSL6ejxmSQ==", "cpu": [ "ia32" ], @@ -296,9 +311,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz", - "integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.1.tgz", + "integrity": "sha512-nH5osn/uK9wsjT8Jh1YxMtRrkN5hoCNLQjsEdvfUfb+loQXeYiBd3n/0DUJkf6Scjfv6/htfUTPP3AEa7AbBxQ==", "cpu": [ "x64" ], @@ -396,9 +411,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "17.0.42", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz", - "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==", + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -2351,14 +2366,14 @@ "dev": true }, "node_modules/next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz", - "integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.1.tgz", + "integrity": "sha512-IOfEIAgroMtsoYz6HXpDS+b5WB9WZ+MH266COXGlcpIiYSgUyJf9xV6vF+zY2RPvBJFT4fUW0EVdVnoOmTloDw==", "dependencies": { - "@next/env": "12.1.0", + "@next/env": "12.1.1", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", - "styled-jsx": "5.0.0", + "styled-jsx": "5.0.1", "use-subscription": "1.5.1" }, "bin": { @@ -2368,17 +2383,18 @@ "node": ">=12.22.0" }, "optionalDependencies": { - "@next/swc-android-arm64": "12.1.0", - "@next/swc-darwin-arm64": "12.1.0", - "@next/swc-darwin-x64": "12.1.0", - "@next/swc-linux-arm-gnueabihf": "12.1.0", - "@next/swc-linux-arm64-gnu": "12.1.0", - "@next/swc-linux-arm64-musl": "12.1.0", - "@next/swc-linux-x64-gnu": "12.1.0", - "@next/swc-linux-x64-musl": "12.1.0", - "@next/swc-win32-arm64-msvc": "12.1.0", - "@next/swc-win32-ia32-msvc": "12.1.0", - "@next/swc-win32-x64-msvc": "12.1.0" + "@next/swc-android-arm-eabi": "12.1.1", + "@next/swc-android-arm64": "12.1.1", + "@next/swc-darwin-arm64": "12.1.1", + "@next/swc-darwin-x64": "12.1.1", + "@next/swc-linux-arm-gnueabihf": "12.1.1", + "@next/swc-linux-arm64-gnu": "12.1.1", + "@next/swc-linux-arm64-musl": "12.1.1", + "@next/swc-linux-x64-gnu": "12.1.1", + "@next/swc-linux-x64-musl": "12.1.1", + "@next/swc-win32-arm64-msvc": "12.1.1", + "@next/swc-win32-ia32-msvc": "12.1.1", + "@next/swc-win32-x64-msvc": "12.1.1" }, "peerDependencies": { "fibers": ">= 3.1.0", @@ -3219,14 +3235,14 @@ } }, "node_modules/styled-jsx": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.0.tgz", - "integrity": "sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.1.tgz", + "integrity": "sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==", "engines": { "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || 18.x.x" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { @@ -3669,9 +3685,9 @@ } }, "@next/env": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz", - "integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==" + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.1.tgz", + "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" }, "@next/eslint-plugin-next": { "version": "12.1.0", @@ -3682,70 +3698,76 @@ "glob": "7.1.7" } }, + "@next/swc-android-arm-eabi": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.1.tgz", + "integrity": "sha512-phV9H6d1eK1oVC7nmKKcCXvgOWT4K7aLC/beyO6yvbFC4XtBLE21vPwVl7B4ybz5xjSa6TXoR3TMR6vkW6Mv+A==", + "optional": true + }, "@next/swc-android-arm64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz", - "integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.1.tgz", + "integrity": "sha512-X5qEz0YeeYT0Gz2wXPAEtRKEuAsLUIEgC/DDfS98t/5Idjv0S4aqIX+TQdzoXP5bwQkIr+mSg+MBIdLtbtnCsA==", "optional": true }, "@next/swc-darwin-arm64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz", - "integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.1.tgz", + "integrity": "sha512-bKKSNaTdnO3XPnfaR4NSpPcbs80fdbtOYC2lgtqLzA0bOMioupixMP5GrA/gfJHwh7GRH+A+sbgKQWsqSsYAqQ==", "optional": true }, "@next/swc-darwin-x64": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz", - "integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.1.tgz", + "integrity": "sha512-2VOsA6WLDuDBA6935djohWGGeUIKeQhXwDwu1CKx1b8+6YMMIvFr/y2dpPWoct+5/IjFz84a2MnbABwpoNB9YA==", "optional": true }, "@next/swc-linux-arm-gnueabihf": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz", - "integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.1.tgz", + "integrity": "sha512-1urXtWwqjqbbpJBWeJYz5ATgelKacVNdKIdhfahbsmW+DZGoK5TYovgieyHFYUCyHdTuKeLTVR62ahIRUBv1YA==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz", - "integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.1.tgz", + "integrity": "sha512-CDD9yFuknDvTOzzDnvfmb58USI5Vu6FUyzw96udKj7KA/n1YrNQ4K8X7KsDCRZoqfRWYceAyj1EpwHkfdiB7bg==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz", - "integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.1.tgz", + "integrity": "sha512-nxyjgmbOpZm7gGPj9EV5Cqosoujt+ih/8SO2XG+BetgfAk0+c15793DHVAljNuc8GF9wpzqQnjMMUZ211VmQsg==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz", - "integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.1.tgz", + "integrity": "sha512-L8Cu8kH3Vn2dnRpvcvGGA1TlmDP2WXJ+qDwvjb/ikDXLdRdmFvJwHh45JUGiW2FHed3lGseOgNsuYiDvnT8Cdw==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz", - "integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.1.tgz", + "integrity": "sha512-4RAb7L69MoRSggBqUfF3OrtBCUN2zPDi7asfL7bfxEhH10LGzyfil8dT0GVjPOPFz/SyLx3ORd6avGij2IlJUA==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz", - "integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.1.tgz", + "integrity": "sha512-zvkuNIgOxkAU3RbzWRGCcFasDxWJdhONt2YeRGe39dJERHhEFA1u4HgaZw/SFE/kfrNRUZbXjJNAg3OU/EpPZw==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz", - "integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.1.tgz", + "integrity": "sha512-GsNDtZ//uKWNVjiwv3YKQYsDXuRWTz8jTmxopf5Ws3dK+zA77hn4o46LBQg0JPCNqTUO6eIOlUBjqSL6ejxmSQ==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz", - "integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.1.tgz", + "integrity": "sha512-nH5osn/uK9wsjT8Jh1YxMtRrkN5hoCNLQjsEdvfUfb+loQXeYiBd3n/0DUJkf6Scjfv6/htfUTPP3AEa7AbBxQ==", "optional": true }, "@nodelib/fs.scandir": { @@ -3822,9 +3844,9 @@ "dev": true }, "@types/react": { - "version": "17.0.42", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz", - "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==", + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", "dev": true, "requires": { "@types/prop-types": "*", @@ -5262,25 +5284,26 @@ "dev": true }, "next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz", - "integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==", - "requires": { - "@next/env": "12.1.0", - "@next/swc-android-arm64": "12.1.0", - "@next/swc-darwin-arm64": "12.1.0", - "@next/swc-darwin-x64": "12.1.0", - "@next/swc-linux-arm-gnueabihf": "12.1.0", - "@next/swc-linux-arm64-gnu": "12.1.0", - "@next/swc-linux-arm64-musl": "12.1.0", - "@next/swc-linux-x64-gnu": "12.1.0", - "@next/swc-linux-x64-musl": "12.1.0", - "@next/swc-win32-arm64-msvc": "12.1.0", - "@next/swc-win32-ia32-msvc": "12.1.0", - "@next/swc-win32-x64-msvc": "12.1.0", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.1.tgz", + "integrity": "sha512-IOfEIAgroMtsoYz6HXpDS+b5WB9WZ+MH266COXGlcpIiYSgUyJf9xV6vF+zY2RPvBJFT4fUW0EVdVnoOmTloDw==", + "requires": { + "@next/env": "12.1.1", + "@next/swc-android-arm-eabi": "12.1.1", + "@next/swc-android-arm64": "12.1.1", + "@next/swc-darwin-arm64": "12.1.1", + "@next/swc-darwin-x64": "12.1.1", + "@next/swc-linux-arm-gnueabihf": "12.1.1", + "@next/swc-linux-arm64-gnu": "12.1.1", + "@next/swc-linux-arm64-musl": "12.1.1", + "@next/swc-linux-x64-gnu": "12.1.1", + "@next/swc-linux-x64-musl": "12.1.1", + "@next/swc-win32-arm64-msvc": "12.1.1", + "@next/swc-win32-ia32-msvc": "12.1.1", + "@next/swc-win32-x64-msvc": "12.1.1", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", - "styled-jsx": "5.0.0", + "styled-jsx": "5.0.1", "use-subscription": "1.5.1" } }, @@ -5859,9 +5882,9 @@ "dev": true }, "styled-jsx": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.0.tgz", - "integrity": "sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.1.tgz", + "integrity": "sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==", "requires": {} }, "supports-color": { diff --git a/frontend/package.json b/frontend/package.json index 1e6e2917..498d1a52 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.0", + "next": "12.1.1", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", @@ -22,7 +22,7 @@ "@types/bcrypt": "^5.0.0", "@types/node": "17.0.23", "@types/nodemailer": "^6.4.4", - "@types/react": "17.0.42", + "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.11.0", "eslint-config-next": "12.1.0", From 1ef880fe7dd0b12ab47a83685c542fe83a0378c0 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:08:31 +0100 Subject: [PATCH 010/827] fix: the root packages should only be devDependencies as they are only required for dev testing --- package-lock.json | 5 +---- package.json | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index e485c209..bd118e17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,9 @@ { - "name": "OSOC-2", + "name": "Project", "lockfileVersion": 2, "requires": true, "packages": { "": { - "dependencies": { - "eslint-plugin-jest": "^26.1.1" - }, "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", diff --git a/package.json b/package.json index 93582a53..10cd3253 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,5 @@ }, "scripts": { "prepare": "husky install" - }, - "dependencies": { - "eslint-plugin-jest": "^26.1.1" } } From 7369e6365a0070f550f70be71a69ca141abba8cb Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:10:21 +0100 Subject: [PATCH 011/827] fix(gh-actions): fixes #191 --- .github/workflows/backendCI.yml | 5 ++--- .github/workflows/eslint.yml | 5 ++--- .github/workflows/frontendCI.yml | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/backendCI.yml b/.github/workflows/backendCI.yml index 2c72785d..7e1ceb28 100644 --- a/.github/workflows/backendCI.yml +++ b/.github/workflows/backendCI.yml @@ -3,9 +3,8 @@ name: BackendCI on: - # Run on every open pull request - pull_request: - types: [opened, edited, reopened, synchronize] + # Run on every open pull request, default types are [opened, edited, reopened] + pull_request # Should run all tests and upload the codecoverage on main push: diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index b865ccd3..564116f5 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -4,9 +4,8 @@ name: ESLint on: - # Run on every open pull request - pull_request: - types: [opened, edited, reopened, synchronize] + # Run on every open pull request, default types are [opened, edited, reopened] + pull_request # Should run on every push to main push: diff --git a/.github/workflows/frontendCI.yml b/.github/workflows/frontendCI.yml index 559a5340..a2b2b6db 100644 --- a/.github/workflows/frontendCI.yml +++ b/.github/workflows/frontendCI.yml @@ -4,9 +4,8 @@ name: FrontendCI on: - # Run on every open pull request - pull_request: - types: [opened, edited, reopened, synchronize] + # Run on every open pull request, default types are [opened, edited, reopened] + pull_request # Should run all tests and upload the codecoverage on main push: From 91e9f1566263a926ea4e52ad9a734127740fd801 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:12:59 +0100 Subject: [PATCH 012/827] fix: renamed local folder to avoid project rename in package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index bd118e17..0d1ad489 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "Project", + "name": "OSOC-2", "lockfileVersion": 2, "requires": true, "packages": { From 1049675ac92ede7c1cf817c274359354de85b78b Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:14:50 +0100 Subject: [PATCH 013/827] fix: does this fixes the syntax error? --- .github/workflows/backendCI.yml | 2 +- .github/workflows/eslint.yml | 2 +- .github/workflows/frontendCI.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backendCI.yml b/.github/workflows/backendCI.yml index 7e1ceb28..e16c5e5c 100644 --- a/.github/workflows/backendCI.yml +++ b/.github/workflows/backendCI.yml @@ -4,7 +4,7 @@ name: BackendCI on: # Run on every open pull request, default types are [opened, edited, reopened] - pull_request + pull_request: # Should run all tests and upload the codecoverage on main push: diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 564116f5..215d0afe 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -5,7 +5,7 @@ name: ESLint on: # Run on every open pull request, default types are [opened, edited, reopened] - pull_request + pull_request: # Should run on every push to main push: diff --git a/.github/workflows/frontendCI.yml b/.github/workflows/frontendCI.yml index a2b2b6db..28f70765 100644 --- a/.github/workflows/frontendCI.yml +++ b/.github/workflows/frontendCI.yml @@ -5,7 +5,7 @@ name: FrontendCI on: # Run on every open pull request, default types are [opened, edited, reopened] - pull_request + pull_request: # Should run all tests and upload the codecoverage on main push: From 7be58d7799de63de2a4302c90eae898f5dd08c96 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:35:47 +0100 Subject: [PATCH 014/827] fix: a matrix is probably not needed since we only run on 1 node version --- .github/workflows/backendCI.yml | 7 ++----- .github/workflows/eslint.yml | 9 ++------- .github/workflows/frontendCI.yml | 10 ++-------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/backendCI.yml b/.github/workflows/backendCI.yml index e16c5e5c..dd02f570 100644 --- a/.github/workflows/backendCI.yml +++ b/.github/workflows/backendCI.yml @@ -14,9 +14,6 @@ jobs: ci: runs-on: self-hosted - strategy: - matrix: - node-version: [16.x] services: postgres: # this is for the integration testing of the orm functions image: postgres:14.2 @@ -39,10 +36,10 @@ jobs: touch backend/prisma/.env echo DATABASE_URL="postgresql://osoc2:password@db:5432/osoc2?connect_timeout=30&pool_timeout=30" >> backend/prisma/.env cat backend/prisma/.env - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js 17.x uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: 17.x - run: npm install working-directory: backend diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 215d0afe..1bb68dae 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -14,16 +14,11 @@ on: jobs: ci: runs-on: self-hosted - - strategy: - matrix: - node-version: [16.x] - steps: - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js 17.x uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: 17.x - run: npm install - run: eslint . --ext .js,.jsx,.ts,.tsx diff --git a/.github/workflows/frontendCI.yml b/.github/workflows/frontendCI.yml index 28f70765..5759358e 100644 --- a/.github/workflows/frontendCI.yml +++ b/.github/workflows/frontendCI.yml @@ -13,19 +13,13 @@ on: jobs: ci: - runs-on: self-hosted - - strategy: - matrix: - node-version: [16.x] - steps: - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js 17.x uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: 17.x - run: npm install working-directory: frontend From 6a3309294f868df7779d048fd4047e7bcdb49ca4 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sat, 26 Mar 2022 14:45:49 +0100 Subject: [PATCH 015/827] fix: fix eslint install and actions --- .github/workflows/eslint.yml | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 1bb68dae..6c88327c 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -21,4 +21,4 @@ jobs: with: node-version: 17.x - run: npm install - - run: eslint . --ext .js,.jsx,.ts,.tsx + - run: npm run eslint diff --git a/package.json b/package.json index 10cd3253..02e7ce9f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "*.{js,jsx,ts,tsx}": "eslint --cache --fix" }, "scripts": { - "prepare": "husky install" + "prepare": "husky install", + "eslint": "eslint . --ext .js,.jsx,.ts,.tsx" } } From ff038cd9c31eedf71344f9ce1c8eb83041ef71c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 13:51:12 +0000 Subject: [PATCH 016/827] npm-root(deps): bump eslint-plugin-jest from 26.1.2 to 26.1.3 Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 26.1.2 to 26.1.3. - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v26.1.2...v26.1.3) --- updated-dependencies: - dependency-name: eslint-plugin-jest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d1ad489..da15ff58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", "eslint-config-next": "^12.1.1", - "eslint-plugin-jest": "^26.1.2", + "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", "typescript": "^4.6.2" @@ -2127,15 +2127,15 @@ "dev": true }, "node_modules/eslint-plugin-jest": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz", - "integrity": "sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw==", + "version": "26.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.3.tgz", + "integrity": "sha512-Pju+T7MFpo5VFhFlwrkK/9jRUu18r2iugvgyrWOnnGRaVTFFmFXp+xFJpHyqmjjLmGJPKLeEFLVTAxezkApcpQ==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -6883,9 +6883,9 @@ } }, "eslint-plugin-jest": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz", - "integrity": "sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw==", + "version": "26.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.3.tgz", + "integrity": "sha512-Pju+T7MFpo5VFhFlwrkK/9jRUu18r2iugvgyrWOnnGRaVTFFmFXp+xFJpHyqmjjLmGJPKLeEFLVTAxezkApcpQ==", "dev": true, "requires": { "@typescript-eslint/utils": "^5.10.0" diff --git a/package.json b/package.json index 02e7ce9f..2c22080c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", "eslint-config-next": "^12.1.1", - "eslint-plugin-jest": "^26.1.2", + "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", "typescript": "^4.6.2" From 818e9d6d72726db26b58f61bfd80a19e126c4c07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 14:42:47 +0000 Subject: [PATCH 017/827] npm-root(deps-dev): bump eslint from 8.11.0 to 8.12.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.11.0 to 8.12.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.11.0...v8.12.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index da15ff58..080f55ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.11.0", + "eslint": "^8.12.0", "eslint-config-next": "^12.1.1", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", @@ -1743,9 +1743,9 @@ } }, "node_modules/eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.2.1", @@ -6590,9 +6590,9 @@ "dev": true }, "eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "requires": { "@eslint/eslintrc": "^1.2.1", diff --git a/package.json b/package.json index 2c22080c..ec3647ee 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", - "eslint": "^8.11.0", + "eslint": "^8.12.0", "eslint-config-next": "^12.1.1", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", From 214e1c0343c69827458ba8ce28a6d6896aa54ecc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 14:43:25 +0000 Subject: [PATCH 018/827] npm-frontend(deps-dev): bump eslint from 8.11.0 to 8.12.0 in /frontend Bumps [eslint](https://github.com/eslint/eslint) from 8.11.0 to 8.12.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.11.0...v8.12.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98d7c4f1..5e3a0c6e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,7 +23,7 @@ "@types/nodemailer": "^6.4.4", "@types/react": "17.0.43", "cross-env": "^7.0.3", - "eslint": "8.11.0", + "eslint": "8.12.0", "eslint-config-next": "12.1.0", "typedoc": "^0.22.12", "typescript": "4.6.2" @@ -1073,9 +1073,9 @@ } }, "node_modules/eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.2.1", @@ -4311,9 +4311,9 @@ "dev": true }, "eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", "dev": true, "requires": { "@eslint/eslintrc": "^1.2.1", diff --git a/frontend/package.json b/frontend/package.json index 498d1a52..10b5c5e7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,7 +24,7 @@ "@types/nodemailer": "^6.4.4", "@types/react": "17.0.43", "cross-env": "^7.0.3", - "eslint": "8.11.0", + "eslint": "8.12.0", "eslint-config-next": "12.1.0", "typedoc": "^0.22.12", "typescript": "4.6.2" From 2821b693c0717b1aeb0e08d853284ac511bae66b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 14:48:02 +0000 Subject: [PATCH 019/827] npm-root(deps-dev): bump typescript from 4.6.2 to 4.6.3 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.6.2 to 4.6.3. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.6.2...v4.6.3) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 080f55ce..3c366c16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", - "typescript": "^4.6.2" + "typescript": "^4.6.3" } }, "node_modules/@babel/code-frame": { @@ -5066,9 +5066,9 @@ } }, "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8971,9 +8971,9 @@ "dev": true }, "typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, "unbox-primitive": { diff --git a/package.json b/package.json index ec3647ee..1ed15a00 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", - "typescript": "^4.6.2" + "typescript": "^4.6.3" }, "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" From 6ac1b0784bcaebddec0e9740e9b6cb284ca0911e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 14:51:02 +0000 Subject: [PATCH 020/827] npm-frontend(deps-dev): bump typescript from 4.6.2 to 4.6.3 in /frontend Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.6.2 to 4.6.3. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.6.2...v4.6.3) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5e3a0c6e..98b048af 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,7 +26,7 @@ "eslint": "8.12.0", "eslint-config-next": "12.1.0", "typedoc": "^0.22.12", - "typescript": "4.6.2" + "typescript": "4.6.3" } }, "node_modules/@babel/runtime": { @@ -3449,9 +3449,9 @@ } }, "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6038,9 +6038,9 @@ } }, "typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, "unbox-primitive": { diff --git a/frontend/package.json b/frontend/package.json index 10b5c5e7..b9499c8d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,7 +27,7 @@ "eslint": "8.12.0", "eslint-config-next": "12.1.0", "typedoc": "^0.22.12", - "typescript": "4.6.2" + "typescript": "4.6.3" }, "browser": { "fs": false, From c04ded914171c7b91c295b189e20caf44d73bf6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Mar 2022 14:51:19 +0000 Subject: [PATCH 021/827] npm-backend(deps-dev): bump typescript from 4.6.2 to 4.6.3 in /backend Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.6.2 to 4.6.3. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.6.2...v4.6.3) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 95e7d472..4d3986fa 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -6394,9 +6394,9 @@ } }, "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11544,9 +11544,9 @@ } }, "typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==" + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==" }, "undefsafe": { "version": "2.0.5", From 317eaa92ef9208a325aea2d8ab011b0e78782619 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Mon, 28 Mar 2022 13:45:22 +0200 Subject: [PATCH 022/827] docs(next-js): next.js 101 --- docs/frontend_developer_guide.md | 204 ++++++++++++++++++++++++++ frontend/components/Header/Header.tsx | 2 +- 2 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 docs/frontend_developer_guide.md diff --git a/docs/frontend_developer_guide.md b/docs/frontend_developer_guide.md new file mode 100644 index 00000000..22ae1c5a --- /dev/null +++ b/docs/frontend_developer_guide.md @@ -0,0 +1,204 @@ +# Frontend development with Netx.JS + +This little guide will show you the very basic principles of Next.JS and how to get started and also external links to the official documentation for further reading. + +## File structure + +Next.JS uses file based routing. This means that every file in the paging folder will result in something like `/login` for the login file. Using nested folders you can achieve longer routes and have dynamic routes. For example when you have a folder named `student` containing a file named `[ { + + const {status} = useSession() + + const logOut = (e: SyntheticEvent) => { + e.preventDefault() + signOut({callbackUrl: "/login"}).then() + } + + return ( +
+
+
+ OSOC Logo +
+

Selections

+
+
+ Students + Projects + Manage Users + +
+
+ ) +} +``` + +Every component is a React Functional Component `React.FC` and should return it's representation in XML / HTML format. + +Functions and variables can then be declared inside the Functional Component to be used in the returned XML. + +Components can also receive props / arguments. We will take the Modal compontent as example. `components/Modal/Modal.tsx` + +```typescript +import React, {SyntheticEvent} from "react"; +import styles from './Modal.module.css' + +/** + * A popup modal + * Will display it's children inside the modal + * @param children - The children to be shown inside the modal + * @param visible - Set the modal to be visible or not + * @param handleClose - The callback to close the modal + */ +// eslint-disable-next-line no-unused-vars +export const Modal: React.FC<{ visible: boolean, handleClose: (e :SyntheticEvent) => void }> = ({ + children, + visible, + handleClose + }) => { + return ( +
+
+ + {children} +
+
+ ) +} +``` + +We can see the declaration of props given to the component here `React.FC<{ visible: boolean, handleClose: (e :SyntheticEvent) => void }>`. Now if we wish to include this modal like we do in the login page we just write: + +```typescript + +

Please enter your email below and we will send you a link to reset your password.

+ +

{passwordResetMailError}

+ +
+``` + +Everything that the modal surrounds will be shown inside the modal. + + +Pages work very alike but only have a slight difference in initialization. We can't just pass props like we can with components as they are standalone pages. Given the login page as example, a page in Next.JS should look something like this: + +```typescript +import type {NextPage} from 'next' +import styles from '../styles/login.module.css' +import Image from "next/image" +import GitHubLogo from "../public/images/github-logo.svg" +import {SyntheticEvent, useState} from "react"; +import {Modal} from "../components/Modal/Modal"; +import {useRouter} from "next/router"; +import {signIn} from "next-auth/react"; +import {Header} from "../components/Header/Header"; + +import * as crypto from 'crypto'; + +const Login: NextPage = () => { + + const router = useRouter() + + // Login field values with corresponding error messages + const [loginEmail, setLoginEmail] = useState(""); + const [loginEmailError, setLoginEmailError] = useState(""); + const [loginPassword, setLoginPassword] = useState(""); + const [loginPasswordError, setLoginPasswordError] = useState(""); + const [loginBackendError, setLoginBackendError] = useState(""); + + + [...] // Rest of the declarations have been truncated as it gets very repetitive + + /** + * Executed upon trying to login + * Checks that inputfields are not empty and sends a request to the backend + * Redirects to the home page if succesfull else returns an error message + * + * @param e - The event triggering this function call + */ + const submitLogin = async (e: SyntheticEvent) => {...} + + /** + * Executed upon trying to register + * Checks that inputfields are not empty and sends a request to the backend + * Redirects to the home page if succesfull else returns an error message + * + * @param e - The event triggering this function call + */ + const submitRegister = async (e: SyntheticEvent) => {...} + + /** + * When the users want to login with github we follow the github web authentication application flow + * https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#web-application-flow + * + * Redirect to homepage upon succes + * + * @param e - The event triggering this function call + */ + const githubLogin = (e: SyntheticEvent) => {...} + + /** + * Handles password reset requests from the popup modal + * + * @param e - The event triggering this function call + */ + const resetPassword = (e: SyntheticEvent) => {...} + + // Show password reset modal + const showModal = (e: SyntheticEvent) => {...} + + // Close password reset modal + const closeModal = (e: SyntheticEvent) => {...} + + return ( +
+
+
+

Welcome to OSOC Selections!

+

Please login, or register to proceed

+
+
+
+
) +} +``` + +Note that when working with events you should always do `event.preventDefault()` before anything else. This will prevent their default behavior of reloading the pages which we absolutely do not want. + +Also notice that every variable that needs to update the page is part of the page state and thus should be declader with `[variable, setVariable] = useState()` . You can then set the variable with `setVariable` and it will immediatly be shown on the page. + +## Further reading + +To get an more broad overview and quick start see the official Next.JS quickstart guide: https://nextjs.org/learn + +And the official complete documentation can be found here: +https://nextjs.org/docs diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index badf3016..8e528c35 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -34,4 +34,4 @@ export const Header: React.FC = () => { ) -} \ No newline at end of file +} From 017ffd92ce220bc7257d08dd8569d899abcdfc7a Mon Sep 17 00:00:00 2001 From: Maurice Van Wassenhove <56763273+Mouwrice@users.noreply.github.com> Date: Mon, 28 Mar 2022 13:52:32 +0200 Subject: [PATCH 023/827] docs(next-guide): add images and fix typo's --- docs/frontend_developer_guide.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/frontend_developer_guide.md b/docs/frontend_developer_guide.md index 22ae1c5a..b9f401d2 100644 --- a/docs/frontend_developer_guide.md +++ b/docs/frontend_developer_guide.md @@ -4,7 +4,9 @@ This little guide will show you the very basic principles of Next.JS and how to ## File structure -Next.JS uses file based routing. This means that every file in the paging folder will result in something like `/login` for the login file. Using nested folders you can achieve longer routes and have dynamic routes. For example when you have a folder named `student` containing a file named `[ Date: Mon, 28 Mar 2022 22:02:10 +0200 Subject: [PATCH 024/827] tests: created orm int. tests for project --- backend/orm_functions/project.ts | 6 +- .../orm_integration/integration_setup.ts | 23 ++ backend/tests/orm_integration/project.test.ts | 212 ++++++++++++++++++ 3 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 backend/tests/orm_integration/project.test.ts diff --git a/backend/orm_functions/project.ts b/backend/orm_functions/project.ts index efd516b5..80c6b66e 100644 --- a/backend/orm_functions/project.ts +++ b/backend/orm_functions/project.ts @@ -233,7 +233,7 @@ export async function updateProject(project: UpdateProject){ /** * * @param projectId the project that we are deleting from the project-table - * @returns TODO what does this return? + * @returns return deleted project, with all its fields */ export async function deleteProject(projectId: number){ const result = await prisma.project.delete({ @@ -247,7 +247,7 @@ export async function deleteProject(projectId: number){ /** * * @param osocId the osoc id of all the projects we want to delete - * @returns TODO what does this return? + * @returns returns batchpayload object, with holds count of number of deleted objects */ export async function deleteProjectByOsocEdition(osocId: number){ const result = await prisma.project.deleteMany({ @@ -261,7 +261,7 @@ export async function deleteProjectByOsocEdition(osocId: number){ /** * * @param partner the partner of who we want to delete all the projects - * @returns TODO what does this return? + * @returns returns batchpayload object, with holds count of number of deleted objects */ export async function deleteProjectByPartner(partner: string){ const result = await prisma.project.deleteMany({ diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 0f168044..783d18ec 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -49,6 +49,29 @@ beforeAll(async () => { } ] }); + const osocs = await prisma.osoc.findMany(); + + // create projects + await prisma.project.createMany({ + data: [ + { + name: "project-test", + osoc_id: osocs[1].osoc_id, + partner: "partner-test", + start_date: new Date("2022-05-22"), + end_date: new Date("2022-06-31"), + positions: 10 + }, + { + name: "project-test-2", + osoc_id: osocs[1].osoc_id, + partner: "partner-test-2", + start_date: new Date("2022-09-15"), + end_date: new Date("2022-10-23"), + positions: 9 + }, + ], + }); }); afterAll(async () => { diff --git a/backend/tests/orm_integration/project.test.ts b/backend/tests/orm_integration/project.test.ts new file mode 100644 index 00000000..fabe7a3f --- /dev/null +++ b/backend/tests/orm_integration/project.test.ts @@ -0,0 +1,212 @@ +import {CreateProject, UpdateProject} from "../../orm_functions/orm_types"; +import {getOsocByYear} from "../../orm_functions/osoc"; +import {createProject, getAllProjects, getProjectByName, getProjectsByOsocEdition, + getProjectsByPartner, getProjectsByStartDate, getProjectsByEndDate, getProjectsStartedBeforeDate, getProjectsStartedAfterDate, getProjectsEndedBeforeDate, getProjectsByNumberPositions, updateProject, deleteProject, deleteProjectByOsocEdition, deleteProjectByPartner, getProjectsLessPositions, getProjectsMorePositions} from "../../orm_functions/project"; + + +const project1: UpdateProject = { + projectId: 1, + name: "test-project", + osocId: 0, + partner: "test-partner", + startDate: new Date("2022-07-13"), + endDate: new Date("2022-07-15"), + positions: 7 +} + +const project2: UpdateProject = { + projectId: 2, + name: "different-test", + osocId: 0, + partner: "different-partner", + startDate: new Date("2022-08-13"), + endDate: new Date("2022-08-15"), + positions: 8 +} + +it('should create 1 new project where osoc is 2022', async () => { + const osoc = await getOsocByYear(2022); + project1.osocId = osoc!.osoc_id; + const project0: CreateProject = { + name: "test-project", + osocId: osoc!.osoc_id, + partner: "test-partner", + startDate: new Date("2022-07-13"), + endDate: new Date("2022-07-15"), + positions: 7 + } + + const created_project = await createProject(project0); + expect(created_project).toHaveProperty("name", project0.name); + expect(created_project).toHaveProperty("osoc_id", project0.osocId); + expect(created_project).toHaveProperty("partner", project0.partner); + expect(created_project).toHaveProperty("start_date", project0.startDate); + expect(created_project).toHaveProperty("end_date", project0.endDate); + expect(created_project).toHaveProperty("positions", project0.positions); +}); + +it('should find all the projects in the db, 3 in total', async () => { + const searched_projects = await getAllProjects(); + expect(searched_projects.length).toEqual(3); + expect(searched_projects[2]).toHaveProperty("name", project1.name); + expect(searched_projects[2]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[2]).toHaveProperty("partner", project1.partner); + expect(searched_projects[2]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[2]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[2]).toHaveProperty("positions", project1.positions); +}); + +it('should return the project, by searching for its name', async () => { + const searched_project = await getProjectByName(project1.name); + expect(searched_project[0]).toHaveProperty("name", project1.name); + expect(searched_project[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_project[0]).toHaveProperty("partner", project1.partner); + expect(searched_project[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_project[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_project[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the project, by searching for its osoc edition', async () => { + const osoc = await getOsocByYear(2022); + const searched_project = await getProjectsByOsocEdition(osoc!.osoc_id); + expect(searched_project[0]).toHaveProperty("name", project1.name); + expect(searched_project[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_project[0]).toHaveProperty("partner", project1.partner); + expect(searched_project[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_project[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_project[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for its partner name', async () => { + const searched_projects = await getProjectsByPartner(project1.partner); + expect(searched_projects[0]).toHaveProperty("name", project1.name); + expect(searched_projects[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[0]).toHaveProperty("partner", project1.partner); + expect(searched_projects[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for its start date', async () => { + const searched_projects = await getProjectsByStartDate(project1.startDate); + expect(searched_projects[0]).toHaveProperty("name", project1.name); + expect(searched_projects[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[0]).toHaveProperty("partner", project1.partner); + expect(searched_projects[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for its end date', async () => { + const searched_projects = await getProjectsByEndDate(project1.endDate); + expect(searched_projects[0]).toHaveProperty("name", project1.name); + expect(searched_projects[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[0]).toHaveProperty("partner", project1.partner); + expect(searched_projects[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects starting before date', async () => { + const searched_projects = await getProjectsStartedBeforeDate(new Date("2022-07-31")); + expect(searched_projects[1]).toHaveProperty("name", project1.name); + expect(searched_projects[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[1]).toHaveProperty("partner", project1.partner); + expect(searched_projects[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[1]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects starting after date', async () => { + const searched_projects = await getProjectsStartedAfterDate(new Date("2022-07-01")); + expect(searched_projects[1]).toHaveProperty("name", project1.name); + expect(searched_projects[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[1]).toHaveProperty("partner", project1.partner); + expect(searched_projects[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[1]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects ending before date', async () => { + const searched_projects = await getProjectsEndedBeforeDate(new Date("2022-07-31")); + expect(searched_projects[1]).toHaveProperty("name", project1.name); + expect(searched_projects[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[1]).toHaveProperty("partner", project1.partner); + expect(searched_projects[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[1]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects ending after date', async () => { + const searched_projects = await getProjectsEndedBeforeDate(new Date("2022-07-31")); + expect(searched_projects[1]).toHaveProperty("name", project1.name); + expect(searched_projects[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[1]).toHaveProperty("partner", project1.partner); + expect(searched_projects[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[1]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for its number of positions', async () => { + const searched_projects = await getProjectsByNumberPositions(project1.positions); + expect(searched_projects[0]).toHaveProperty("name", project1.name); + expect(searched_projects[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[0]).toHaveProperty("partner", project1.partner); + expect(searched_projects[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects with less positions', async () => { + const searched_projects = await getProjectsLessPositions(project1.positions + 1); + expect(searched_projects[0]).toHaveProperty("name", project1.name); + expect(searched_projects[0]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[0]).toHaveProperty("partner", project1.partner); + expect(searched_projects[0]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[0]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[0]).toHaveProperty("positions", project1.positions); +}); + +it('should return the projects, by searching for all projects with more positions', async () => { + const searched_projects = await getProjectsMorePositions(project1.positions - 1); + expect(searched_projects[2]).toHaveProperty("name", project1.name); + expect(searched_projects[2]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[2]).toHaveProperty("partner", project1.partner); + expect(searched_projects[2]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[2]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[2]).toHaveProperty("positions", project1.positions); +}); + +it('should update project based upon project id', async () => { + const searched_project = await getProjectByName(project1.name); + project2.projectId = searched_project[0].project_id; + project2.osocId = project1.osocId; + const updated_project = await updateProject(project2); + expect(updated_project).toHaveProperty("name", project2.name); + expect(updated_project).toHaveProperty("osoc_id", project2.osocId); + expect(updated_project).toHaveProperty("partner", project2.partner); + expect(updated_project).toHaveProperty("start_date", project2.startDate); + expect(updated_project).toHaveProperty("end_date", project2.endDate); + expect(updated_project).toHaveProperty("positions", project2.positions); +}); + +it('should delete the project based upon project id', async () => { + const deleted_project = await deleteProject(project2.projectId); + expect(deleted_project).toHaveProperty("name", project2.name); + expect(deleted_project).toHaveProperty("osoc_id", project2.osocId); + expect(deleted_project).toHaveProperty("partner", project2.partner); + expect(deleted_project).toHaveProperty("start_date", project2.startDate); + expect(deleted_project).toHaveProperty("end_date", project2.endDate); + expect(deleted_project).toHaveProperty("positions", project2.positions); +}); + +it('should delete the project based upon project partner', async () => { + const deleted_project = await deleteProjectByPartner("partner-test"); + expect(deleted_project).toHaveProperty("count", 1); +}); + +it('should delete the project based upon osoc id', async () => { + const osoc = await getOsocByYear(2023); + const deleted_project = await deleteProjectByOsocEdition(osoc!.osoc_id); + expect(deleted_project).toHaveProperty("count", 1); +}); From 78c848321321cbd225c29aa76ae49ae455427076 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 03:32:13 +0000 Subject: [PATCH 025/827] npm-root(deps-dev): bump eslint-config-next from 12.1.1 to 12.1.2 Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.1 to 12.1.2. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.2/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c366c16..f2928d5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.12.0", - "eslint-config-next": "^12.1.1", + "eslint-config-next": "^12.1.2", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", @@ -454,9 +454,9 @@ "peer": true }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.1.tgz", - "integrity": "sha512-5hd1VFWZzECADhvA+OE+g0CnrRBFZbPm03HbiUtpk7XeluNn7xVxBU6XvNQA+YrQ7qe5jCK9q7R8MbI9R55Y/Q==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", + "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -1795,12 +1795,12 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.1.tgz", - "integrity": "sha512-+Ql9F07Pafs+cDgy8Zp0F8FxCBq7ke02ZyC2/MMEiGAX+WlnuUCrboBDnfzmHJpAAkcBPjUthunu6LBnF9KWIQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", + "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.1", + "@next/eslint-plugin-next": "12.1.2", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", @@ -5699,9 +5699,9 @@ "peer": true }, "@next/eslint-plugin-next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.1.tgz", - "integrity": "sha512-5hd1VFWZzECADhvA+OE+g0CnrRBFZbPm03HbiUtpk7XeluNn7xVxBU6XvNQA+YrQ7qe5jCK9q7R8MbI9R55Y/Q==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", + "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", "dev": true, "requires": { "glob": "7.1.7" @@ -6633,12 +6633,12 @@ } }, "eslint-config-next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.1.tgz", - "integrity": "sha512-+Ql9F07Pafs+cDgy8Zp0F8FxCBq7ke02ZyC2/MMEiGAX+WlnuUCrboBDnfzmHJpAAkcBPjUthunu6LBnF9KWIQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", + "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", "dev": true, "requires": { - "@next/eslint-plugin-next": "12.1.1", + "@next/eslint-plugin-next": "12.1.2", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", diff --git a/package.json b/package.json index 1ed15a00..7cdf47d3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.12.0", - "eslint-config-next": "^12.1.1", + "eslint-config-next": "^12.1.2", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", From f28cfb83f40e5695b69183074279b10ddd8dafea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 03:32:15 +0000 Subject: [PATCH 026/827] npm-frontend(deps-dev): bump eslint-config-next in /frontend Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.0 to 12.1.2. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.2/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 382 ++++++++++++++++++++++--------------- frontend/package.json | 2 +- 2 files changed, 224 insertions(+), 160 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98b048af..cae1a746 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,7 +24,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.0", + "eslint-config-next": "12.1.2", "typedoc": "^0.22.12", "typescript": "4.6.3" } @@ -137,9 +137,9 @@ "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz", - "integrity": "sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", + "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -369,9 +369,9 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", - "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", + "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, "node_modules/@types/bcrypt": { @@ -428,14 +428,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.13.0.tgz", - "integrity": "sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "debug": "^4.3.2" }, "engines": { @@ -455,13 +455,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz", - "integrity": "sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0" + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -472,9 +472,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.13.0.tgz", - "integrity": "sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -485,13 +485,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz", - "integrity": "sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -512,12 +512,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz", - "integrity": "sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/types": "5.10.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -1125,20 +1125,20 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.0.tgz", - "integrity": "sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", + "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.0", - "@rushstack/eslint-patch": "^1.0.8", - "@typescript-eslint/parser": "^5.0.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-import-resolver-typescript": "^2.4.0", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.0", - "eslint-plugin-react-hooks": "^4.3.0" + "@next/eslint-plugin-next": "12.1.2", + "@rushstack/eslint-patch": "1.0.8", + "@typescript-eslint/parser": "5.10.1", + "eslint-import-resolver-node": "0.3.4", + "eslint-import-resolver-typescript": "2.4.0", + "eslint-plugin-import": "2.25.2", + "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-react": "7.29.1", + "eslint-plugin-react-hooks": "4.3.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0", @@ -1152,34 +1152,40 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" + "debug": "^2.6.9", + "resolve": "^1.13.1" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.0.0" } }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/eslint-import-resolver-typescript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", - "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", + "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", "dev": true, "dependencies": { - "debug": "^4.3.1", - "glob": "^7.1.7", + "debug": "^4.1.1", + "glob": "^7.1.6", "is-glob": "^4.0.1", - "resolve": "^1.20.0", + "resolve": "^1.17.0", "tsconfig-paths": "^3.9.0" }, "engines": { @@ -1213,9 +1219,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", + "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -1223,14 +1229,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.0", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.7.0", "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "tsconfig-paths": "^3.11.0" }, "engines": { "node": ">=4" @@ -1260,6 +1266,31 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1293,9 +1324,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz", - "integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz", + "integrity": "sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -2280,13 +2311,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -3158,18 +3189,18 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" }, "funding": { @@ -3317,14 +3348,14 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "node_modules/tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -3690,9 +3721,9 @@ "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" }, "@next/eslint-plugin-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz", - "integrity": "sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", + "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", "dev": true, "requires": { "glob": "7.1.7" @@ -3802,9 +3833,9 @@ "integrity": "sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg==" }, "@rushstack/eslint-patch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", - "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", + "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, "@types/bcrypt": { @@ -3861,41 +3892,41 @@ "dev": true }, "@typescript-eslint/parser": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.13.0.tgz", - "integrity": "sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", + "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.10.1", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/typescript-estree": "5.10.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz", - "integrity": "sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", + "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0" + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1" } }, "@typescript-eslint/types": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.13.0.tgz", - "integrity": "sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", + "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz", - "integrity": "sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", + "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0", + "@typescript-eslint/types": "5.10.1", + "@typescript-eslint/visitor-keys": "5.10.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -3904,12 +3935,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz", - "integrity": "sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", + "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/types": "5.10.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -4354,53 +4385,59 @@ } }, "eslint-config-next": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.0.tgz", - "integrity": "sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", + "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", "dev": true, "requires": { - "@next/eslint-plugin-next": "12.1.0", - "@rushstack/eslint-patch": "^1.0.8", - "@typescript-eslint/parser": "^5.0.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-import-resolver-typescript": "^2.4.0", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.0", - "eslint-plugin-react-hooks": "^4.3.0" + "@next/eslint-plugin-next": "12.1.2", + "@rushstack/eslint-patch": "1.0.8", + "@typescript-eslint/parser": "5.10.1", + "eslint-import-resolver-node": "0.3.4", + "eslint-import-resolver-typescript": "2.4.0", + "eslint-plugin-import": "2.25.2", + "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-react": "7.29.1", + "eslint-plugin-react-hooks": "4.3.0" } }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" + "debug": "^2.6.9", + "resolve": "^1.13.1" }, "dependencies": { "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "eslint-import-resolver-typescript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", - "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", + "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", "dev": true, "requires": { - "debug": "^4.3.1", - "glob": "^7.1.7", + "debug": "^4.1.1", + "glob": "^7.1.6", "is-glob": "^4.0.1", - "resolve": "^1.20.0", + "resolve": "^1.17.0", "tsconfig-paths": "^3.9.0" } }, @@ -4426,9 +4463,9 @@ } }, "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", + "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -4436,14 +4473,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.0", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.7.0", "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "tsconfig-paths": "^3.11.0" }, "dependencies": { "debug": { @@ -4464,6 +4501,33 @@ "esutils": "^2.0.2" } }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -4493,9 +4557,9 @@ } }, "eslint-plugin-react": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz", - "integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz", + "integrity": "sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -5222,13 +5286,13 @@ "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "minimatch": { @@ -5826,18 +5890,18 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" } }, @@ -5936,14 +6000,14 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "tsconfig-paths": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", - "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, diff --git a/frontend/package.json b/frontend/package.json index b9499c8d..45b2a6c9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.0", + "eslint-config-next": "12.1.2", "typedoc": "^0.22.12", "typescript": "4.6.3" }, From 68d33fbf572d027446cb73b1f2936255ac12bf5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 07:33:35 +0000 Subject: [PATCH 027/827] npm-root(deps-dev): bump @typescript-eslint/eslint-plugin Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.16.0 to 5.17.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.17.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 256 ++++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 224 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2928d5c..bba34dd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.2", @@ -734,14 +734,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -766,6 +766,53 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", @@ -811,12 +858,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -877,15 +924,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -900,6 +947,80 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5880,20 +6001,48 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", - "integrity": "sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/type-utils": "5.16.0", - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + } + }, + "@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + } + } } }, "@typescript-eslint/parser": { @@ -5919,12 +6068,12 @@ } }, "@typescript-eslint/type-utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz", - "integrity": "sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.16.0", + "@typescript-eslint/utils": "5.17.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } @@ -5951,19 +6100,60 @@ } }, "@typescript-eslint/utils": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.16.0.tgz", - "integrity": "sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + } + }, + "@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", diff --git a/package.json b/package.json index 7cdf47d3..1b1e07e9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.2", From 53ccad3abba962e0b9038f9cfa13f4e7a4c85f06 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 09:38:18 +0200 Subject: [PATCH 028/827] chore: uninstall next-auth and next-auth-client --- frontend/package-lock.json | 305 +++---------------------------------- frontend/package.json | 2 - 2 files changed, 20 insertions(+), 287 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98b048af..d2ceaf5a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,8 +11,6 @@ "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", "next": "12.1.1", - "next-auth": "^4.3.1", - "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2" @@ -33,6 +31,7 @@ "version": "7.17.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -360,14 +359,6 @@ "node": ">= 8" } }, - "node_modules/@panva/hkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.1.tgz", - "integrity": "sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", @@ -853,14 +844,6 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/core-js-pure": { "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", @@ -1005,6 +988,8 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -1802,6 +1787,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2028,14 +2015,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -2084,23 +2063,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "node_modules/isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "dependencies": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "node_modules/jose": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.6.0.tgz", - "integrity": "sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2415,57 +2377,11 @@ } } }, - "node_modules/next-auth": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.3.1.tgz", - "integrity": "sha512-DBYEPBLq5naIqh/1i2zEHljcA1OXXecKW3NRU1W4s6R3UX3RdLZ2lWlqgBHUiZQ1zdNikFM/bYQxVGyG7bx8oA==", - "dependencies": { - "@babel/runtime": "^7.16.3", - "@panva/hkdf": "^1.0.1", - "cookie": "^0.4.1", - "jose": "^4.3.7", - "oauth": "^0.9.15", - "openid-client": "^5.1.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" - }, - "engines": { - "node": "^12.19.0 || ^14.15.0 || ^16.13.0" - }, - "peerDependencies": { - "nodemailer": "^6.6.5", - "react": "^17.0.2 || ^18.0.0-0", - "react-dom": "^17.0.2 || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "nodemailer": { - "optional": true - } - } - }, - "node_modules/next-auth-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/next-auth-client/-/next-auth-client-1.5.0.tgz", - "integrity": "sha1-mmSM/778HR/MWSdwpWs3Ps9PNjg=", - "dependencies": { - "isomorphic-fetch": "^2.2.1" - } - }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, - "node_modules/node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "dependencies": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, "node_modules/nodemailer": { "version": "6.7.3", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", @@ -2499,11 +2415,6 @@ "set-blocking": "^2.0.0" } }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2512,14 +2423,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", @@ -2617,14 +2520,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/oidc-token-hash": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", - "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2633,23 +2528,6 @@ "wrappy": "1" } }, - "node_modules/openid-client": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.4.tgz", - "integrity": "sha512-36/PZY3rDgiIFj2uCL9a1fILPmIwu3HksoWO4mukgXe74ZOsEisJMMqTMfmPNw6j/7kO0mBc2xqy4eYRrB8xPA==", - "dependencies": { - "jose": "^4.1.4", - "lru-cache": "^6.0.0", - "object-hash": "^2.0.1", - "oidc-token-hash": "^5.0.1" - }, - "engines": { - "node": "^12.19.0 || ^14.15.0 || ^16.13.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -2787,26 +2665,6 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/preact": { - "version": "10.6.6", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.6.6.tgz", - "integrity": "sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/preact-render-to-string": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.20.tgz", - "integrity": "sha512-ivh2oOGzth0o7XqbatWUQ81WQGoJwSqDKP5z917SoqTWYCAr7dlBzMv3SAMTAu3Gr5g47BJwrvyO44H2Y10ubg==", - "dependencies": { - "pretty-format": "^3.8.0" - }, - "peerDependencies": { - "preact": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2816,11 +2674,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U=" - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -2908,7 +2761,8 @@ "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "node_modules/regexp.prototype.flags": { "version": "1.4.1", @@ -3033,7 +2887,9 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true }, "node_modules/scheduler": { "version": "0.20.2", @@ -3501,14 +3357,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -3532,11 +3380,6 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -3610,6 +3453,7 @@ "version": "7.17.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -3796,11 +3640,6 @@ "fastq": "^1.6.0" } }, - "@panva/hkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.1.tgz", - "integrity": "sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg==" - }, "@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", @@ -4152,11 +3991,6 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" - }, "core-js-pure": { "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", @@ -4261,6 +4095,8 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, "requires": { "iconv-lite": "^0.6.2" } @@ -4877,6 +4713,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -5028,11 +4866,6 @@ "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", "dev": true }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -5066,20 +4899,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "jose": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.6.0.tgz", - "integrity": "sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5307,44 +5126,11 @@ "use-subscription": "1.5.1" } }, - "next-auth": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.3.1.tgz", - "integrity": "sha512-DBYEPBLq5naIqh/1i2zEHljcA1OXXecKW3NRU1W4s6R3UX3RdLZ2lWlqgBHUiZQ1zdNikFM/bYQxVGyG7bx8oA==", - "requires": { - "@babel/runtime": "^7.16.3", - "@panva/hkdf": "^1.0.1", - "cookie": "^0.4.1", - "jose": "^4.3.7", - "oauth": "^0.9.15", - "openid-client": "^5.1.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" - } - }, - "next-auth-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/next-auth-client/-/next-auth-client-1.5.0.tgz", - "integrity": "sha1-mmSM/778HR/MWSdwpWs3Ps9PNjg=", - "requires": { - "isomorphic-fetch": "^2.2.1" - } - }, "node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, "nodemailer": { "version": "6.7.3", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", @@ -5369,21 +5155,11 @@ "set-blocking": "^2.0.0" } }, - "oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" - }, "object-inspect": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", @@ -5451,11 +5227,6 @@ "es-abstract": "^1.19.1" } }, - "oidc-token-hash": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", - "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5464,17 +5235,6 @@ "wrappy": "1" } }, - "openid-client": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.1.4.tgz", - "integrity": "sha512-36/PZY3rDgiIFj2uCL9a1fILPmIwu3HksoWO4mukgXe74ZOsEisJMMqTMfmPNw6j/7kO0mBc2xqy4eYRrB8xPA==", - "requires": { - "jose": "^4.1.4", - "lru-cache": "^6.0.0", - "object-hash": "^2.0.1", - "oidc-token-hash": "^5.0.1" - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5572,30 +5332,12 @@ "source-map-js": "^1.0.1" } }, - "preact": { - "version": "10.6.6", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.6.6.tgz", - "integrity": "sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw==" - }, - "preact-render-to-string": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.20.tgz", - "integrity": "sha512-ivh2oOGzth0o7XqbatWUQ81WQGoJwSqDKP5z917SoqTWYCAr7dlBzMv3SAMTAu3Gr5g47BJwrvyO44H2Y10ubg==", - "requires": { - "pretty-format": "^3.8.0" - } - }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U=" - }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5657,7 +5399,8 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "regexp.prototype.flags": { "version": "1.4.1", @@ -5723,7 +5466,9 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true }, "scheduler": { "version": "0.20.2", @@ -6077,11 +5822,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -6105,11 +5845,6 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index b9499c8d..bd719e75 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,8 +12,6 @@ "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", "next": "12.1.1", - "next-auth": "^4.3.1", - "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2" From d16b07070b1e47257cba635fb5fc4d9035fa75dd Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 10:37:53 +0200 Subject: [PATCH 029/827] feat: setup own session provider and store session key upon login --- frontend/components/Header/Header.tsx | 12 ++-- frontend/pages/_app.tsx | 7 ++- frontend/pages/api/auth/[...nextauth].ts | 41 ------------- frontend/pages/contexts/sessionProvider.tsx | 28 +++++++++ frontend/pages/index.tsx | 24 +++----- frontend/pages/login.tsx | 68 ++++++++++----------- 6 files changed, 76 insertions(+), 104 deletions(-) delete mode 100644 frontend/pages/api/auth/[...nextauth].ts create mode 100644 frontend/pages/contexts/sessionProvider.tsx diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index badf3016..20d74fa1 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -2,16 +2,16 @@ import styles from "./Header.module.css"; import Image from "next/image"; import LogoOsocColor from "../../public/images/logo-osoc-color.svg"; import Link from "next/link"; -import React, {SyntheticEvent} from "react"; -import {signOut, useSession} from "next-auth/react"; +import React, {SyntheticEvent, useContext} from "react"; +import SessionContext from "../../pages/contexts/sessionProvider"; export const Header: React.FC = () => { - const {status} = useSession() + const { sessionKey } = useContext(SessionContext) const logOut = (e: SyntheticEvent) => { e.preventDefault() - signOut({callbackUrl: "/login"}).then() + // TODO } return ( @@ -26,7 +26,7 @@ export const Header: React.FC = () => {

Selections

-
+
Students Projects Manage Users @@ -34,4 +34,4 @@ export const Header: React.FC = () => {
) -} \ No newline at end of file +} diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 7b3a4dad..ce67dbf1 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,13 +1,14 @@ import '../styles/globals.css' import type {AppProps} from 'next/app' -import {SessionProvider} from "next-auth/react"; +import { SessionProvider } from "./contexts/sessionProvider"; -function App({Component, pageProps: {session, ...pageProps}}: AppProps) { +function App({Component, pageProps}: AppProps) { return ( - + + ) } diff --git a/frontend/pages/api/auth/[...nextauth].ts b/frontend/pages/api/auth/[...nextauth].ts deleted file mode 100644 index e408d726..00000000 --- a/frontend/pages/api/auth/[...nextauth].ts +++ /dev/null @@ -1,41 +0,0 @@ -import NextAuth from "next-auth" -import GithubProvider from "next-auth/providers/github" -import CredentialsProvider from "next-auth/providers/credentials"; - -export default NextAuth({ - - // Configure one or more authentication providers - providers: [ - GithubProvider({ - clientId: process.env.GITHUB_ID, - clientSecret: process.env.GITHUB_SECRET, - }), - - CredentialsProvider({ - // The name to display on the sign in form (e.g. "Sign in with...") - name: "Credentials", - // The credentials is used to generate a suitable form on the sign in page. - // You can specify whatever fields you are expecting to be submitted. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - email: { type: "text" }, - password: { type: "password" } - }, - async authorize(credentials) { - - let user = null; - if (credentials) { - user = {id: 1, email: credentials.email} - } - return user - } - }), - ], - - pages: { - signIn: '/login', - signOut: '/' - }, - -}) diff --git a/frontend/pages/contexts/sessionProvider.tsx b/frontend/pages/contexts/sessionProvider.tsx new file mode 100644 index 00000000..9a8548f2 --- /dev/null +++ b/frontend/pages/contexts/sessionProvider.tsx @@ -0,0 +1,28 @@ +import React, { createContext, useState } from 'react'; + + +interface ISessionContext { + sessionKey: string; + setSessionKey?: (key: string) => void +} + +const defaultState = { + sessionKey: "" +} + + +// eslint-disable-next-line no-unused-vars +const SessionContext = createContext(defaultState); + +export const SessionProvider: React.FC<{value: { sessionKey: string; setSessionKey: React.Dispatch>; }}> = ({ children }) => { + const [sessionKey, setSessionKey] = useState(""); + + + return ( + + {children} + + ); +}; + +export default SessionContext; diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 2b63fb20..2959190d 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,25 +1,15 @@ import type {NextPage} from 'next' -import {useRouter} from "next/router"; -import { useSession } from "next-auth/react" -import {useEffect} from "react"; +import {useContext} from "react"; +import SessionContext from "./contexts/sessionProvider"; const Home: NextPage = () => { - const router = useRouter() - const session = useSession() + // TODO + const {sessionKey} = useContext(SessionContext) - useEffect(() => { - console.log(session) - // The user is logged in - if (session !== null && session.status === "authenticated") { - router.push("/students").then() - } else { - // The user is not logged in and will be redirected to /login - router.push("/login").then() - } - }) - - return (<>) + return (<> +

{sessionKey}

+ ) } export default Home; diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index ea864776..7d34b93f 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -2,16 +2,18 @@ import type {NextPage} from 'next' import styles from '../styles/login.module.css' import Image from "next/image" import GitHubLogo from "../public/images/github-logo.svg" -import {SyntheticEvent, useState} from "react"; +import {SyntheticEvent, useContext, useState} from "react"; import {Modal} from "../components/Modal/Modal"; import {useRouter} from "next/router"; -import {signIn} from "next-auth/react"; import {Header} from "../components/Header/Header"; import * as crypto from 'crypto'; +import SessionContext from "./contexts/sessionProvider"; const Login: NextPage = () => { + const {sessionKey, setSessionKey} = useContext(SessionContext) + const router = useRouter() // Login field values with corresponding error messages @@ -72,7 +74,6 @@ const Login: NextPage = () => { const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/login`, { method: 'POST', - //body: JSON.stringify({pass: loginPassword, name: loginEmail}), body: JSON.stringify({pass: encryptedPassword, name: loginEmail}), headers: { 'Content-Type': 'application/json', @@ -90,25 +91,13 @@ const Login: NextPage = () => { setLoginBackendError(`Failed to login. ${err.reason}`); return {success: false}; }); - console.log(response) - if (response.success !== false) { - signIn('credentials', { - email: loginEmail, - password: loginPassword, - redirect: false - }).then(res => { - // TODO -- Redirect or handle errors - console.log(res) - if (res !== undefined) { - const signInRes = res as SignInResult - // The user is succesfully logged in => redirect to /students - if (signInRes.error === null && signInRes.ok && signInRes.status === 200) { - console.log("redirect") - router.push("/students") - } - } - } - ) + + if (response.success) { + console.log(response) + if (setSessionKey) { + setSessionKey(response.sessionkey) + await router.push("/") + } } } } @@ -193,20 +182,21 @@ const Login: NextPage = () => { }); // TODO -- Handle response if(res.success){ - signIn('credentials', { - email: registerEmail, - password: registerPassword, - redirect: false - }).then(res => { - console.log(res) - if (res !== undefined) { - const signInRes = res as SignInResult - // The user is succesfully logged in => redirect to /students - if (signInRes.error === null && signInRes.ok && signInRes.status === 200) { - router.push("/students").then() - } - } - }); + // TODO + //signIn('credentials', { + // email: registerEmail, + // password: registerPassword, + // redirect: false + //}).then(res => { + // console.log(res) + // if (res !== undefined) { + // const signInRes = res as SignInResult + // // The user is succesfully logged in => redirect to /students + // if (signInRes.error === null && signInRes.ok && signInRes.status === 200) { + // router.push("/students").then() + // } + // } + //}); } } } @@ -221,7 +211,10 @@ const Login: NextPage = () => { */ const githubLogin = (e: SyntheticEvent) => { e.preventDefault(); - signIn("github", {callbackUrl: "/students"}).then() + if (setSessionKey) { + setSessionKey("123test") + router.push("/") + } // TODO -- How are we supposed to send the data to the backend? } @@ -265,6 +258,7 @@ const Login: NextPage = () => { return (
+

{sessionKey}

Welcome to OSOC Selections!

Please login, or register to proceed

From 0b4a8c46090e0d938bf4ccb3b546b98af7e1ea35 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 10:59:57 +0200 Subject: [PATCH 030/827] feat: pending page --- frontend/components/Header/Header.tsx | 10 +++++++--- frontend/pages/index.tsx | 5 +++++ frontend/pages/login.tsx | 25 +++++-------------------- frontend/pages/pending.tsx | 20 ++++++++++++++++++++ 4 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 frontend/pages/pending.tsx diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index 20d74fa1..744d8067 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -4,10 +4,13 @@ import LogoOsocColor from "../../public/images/logo-osoc-color.svg"; import Link from "next/link"; import React, {SyntheticEvent, useContext} from "react"; import SessionContext from "../../pages/contexts/sessionProvider"; +import {useRouter} from "next/router"; export const Header: React.FC = () => { - const { sessionKey } = useContext(SessionContext) + const {sessionKey} = useContext(SessionContext) + + const router = useRouter() const logOut = (e: SyntheticEvent) => { e.preventDefault() @@ -26,11 +29,12 @@ export const Header: React.FC = () => {

Selections

-
+
Students Projects Manage Users - +
) diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 2959190d..6b98822b 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -2,6 +2,11 @@ import type {NextPage} from 'next' import {useContext} from "react"; import SessionContext from "./contexts/sessionProvider"; +/** + * Checks if the logged in user is an admin or not. + * Coaches have restricted acces to some pages / features + * @constructor + */ const Home: NextPage = () => { // TODO diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 7d34b93f..d206b7e2 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -92,8 +92,8 @@ const Login: NextPage = () => { return {success: false}; }); + // The user is succesfully logged in and we can use the sessionkey provided by the backend if (response.success) { - console.log(response) if (setSessionKey) { setSessionKey(response.sessionkey) await router.push("/") @@ -155,8 +155,7 @@ const Login: NextPage = () => { // Fields are not empty if (!error) { const encryptedPassword = crypto.createHash('sha256').update(registerPassword).digest('hex'); - console.log(encryptedPassword); - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/coach/request`, { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/coach/request`, { method: 'POST', body: JSON.stringify({ firstName: registerFirstName, @@ -180,23 +179,9 @@ const Login: NextPage = () => { setRegisterBackendError('Failed to register. Please check all fields. ' + json.reason); return Promise.resolve({success: false}); }); - // TODO -- Handle response - if(res.success){ - // TODO - //signIn('credentials', { - // email: registerEmail, - // password: registerPassword, - // redirect: false - //}).then(res => { - // console.log(res) - // if (res !== undefined) { - // const signInRes = res as SignInResult - // // The user is succesfully logged in => redirect to /students - // if (signInRes.error === null && signInRes.ok && signInRes.status === 200) { - // router.push("/students").then() - // } - // } - //}); + + if (response.success) { + await router.push("/pending") } } } diff --git a/frontend/pages/pending.tsx b/frontend/pages/pending.tsx new file mode 100644 index 00000000..cbc7b377 --- /dev/null +++ b/frontend/pages/pending.tsx @@ -0,0 +1,20 @@ +/** + * Will be shown to every user that is not yet accepted by an admin + * TODO + */ + +import {NextPage} from "next"; +import {UnderConstruction} from "../components/UnderConstruction/UnderConstruction"; +import {Header} from "../components/Header/Header"; + +const Pending: NextPage = () => { + return ( + <> +
+

You are not yet accepted by an admin...

+ + + ) +} + +export default Pending; From 146437b0ea1791a424d68e526d8d2c7f3a240a2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:05:09 +0000 Subject: [PATCH 031/827] npm-root(deps-dev): bump @typescript-eslint/parser from 5.16.0 to 5.17.0 Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.16.0 to 5.17.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.17.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 282 ++++++++-------------------------------------- package.json | 2 +- 2 files changed, 47 insertions(+), 237 deletions(-) diff --git a/package-lock.json b/package-lock.json index bba34dd2..3389c5c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.17.0", - "@typescript-eslint/parser": "^5.16.0", + "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.2", "eslint-plugin-jest": "^26.1.3", @@ -766,62 +766,15 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/parser": { "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" }, "engines": { @@ -841,13 +794,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -884,9 +837,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -897,13 +850,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -947,80 +900,6 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1044,12 +923,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -6015,56 +5894,28 @@ "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - } - }, - "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - } - } } }, "@typescript-eslint/parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.16.0.tgz", - "integrity": "sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.16.0", - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/typescript-estree": "5.16.0", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz", - "integrity": "sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0" + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" } }, "@typescript-eslint/type-utils": { @@ -6079,19 +5930,19 @@ } }, "@typescript-eslint/types": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.16.0.tgz", - "integrity": "sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz", - "integrity": "sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", - "@typescript-eslint/visitor-keys": "5.16.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -6113,47 +5964,6 @@ "eslint-utils": "^3.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - } - }, - "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.17.0", - "eslint-visitor-keys": "^3.0.0" - } - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6173,12 +5983,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz", - "integrity": "sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.16.0", + "@typescript-eslint/types": "5.17.0", "eslint-visitor-keys": "^3.0.0" } }, diff --git a/package.json b/package.json index 1b1e07e9..5553b241 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.17.0", - "@typescript-eslint/parser": "^5.16.0", + "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.2", "eslint-plugin-jest": "^26.1.3", From 261f2fc278eb59035e29a51730ebf92d1aafe37c Mon Sep 17 00:00:00 2001 From: jay-tux Date: Tue, 29 Mar 2022 11:14:36 +0200 Subject: [PATCH 032/827] feat: login now informs of your status --- backend/orm_functions/person.ts | 7 ++++++- backend/routes/login.ts | 26 ++++++++++++++++++-------- backend/types.ts | 8 ++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/backend/orm_functions/person.ts b/backend/orm_functions/person.ts index 50ff605b..6daa9917 100644 --- a/backend/orm_functions/person.ts +++ b/backend/orm_functions/person.ts @@ -34,7 +34,12 @@ export async function getPasswordPersonByEmail(email: string) { where : {email : email}, select : { login_user : { - select : {password : true, login_user_id : true, account_status : true} + select : { + password : true, + login_user_id : true, + account_status : true, + is_admin : true + } } } }); diff --git a/backend/routes/login.ts b/backend/routes/login.ts index af4aa250..08ee9a55 100644 --- a/backend/routes/login.ts +++ b/backend/routes/login.ts @@ -1,22 +1,30 @@ import express from 'express'; import {getPasswordPersonByEmail} from '../orm_functions/person'; -import {addSessionKey, removeAllKeysForUser} from '../orm_functions/session_key'; +import { + addSessionKey, + removeAllKeysForUser +} from '../orm_functions/session_key'; import {parseLoginRequest, parseLogoutRequest} from '../request'; import {Responses} from '../types'; import * as util from '../utility'; +function orDefault(v: T|undefined, def: T): T { + return (v == undefined || v == null) ? def : v; +} + /** * Attempts to log a user into the system. * @param req The Express.js request to extract all required data from. * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function login(req: express.Request): Promise { +async function login(req: express.Request): Promise { console.log("Calling login endpoint " + JSON.stringify(req.body)); return parseLoginRequest(req).then( parsed => getPasswordPersonByEmail(parsed.name).then(async pass => { - if (pass?.login_user?.password != parsed.pass) { + if (pass == null || pass.login_user == null || + pass?.login_user?.password != parsed.pass) { return Promise.reject( {http : 409, reason : 'Invalid e-mail or password.'}); } @@ -26,7 +34,10 @@ async function login(req: express.Request): Promise { } const key: string = util.generateKey(); return addSessionKey(pass.login_user.login_user_id, key) - .then(ins => ({sessionkey : ins.session_key})); + .then(ins => ({ + sessionkey : ins.session_key, + is_admin : orDefault(pass?.login_user?.is_admin, false) + })); })); } @@ -39,10 +50,9 @@ async function login(req: express.Request): Promise { async function logout(req: express.Request): Promise { return parseLogoutRequest(req) .then(parsed => util.checkSessionKey(parsed)) - .then(checked => { - return removeAllKeysForUser(checked.data.sessionkey).then(() => { - return Promise.resolve({}); - }); + .then(async checked => { + return removeAllKeysForUser(checked.data.sessionkey) + .then(() => { return Promise.resolve({}); }); }) } diff --git a/backend/types.ts b/backend/types.ts index a22d59f2..cf4ce840 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -266,6 +266,14 @@ export interface Key { sessionkey: InternalTypes.SessionKey; } +/** + * A login response contains of a key and a boolean determining whether a user + * is an admin. + */ +export interface Login extends Key { + is_admin: boolean; +} + /** * A partial student response is the keyed combination of their id and name. */ From 569974c18fb74ac07e3b6e4885531775cb5d1bcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:25:14 +0000 Subject: [PATCH 033/827] npm-frontend(deps): bump next from 12.1.1 to 12.1.2 in /frontend Bumps [next](https://github.com/vercel/next.js) from 12.1.1 to 12.1.2. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v12.1.1...v12.1.2) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 224 ++++++++++++++++++------------------- frontend/package.json | 2 +- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cae1a746..6d89033b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.1", + "next": "12.1.2", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", @@ -132,9 +132,9 @@ } }, "node_modules/@next/env": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.1.tgz", - "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", + "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" }, "node_modules/@next/eslint-plugin-next": { "version": "12.1.2", @@ -146,9 +146,9 @@ } }, "node_modules/@next/swc-android-arm-eabi": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.1.tgz", - "integrity": "sha512-phV9H6d1eK1oVC7nmKKcCXvgOWT4K7aLC/beyO6yvbFC4XtBLE21vPwVl7B4ybz5xjSa6TXoR3TMR6vkW6Mv+A==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz", + "integrity": "sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ==", "cpu": [ "arm" ], @@ -161,9 +161,9 @@ } }, "node_modules/@next/swc-android-arm64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.1.tgz", - "integrity": "sha512-X5qEz0YeeYT0Gz2wXPAEtRKEuAsLUIEgC/DDfS98t/5Idjv0S4aqIX+TQdzoXP5bwQkIr+mSg+MBIdLtbtnCsA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz", + "integrity": "sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.1.tgz", - "integrity": "sha512-bKKSNaTdnO3XPnfaR4NSpPcbs80fdbtOYC2lgtqLzA0bOMioupixMP5GrA/gfJHwh7GRH+A+sbgKQWsqSsYAqQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz", + "integrity": "sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ==", "cpu": [ "arm64" ], @@ -191,9 +191,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.1.tgz", - "integrity": "sha512-2VOsA6WLDuDBA6935djohWGGeUIKeQhXwDwu1CKx1b8+6YMMIvFr/y2dpPWoct+5/IjFz84a2MnbABwpoNB9YA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz", + "integrity": "sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw==", "cpu": [ "x64" ], @@ -206,9 +206,9 @@ } }, "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.1.tgz", - "integrity": "sha512-1urXtWwqjqbbpJBWeJYz5ATgelKacVNdKIdhfahbsmW+DZGoK5TYovgieyHFYUCyHdTuKeLTVR62ahIRUBv1YA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz", + "integrity": "sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw==", "cpu": [ "arm" ], @@ -221,9 +221,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.1.tgz", - "integrity": "sha512-CDD9yFuknDvTOzzDnvfmb58USI5Vu6FUyzw96udKj7KA/n1YrNQ4K8X7KsDCRZoqfRWYceAyj1EpwHkfdiB7bg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz", + "integrity": "sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w==", "cpu": [ "arm64" ], @@ -236,9 +236,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.1.tgz", - "integrity": "sha512-nxyjgmbOpZm7gGPj9EV5Cqosoujt+ih/8SO2XG+BetgfAk0+c15793DHVAljNuc8GF9wpzqQnjMMUZ211VmQsg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz", + "integrity": "sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA==", "cpu": [ "arm64" ], @@ -251,9 +251,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.1.tgz", - "integrity": "sha512-L8Cu8kH3Vn2dnRpvcvGGA1TlmDP2WXJ+qDwvjb/ikDXLdRdmFvJwHh45JUGiW2FHed3lGseOgNsuYiDvnT8Cdw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz", + "integrity": "sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw==", "cpu": [ "x64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.1.tgz", - "integrity": "sha512-4RAb7L69MoRSggBqUfF3OrtBCUN2zPDi7asfL7bfxEhH10LGzyfil8dT0GVjPOPFz/SyLx3ORd6avGij2IlJUA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz", + "integrity": "sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA==", "cpu": [ "x64" ], @@ -281,9 +281,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.1.tgz", - "integrity": "sha512-zvkuNIgOxkAU3RbzWRGCcFasDxWJdhONt2YeRGe39dJERHhEFA1u4HgaZw/SFE/kfrNRUZbXjJNAg3OU/EpPZw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz", + "integrity": "sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw==", "cpu": [ "arm64" ], @@ -296,9 +296,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.1.tgz", - "integrity": "sha512-GsNDtZ//uKWNVjiwv3YKQYsDXuRWTz8jTmxopf5Ws3dK+zA77hn4o46LBQg0JPCNqTUO6eIOlUBjqSL6ejxmSQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz", + "integrity": "sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg==", "cpu": [ "ia32" ], @@ -311,9 +311,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.1.tgz", - "integrity": "sha512-nH5osn/uK9wsjT8Jh1YxMtRrkN5hoCNLQjsEdvfUfb+loQXeYiBd3n/0DUJkf6Scjfv6/htfUTPP3AEa7AbBxQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz", + "integrity": "sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw==", "cpu": [ "x64" ], @@ -2397,11 +2397,11 @@ "dev": true }, "node_modules/next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.1.tgz", - "integrity": "sha512-IOfEIAgroMtsoYz6HXpDS+b5WB9WZ+MH266COXGlcpIiYSgUyJf9xV6vF+zY2RPvBJFT4fUW0EVdVnoOmTloDw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.2.tgz", + "integrity": "sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ==", "dependencies": { - "@next/env": "12.1.1", + "@next/env": "12.1.2", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", "styled-jsx": "5.0.1", @@ -2414,18 +2414,18 @@ "node": ">=12.22.0" }, "optionalDependencies": { - "@next/swc-android-arm-eabi": "12.1.1", - "@next/swc-android-arm64": "12.1.1", - "@next/swc-darwin-arm64": "12.1.1", - "@next/swc-darwin-x64": "12.1.1", - "@next/swc-linux-arm-gnueabihf": "12.1.1", - "@next/swc-linux-arm64-gnu": "12.1.1", - "@next/swc-linux-arm64-musl": "12.1.1", - "@next/swc-linux-x64-gnu": "12.1.1", - "@next/swc-linux-x64-musl": "12.1.1", - "@next/swc-win32-arm64-msvc": "12.1.1", - "@next/swc-win32-ia32-msvc": "12.1.1", - "@next/swc-win32-x64-msvc": "12.1.1" + "@next/swc-android-arm-eabi": "12.1.2", + "@next/swc-android-arm64": "12.1.2", + "@next/swc-darwin-arm64": "12.1.2", + "@next/swc-darwin-x64": "12.1.2", + "@next/swc-linux-arm-gnueabihf": "12.1.2", + "@next/swc-linux-arm64-gnu": "12.1.2", + "@next/swc-linux-arm64-musl": "12.1.2", + "@next/swc-linux-x64-gnu": "12.1.2", + "@next/swc-linux-x64-musl": "12.1.2", + "@next/swc-win32-arm64-msvc": "12.1.2", + "@next/swc-win32-ia32-msvc": "12.1.2", + "@next/swc-win32-x64-msvc": "12.1.2" }, "peerDependencies": { "fibers": ">= 3.1.0", @@ -3716,9 +3716,9 @@ } }, "@next/env": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.1.tgz", - "integrity": "sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==" + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", + "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" }, "@next/eslint-plugin-next": { "version": "12.1.2", @@ -3730,75 +3730,75 @@ } }, "@next/swc-android-arm-eabi": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.1.tgz", - "integrity": "sha512-phV9H6d1eK1oVC7nmKKcCXvgOWT4K7aLC/beyO6yvbFC4XtBLE21vPwVl7B4ybz5xjSa6TXoR3TMR6vkW6Mv+A==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz", + "integrity": "sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ==", "optional": true }, "@next/swc-android-arm64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.1.tgz", - "integrity": "sha512-X5qEz0YeeYT0Gz2wXPAEtRKEuAsLUIEgC/DDfS98t/5Idjv0S4aqIX+TQdzoXP5bwQkIr+mSg+MBIdLtbtnCsA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz", + "integrity": "sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ==", "optional": true }, "@next/swc-darwin-arm64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.1.tgz", - "integrity": "sha512-bKKSNaTdnO3XPnfaR4NSpPcbs80fdbtOYC2lgtqLzA0bOMioupixMP5GrA/gfJHwh7GRH+A+sbgKQWsqSsYAqQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz", + "integrity": "sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ==", "optional": true }, "@next/swc-darwin-x64": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.1.tgz", - "integrity": "sha512-2VOsA6WLDuDBA6935djohWGGeUIKeQhXwDwu1CKx1b8+6YMMIvFr/y2dpPWoct+5/IjFz84a2MnbABwpoNB9YA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz", + "integrity": "sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw==", "optional": true }, "@next/swc-linux-arm-gnueabihf": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.1.tgz", - "integrity": "sha512-1urXtWwqjqbbpJBWeJYz5ATgelKacVNdKIdhfahbsmW+DZGoK5TYovgieyHFYUCyHdTuKeLTVR62ahIRUBv1YA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz", + "integrity": "sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.1.tgz", - "integrity": "sha512-CDD9yFuknDvTOzzDnvfmb58USI5Vu6FUyzw96udKj7KA/n1YrNQ4K8X7KsDCRZoqfRWYceAyj1EpwHkfdiB7bg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz", + "integrity": "sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.1.tgz", - "integrity": "sha512-nxyjgmbOpZm7gGPj9EV5Cqosoujt+ih/8SO2XG+BetgfAk0+c15793DHVAljNuc8GF9wpzqQnjMMUZ211VmQsg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz", + "integrity": "sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.1.tgz", - "integrity": "sha512-L8Cu8kH3Vn2dnRpvcvGGA1TlmDP2WXJ+qDwvjb/ikDXLdRdmFvJwHh45JUGiW2FHed3lGseOgNsuYiDvnT8Cdw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz", + "integrity": "sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.1.tgz", - "integrity": "sha512-4RAb7L69MoRSggBqUfF3OrtBCUN2zPDi7asfL7bfxEhH10LGzyfil8dT0GVjPOPFz/SyLx3ORd6avGij2IlJUA==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz", + "integrity": "sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.1.tgz", - "integrity": "sha512-zvkuNIgOxkAU3RbzWRGCcFasDxWJdhONt2YeRGe39dJERHhEFA1u4HgaZw/SFE/kfrNRUZbXjJNAg3OU/EpPZw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz", + "integrity": "sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.1.tgz", - "integrity": "sha512-GsNDtZ//uKWNVjiwv3YKQYsDXuRWTz8jTmxopf5Ws3dK+zA77hn4o46LBQg0JPCNqTUO6eIOlUBjqSL6ejxmSQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz", + "integrity": "sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.1.tgz", - "integrity": "sha512-nH5osn/uK9wsjT8Jh1YxMtRrkN5hoCNLQjsEdvfUfb+loQXeYiBd3n/0DUJkf6Scjfv6/htfUTPP3AEa7AbBxQ==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz", + "integrity": "sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw==", "optional": true }, "@nodelib/fs.scandir": { @@ -5348,23 +5348,23 @@ "dev": true }, "next": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.1.tgz", - "integrity": "sha512-IOfEIAgroMtsoYz6HXpDS+b5WB9WZ+MH266COXGlcpIiYSgUyJf9xV6vF+zY2RPvBJFT4fUW0EVdVnoOmTloDw==", - "requires": { - "@next/env": "12.1.1", - "@next/swc-android-arm-eabi": "12.1.1", - "@next/swc-android-arm64": "12.1.1", - "@next/swc-darwin-arm64": "12.1.1", - "@next/swc-darwin-x64": "12.1.1", - "@next/swc-linux-arm-gnueabihf": "12.1.1", - "@next/swc-linux-arm64-gnu": "12.1.1", - "@next/swc-linux-arm64-musl": "12.1.1", - "@next/swc-linux-x64-gnu": "12.1.1", - "@next/swc-linux-x64-musl": "12.1.1", - "@next/swc-win32-arm64-msvc": "12.1.1", - "@next/swc-win32-ia32-msvc": "12.1.1", - "@next/swc-win32-x64-msvc": "12.1.1", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.2.tgz", + "integrity": "sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ==", + "requires": { + "@next/env": "12.1.2", + "@next/swc-android-arm-eabi": "12.1.2", + "@next/swc-android-arm64": "12.1.2", + "@next/swc-darwin-arm64": "12.1.2", + "@next/swc-darwin-x64": "12.1.2", + "@next/swc-linux-arm-gnueabihf": "12.1.2", + "@next/swc-linux-arm64-gnu": "12.1.2", + "@next/swc-linux-arm64-musl": "12.1.2", + "@next/swc-linux-x64-gnu": "12.1.2", + "@next/swc-linux-x64-musl": "12.1.2", + "@next/swc-win32-arm64-msvc": "12.1.2", + "@next/swc-win32-ia32-msvc": "12.1.2", + "@next/swc-win32-x64-msvc": "12.1.2", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", "styled-jsx": "5.0.1", diff --git a/frontend/package.json b/frontend/package.json index 45b2a6c9..57a40325 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.1", + "next": "12.1.2", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", From 218424ffeb3326330839a16668261280f331d474 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 11:47:22 +0200 Subject: [PATCH 034/827] fix: some cleanup and github shenanigans --- frontend/pages/contexts/sessionProvider.tsx | 2 +- frontend/pages/login.tsx | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/frontend/pages/contexts/sessionProvider.tsx b/frontend/pages/contexts/sessionProvider.tsx index 9a8548f2..0955fe73 100644 --- a/frontend/pages/contexts/sessionProvider.tsx +++ b/frontend/pages/contexts/sessionProvider.tsx @@ -14,7 +14,7 @@ const defaultState = { // eslint-disable-next-line no-unused-vars const SessionContext = createContext(defaultState); -export const SessionProvider: React.FC<{value: { sessionKey: string; setSessionKey: React.Dispatch>; }}> = ({ children }) => { +export const SessionProvider: React.FC = ({ children }) => { const [sessionKey, setSessionKey] = useState(""); diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index d206b7e2..4aed45a9 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -81,11 +81,10 @@ const Login: NextPage = () => { } }) .then(response => response.json()).then(json => { - if(!json.success) { + if (!json.success) { setLoginBackendError(`Failed to login. ${json.reason}`); return {success: false}; - } - else return json; + } else return json; }) .catch(err => { setLoginBackendError(`Failed to login. ${err.reason}`); @@ -169,11 +168,10 @@ const Login: NextPage = () => { } }) .then(response => response.json()).then(json => { - if(!json.success) { + if (!json.success) { setRegisterBackendError('Failed to register. Please check all fields. ' + json.reason); return Promise.resolve({success: false}); - } - else return json; + } else return json; }) .catch(json => { setRegisterBackendError('Failed to register. Please check all fields. ' + json.reason); @@ -194,12 +192,8 @@ const Login: NextPage = () => { * * @param e - The event triggering this function call */ - const githubLogin = (e: SyntheticEvent) => { + const githubLogin = async (e: SyntheticEvent) => { e.preventDefault(); - if (setSessionKey) { - setSessionKey("123test") - router.push("/") - } // TODO -- How are we supposed to send the data to the backend? } From c49d947905a2ba4209be127420f57574c0cdcffc Mon Sep 17 00:00:00 2001 From: jay-tux Date: Tue, 29 Mar 2022 12:39:02 +0200 Subject: [PATCH 035/827] feat: login now informs of your status (v2: both is_admin and is_coach) --- backend/orm_functions/person.ts | 3 ++- backend/routes/login.ts | 3 ++- backend/types.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/orm_functions/person.ts b/backend/orm_functions/person.ts index 6daa9917..57d59a82 100644 --- a/backend/orm_functions/person.ts +++ b/backend/orm_functions/person.ts @@ -38,7 +38,8 @@ export async function getPasswordPersonByEmail(email: string) { password : true, login_user_id : true, account_status : true, - is_admin : true + is_admin : true, + is_coach : true } } } diff --git a/backend/routes/login.ts b/backend/routes/login.ts index 08ee9a55..9095489e 100644 --- a/backend/routes/login.ts +++ b/backend/routes/login.ts @@ -36,7 +36,8 @@ async function login(req: express.Request): Promise { return addSessionKey(pass.login_user.login_user_id, key) .then(ins => ({ sessionkey : ins.session_key, - is_admin : orDefault(pass?.login_user?.is_admin, false) + is_admin : orDefault(pass?.login_user?.is_admin, false), + is_coach : orDefault(pass?.login_user?.is_coach, false) })); })); } diff --git a/backend/types.ts b/backend/types.ts index cf4ce840..74f08930 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -272,6 +272,7 @@ export interface Key { */ export interface Login extends Key { is_admin: boolean; + is_coach: boolean; } /** From d8338ef48c436c35dd7c9e1adbc3e94cf463eb00 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 13:12:52 +0200 Subject: [PATCH 036/827] fix: contexts should not be under the pages folder --- frontend/components/Header/Header.tsx | 2 +- frontend/{pages => }/contexts/sessionProvider.tsx | 0 frontend/pages/_app.tsx | 2 +- frontend/pages/index.tsx | 2 +- frontend/pages/login.tsx | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename frontend/{pages => }/contexts/sessionProvider.tsx (100%) diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index 744d8067..bc814aa6 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -3,7 +3,7 @@ import Image from "next/image"; import LogoOsocColor from "../../public/images/logo-osoc-color.svg"; import Link from "next/link"; import React, {SyntheticEvent, useContext} from "react"; -import SessionContext from "../../pages/contexts/sessionProvider"; +import SessionContext from "../../contexts/sessionProvider"; import {useRouter} from "next/router"; export const Header: React.FC = () => { diff --git a/frontend/pages/contexts/sessionProvider.tsx b/frontend/contexts/sessionProvider.tsx similarity index 100% rename from frontend/pages/contexts/sessionProvider.tsx rename to frontend/contexts/sessionProvider.tsx diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index ce67dbf1..bbf77cc7 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,6 +1,6 @@ import '../styles/globals.css' import type {AppProps} from 'next/app' -import { SessionProvider } from "./contexts/sessionProvider"; +import { SessionProvider } from "../contexts/sessionProvider"; function App({Component, pageProps}: AppProps) { diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 6b98822b..ca20a5ef 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,6 +1,6 @@ import type {NextPage} from 'next' import {useContext} from "react"; -import SessionContext from "./contexts/sessionProvider"; +import SessionContext from "../contexts/sessionProvider"; /** * Checks if the logged in user is an admin or not. diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 4aed45a9..e45d308d 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -8,7 +8,7 @@ import {useRouter} from "next/router"; import {Header} from "../components/Header/Header"; import * as crypto from 'crypto'; -import SessionContext from "./contexts/sessionProvider"; +import SessionContext from "../contexts/sessionProvider"; const Login: NextPage = () => { From 5dc60857f91539938c09c3c8638c4b2945d95d98 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 14:03:40 +0200 Subject: [PATCH 037/827] feat: login is starting to be pretty solid --- frontend/components/Header/Header.tsx | 25 +++++++++++++++++++------ frontend/contexts/sessionProvider.tsx | 21 ++++++++++++++++----- frontend/pages/index.tsx | 20 ++++++++++++++------ frontend/pages/login.tsx | 10 ++++++++-- frontend/pages/pending.tsx | 14 ++++++-------- frontend/styles/pending.module.css | 3 +++ 6 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 frontend/styles/pending.module.css diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index bc814aa6..2ab226e9 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -8,13 +8,26 @@ import {useRouter} from "next/router"; export const Header: React.FC = () => { - const {sessionKey} = useContext(SessionContext) + const {sessionKey, setSessionKey, isAdmin, setIsAdmin, setIsCoach} = useContext(SessionContext) const router = useRouter() + /** + * Resets the session state and redirects to the login page + * @param e + */ const logOut = (e: SyntheticEvent) => { e.preventDefault() - // TODO + if (setSessionKey) { + setSessionKey("") + } + if (setIsAdmin) { + setIsAdmin(false) + } + if (setIsCoach) { + setIsCoach(false) + } + router.push("/login").then() } return ( @@ -29,10 +42,10 @@ export const Header: React.FC = () => {

Selections

-
- Students - Projects - Manage Users +
+ {sessionKey !== "" ? Students : null} + {sessionKey !== "" ? Projects : null} + {isAdmin ? Manage Users : null}
diff --git a/frontend/contexts/sessionProvider.tsx b/frontend/contexts/sessionProvider.tsx index 0955fe73..ac5e694d 100644 --- a/frontend/contexts/sessionProvider.tsx +++ b/frontend/contexts/sessionProvider.tsx @@ -1,25 +1,36 @@ import React, { createContext, useState } from 'react'; - +/** + * Interface for the context, stores the user session application wide + */ interface ISessionContext { sessionKey: string; - setSessionKey?: (key: string) => void + setSessionKey?: (key: string) => void; + + isCoach: boolean; + setIsCoach?: (coach: boolean) => void; + isAdmin: boolean; + setIsAdmin?: (admin: boolean) => void; } +// The default state the application is in const defaultState = { - sessionKey: "" + sessionKey: "", // No user is logged in + isCoach: false, + isAdmin: false } -// eslint-disable-next-line no-unused-vars const SessionContext = createContext(defaultState); export const SessionProvider: React.FC = ({ children }) => { const [sessionKey, setSessionKey] = useState(""); + const [isCoach, setIsCoach] = useState(false); + const [isAdmin, setIsAdmin] = useState(false); return ( - + {children} ); diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index ca20a5ef..ec69f00b 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,20 +1,28 @@ import type {NextPage} from 'next' -import {useContext} from "react"; +import {useContext, useEffect} from "react"; import SessionContext from "../contexts/sessionProvider"; +import {useRouter} from "next/router"; /** - * Checks if the logged in user is an admin or not. + * Checks if a user is logged in and redirects to the corresponding page * Coaches have restricted acces to some pages / features * @constructor */ const Home: NextPage = () => { - // TODO const {sessionKey} = useContext(SessionContext) + const router = useRouter() - return (<> -

{sessionKey}

- ) + useEffect(() => { + // No user is logged in + if (sessionKey === "") { + router.push("/login").then() + } else { + router.push("/students").then() + } + }, [router, sessionKey]) + + return (<>) } export default Home; diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index e45d308d..0ca43da6 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -12,7 +12,7 @@ import SessionContext from "../contexts/sessionProvider"; const Login: NextPage = () => { - const {sessionKey, setSessionKey} = useContext(SessionContext) + const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) const router = useRouter() @@ -95,8 +95,14 @@ const Login: NextPage = () => { if (response.success) { if (setSessionKey) { setSessionKey(response.sessionkey) - await router.push("/") } + if (setIsAdmin) { + setIsAdmin(response.is_admin) + } + if (setIsCoach) { + setIsCoach(response.is_coach) + } + router.push("/").then() } } } diff --git a/frontend/pages/pending.tsx b/frontend/pages/pending.tsx index cbc7b377..4d8181d2 100644 --- a/frontend/pages/pending.tsx +++ b/frontend/pages/pending.tsx @@ -1,18 +1,16 @@ -/** - * Will be shown to every user that is not yet accepted by an admin - * TODO - */ - import {NextPage} from "next"; -import {UnderConstruction} from "../components/UnderConstruction/UnderConstruction"; import {Header} from "../components/Header/Header"; +import styles from "../styles/pending.module.css" +/** + * Will be shown to every user that is not yet accepted by an admin + */ const Pending: NextPage = () => { return ( <>
-

You are not yet accepted by an admin...

- +

Your account is pending approval of an admin.

+

Awaiting approval...

) } diff --git a/frontend/styles/pending.module.css b/frontend/styles/pending.module.css new file mode 100644 index 00000000..78b65882 --- /dev/null +++ b/frontend/styles/pending.module.css @@ -0,0 +1,3 @@ +.pending { + text-align: center +} From b1a7855afa45608f538763bef9e05de38211f6da Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Tue, 29 Mar 2022 14:09:49 +0200 Subject: [PATCH 038/827] test: add testdata for job application and evaluation to the integration_setup --- .../orm_integration/integration_setup.ts | 78 +++++++++++++++++ .../orm_integration/job_application.test.ts | 6 ++ backend/tests/orm_integration/person.test.ts | 86 +++++++++---------- 3 files changed, 127 insertions(+), 43 deletions(-) create mode 100644 backend/tests/orm_integration/job_application.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 783d18ec..ebcaa355 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -1,4 +1,5 @@ import prisma from "../../prisma/prisma"; +import {decision_enum, email_status_enum} from "@prisma/client"; beforeAll(async () => { // create persons @@ -14,6 +15,11 @@ beforeAll(async () => { firstname: "firstNameTest2", lastname: "lastNameTest2", }, + { + email: 'testmail2@mail.com', + firstname: "first", + lastname: "last" + } ], }); const persons = await prisma.person.findMany(); @@ -38,6 +44,8 @@ beforeAll(async () => { ], }); + const login_users = await prisma.login_user.findMany(); + // create osoc editions await prisma.osoc.createMany({ data: [ @@ -72,6 +80,76 @@ beforeAll(async () => { }, ], }); + + await prisma.student.createMany({ + data : [ + { + person_id: persons[2].person_id, + gender: "Male", + pronouns: ["He", "Him"], + phone_number: "112", + alumni: false, + } + ] + }) + const students = await prisma.student.findMany(); + // create evaluations + await prisma.job_application.createMany({ + data : [ + { + student_id: students[0].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "no responsibilities", + fun_fact: "this is a fun fact", + student_coach: false, + osoc_id: osocs[0].osoc_id, + edus: ["basic education"], + edu_level: "higher education", + edu_duration: 5, + edu_institute: "Ugent", + edu_year: 2022, + email_status: email_status_enum.DRAFT, + created_at: new Date(), + }, + { + student_id: students[0].student_id, + student_volunteer_info: "I'd like to volunteer", + responsibilities: "no responsibilities2", + fun_fact: "this is a fun fact too", + student_coach: true, + osoc_id: osocs[0].osoc_id, + edus: ["higher education"], + edu_level: "MaNaMa", + edu_duration: 8, + edu_institute: "Ugent", + edu_year: 2023, + email_status: email_status_enum.SENT, + created_at: new Date(), + } + ] + }); + + const job_applications = await prisma.job_application.findMany(); + + // create evaluations + await prisma.evaluation.createMany({ + data : [ + { + login_user_id: login_users[0].login_user_id, + job_application_id: job_applications[0].job_application_id, + decision: decision_enum.MAYBE, + motivation: "low education level", + is_final: false, + }, + { + login_user_id: login_users[0].login_user_id, + job_application_id: job_applications[1].job_application_id, + decision: decision_enum.YES, + motivation: "awesome job applicaton", + is_final: true + } + ] + }) }); afterAll(async () => { diff --git a/backend/tests/orm_integration/job_application.test.ts b/backend/tests/orm_integration/job_application.test.ts new file mode 100644 index 00000000..d2ca80c2 --- /dev/null +++ b/backend/tests/orm_integration/job_application.test.ts @@ -0,0 +1,6 @@ +import prisma from "../../prisma/prisma"; + +it("should do nothing", async () => { + await prisma.job_application.findMany(); + expect(true).toBeTruthy(); +}); \ No newline at end of file diff --git a/backend/tests/orm_integration/person.test.ts b/backend/tests/orm_integration/person.test.ts index 416953b6..1957d8b7 100644 --- a/backend/tests/orm_integration/person.test.ts +++ b/backend/tests/orm_integration/person.test.ts @@ -3,19 +3,15 @@ import {createPerson, getAllPersons, searchPersonByName, searchPersonByLogin, updatePerson, deletePersonById} from "../../orm_functions/person"; -const person2 : UpdatePerson = { - personId: 2, +const person3 : CreatePerson = { email: "test@email.be", - github: null, firstname: "first_name", lastname: "last_name", } -const person3: UpdatePerson = { - personId: 3, +const person4: CreatePerson = { github: "testhub.com", - email: null, - firstname: "person3", + firstname: "person4", lastname: "second name", } @@ -36,7 +32,7 @@ it('should create 1 new person where github is null', async () => { it('should create 1 new person where email is null', async () => { const person1: CreatePerson = { github: "testhub.com", - firstname: "person3", + firstname: "person4", lastname: "second name", } @@ -49,62 +45,66 @@ it('should create 1 new person where email is null', async () => { it('should find all the persons in the db, 2 in total', async () => { const searched_persons = await getAllPersons(); - expect(searched_persons[2]).toHaveProperty("github", person2.github); - expect(searched_persons[2]).toHaveProperty("firstname", person2.firstname); - expect(searched_persons[2]).toHaveProperty("lastname", person2.lastname); - expect(searched_persons[2]).toHaveProperty("email", person2.email); - expect(searched_persons[3]).toHaveProperty("github", person3.github); expect(searched_persons[3]).toHaveProperty("firstname", person3.firstname); expect(searched_persons[3]).toHaveProperty("lastname", person3.lastname); expect(searched_persons[3]).toHaveProperty("email", person3.email); + + expect(searched_persons[4]).toHaveProperty("github", person4.github); + expect(searched_persons[4]).toHaveProperty("firstname", person4.firstname); + expect(searched_persons[4]).toHaveProperty("lastname", person4.lastname); + expect(searched_persons[4]).toHaveProperty("email", person4.email); }); // Can only be tested with a login user, should therefore be tested in the login user tests? /*it('should find person 1 in the db, by searching for its email', async () => { - const searched_person = await getPasswordPersonByEmail(person2.email!); - expect(searched_person).toHaveProperty("github", person2.github); - expect(searched_person).toHaveProperty("firstname", person2.firstname); - expect(searched_person).toHaveProperty("lastname", person2.lastname); - expect(searched_person).toHaveProperty("email", person2.email); + const searched_person = await getPasswordPersonByEmail(person3.email!); + expect(searched_person).toHaveProperty("github", person3.github); + expect(searched_person).toHaveProperty("firstname", person3.firstname); + expect(searched_person).toHaveProperty("lastname", person3.lastname); + expect(searched_person).toHaveProperty("email", person3.email); });*/ it('should find person 1 in the db, by searching for its firstname', async () => { - const searched_person = await searchPersonByName(person2.firstname!); - expect(searched_person[0]).toHaveProperty("github", person2.github); - expect(searched_person[0]).toHaveProperty("firstname", person2.firstname); - expect(searched_person[0]).toHaveProperty("lastname", person2.lastname); - expect(searched_person[0]).toHaveProperty("email", person2.email); + const searched_person = await searchPersonByName(person3.firstname); + expect(searched_person[0]).toHaveProperty("github", person3.github); + expect(searched_person[0]).toHaveProperty("firstname", person3.firstname); + expect(searched_person[0]).toHaveProperty("lastname", person3.lastname); + expect(searched_person[0]).toHaveProperty("email", person3.email); }); it('should find person 2 in the db, by searching for its lastname', async () => { - const searched_person3 = await searchPersonByName(person3.lastname!); - expect(searched_person3[0]).toHaveProperty("github", person3.github); - expect(searched_person3[0]).toHaveProperty("firstname", person3.firstname); - expect(searched_person3[0]).toHaveProperty("lastname", person3.lastname); - expect(searched_person3[0]).toHaveProperty("email", person3.email); + const searched_person4 = await searchPersonByName(person4.lastname); + expect(searched_person4[0]).toHaveProperty("github", person4.github); + expect(searched_person4[0]).toHaveProperty("firstname", person4.firstname); + expect(searched_person4[0]).toHaveProperty("lastname", person4.lastname); + expect(searched_person4[0]).toHaveProperty("email", person4.email); }); it('should find all the persons in the db with given email, 1 in total', async () => { - const searched_persons = await searchPersonByLogin(person2.email!); - expect(searched_persons[0]).toHaveProperty("github", person2.github); - expect(searched_persons[0]).toHaveProperty("firstname", person2.firstname); - expect(searched_persons[0]).toHaveProperty("lastname", person2.lastname); - expect(searched_persons[0]).toHaveProperty("email", person2.email); + if (person3.email != undefined) { + const searched_persons = await searchPersonByLogin(person3.email); + expect(searched_persons[0]).toHaveProperty("github", person3.github); + expect(searched_persons[0]).toHaveProperty("firstname", person3.firstname); + expect(searched_persons[0]).toHaveProperty("lastname", person3.lastname); + expect(searched_persons[0]).toHaveProperty("email", person3.email); + } }); it('should find all the persons in the db with given github, 1 in total', async () => { - const searched_persons = await searchPersonByLogin(person3.github!); - expect(searched_persons[0]).toHaveProperty("github", person3.github); - expect(searched_persons[0]).toHaveProperty("firstname", person3.firstname); - expect(searched_persons[0]).toHaveProperty("lastname", person3.lastname); - expect(searched_persons[0]).toHaveProperty("email", person3.email); + if (person4.github) { + const searched_persons = await searchPersonByLogin(person4.github); + expect(searched_persons[0]).toHaveProperty("github", person4.github); + expect(searched_persons[0]).toHaveProperty("firstname", person4.firstname); + expect(searched_persons[0]).toHaveProperty("lastname", person4.lastname); + expect(searched_persons[0]).toHaveProperty("email", person4.email); + } }); it('should update person based upon personid', async () => { - const searched_person2 = await searchPersonByName(person2.firstname!); + const searched_person3 = await searchPersonByName(person3.firstname); const personUpdate: UpdatePerson = { - personId: searched_person2[0].person_id, + personId: searched_person3[0].person_id, email: "new@email.be", firstname: "new_name", lastname: "different_name", @@ -117,9 +117,9 @@ it('should update person based upon personid', async () => { }); it('should delete the person based upon personid', async () => { - const searched_person3 = await searchPersonByName(person3.lastname!); - const deleted_person = await deletePersonById(searched_person3[0].person_id); - expect(deleted_person).toHaveProperty("person_id", searched_person3[0].person_id); + const searched_person4 = await searchPersonByName(person4.lastname); + const deleted_person = await deletePersonById(searched_person4[0].person_id); + expect(deleted_person).toHaveProperty("person_id", searched_person4[0].person_id); expect(deleted_person).toHaveProperty("github", deleted_person.github); expect(deleted_person).toHaveProperty("firstname", deleted_person.firstname); expect(deleted_person).toHaveProperty("lastname", deleted_person.lastname); From 299542663d285f70f2610fc3e843bfee555b6334 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Tue, 29 Mar 2022 14:10:17 +0200 Subject: [PATCH 039/827] test: add testdata for job application and evaluation to the integration_setup --- backend/tests/orm_integration/job_application.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/tests/orm_integration/job_application.test.ts b/backend/tests/orm_integration/job_application.test.ts index d2ca80c2..0c69a75e 100644 --- a/backend/tests/orm_integration/job_application.test.ts +++ b/backend/tests/orm_integration/job_application.test.ts @@ -1,6 +1,4 @@ -import prisma from "../../prisma/prisma"; it("should do nothing", async () => { - await prisma.job_application.findMany(); expect(true).toBeTruthy(); }); \ No newline at end of file From 021ac80cf38fa1b6c46acce121b35a3c1c5ad29b Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 29 Mar 2022 14:17:26 +0200 Subject: [PATCH 040/827] chore: remove unused packages --- frontend/package-lock.json | 806 +++---------------------------------- frontend/package.json | 7 - 2 files changed, 54 insertions(+), 759 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index af06eaa6..ff6e0bc1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,10 +8,8 @@ "name": "frontend", "version": "0.1.0", "dependencies": { - "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", "next": "12.1.2", - "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2" }, @@ -92,44 +90,6 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", - "dependencies": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/@next/env": { "version": "12.1.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", @@ -519,11 +479,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "node_modules/acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -545,17 +500,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -576,6 +520,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -595,23 +540,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -717,25 +645,14 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bcrypt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", - "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "node-addon-api": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - } + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -800,14 +717,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -826,23 +735,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "node_modules/core-js-pure": { "version": "3.21.1", @@ -908,6 +805,7 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -938,22 +836,6 @@ "node": ">= 0.4" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -984,16 +866,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/es-abstract": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", @@ -1597,21 +1469,11 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "node_modules/function-bind": { "version": "1.1.1", @@ -1625,25 +1487,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -1678,6 +1521,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1797,36 +1641,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -1865,6 +1679,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1873,7 +1688,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/internal-slot": { "version": "1.0.3", @@ -1965,14 +1781,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2216,6 +2024,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2229,28 +2038,6 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/marked": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", @@ -2289,6 +2076,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2302,44 +2090,11 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.1", @@ -2408,44 +2163,6 @@ } } }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "node_modules/nodemailer": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", - "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2555,6 +2272,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "dependencies": { "wrappy": "1" } @@ -2634,6 +2352,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2776,19 +2495,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -2863,6 +2569,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -2896,32 +2603,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true, - "peer": true - }, "node_modules/scheduler": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", @@ -2935,6 +2616,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2945,11 +2627,6 @@ "node": ">=10" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2996,11 +2673,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3018,32 +2690,6 @@ "node": ">=0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -3093,6 +2739,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3164,22 +2811,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3198,11 +2829,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -3383,11 +3009,6 @@ "react": "^16.8.0 || ^17.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -3406,20 +3027,6 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3451,14 +3058,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -3471,12 +3070,14 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } }, "dependencies": { @@ -3533,32 +3134,6 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "@mapbox/node-pre-gyp": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", - "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", - "requires": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.5", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - } - } - }, "@next/env": { "version": "12.1.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", @@ -3783,11 +3358,6 @@ "eslint-visitor-keys": "^3.0.0" } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -3801,14 +3371,6 @@ "dev": true, "requires": {} }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3824,7 +3386,8 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "ansi-styles": { "version": "4.3.0", @@ -3835,20 +3398,6 @@ "color-convert": "^2.0.1" } }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3927,21 +3476,14 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bcrypt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", - "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.0", - "node-addon-api": "^3.1.0" - } + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3987,11 +3529,6 @@ "supports-color": "^7.1.0" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4007,20 +3544,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "core-js-pure": { "version": "3.21.1", @@ -4069,6 +3597,7 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -4088,16 +3617,6 @@ "object-keys": "^1.0.12" } }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4122,16 +3641,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "peer": true, - "requires": { - "iconv-lite": "^0.6.2" - } - }, "es-abstract": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", @@ -4616,18 +4125,11 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "function-bind": { "version": "1.1.1", @@ -4641,22 +4143,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -4682,6 +4168,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4759,30 +4246,6 @@ "has-symbols": "^1.0.2" } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "peer": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -4809,6 +4272,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4817,7 +4281,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "internal-slot": { "version": "1.0.3", @@ -4879,11 +4344,6 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -5067,6 +4527,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -5077,21 +4538,6 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "marked": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", @@ -5118,6 +4564,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5128,32 +4575,11 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "nanoid": { "version": "3.3.1", @@ -5190,35 +4616,6 @@ "use-subscription": "1.5.1" } }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "nodemailer": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", - "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==" - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5295,6 +4692,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -5355,7 +4753,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "3.1.1", @@ -5450,16 +4849,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -5509,6 +4898,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "requires": { "glob": "^7.1.3" } @@ -5522,18 +4912,6 @@ "queue-microtask": "^1.2.2" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true, - "peer": true - }, "scheduler": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", @@ -5547,15 +4925,11 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5593,11 +4967,6 @@ "object-inspect": "^1.9.0" } }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5609,31 +4978,6 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - } - } - }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -5674,6 +5018,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -5711,19 +5056,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5739,11 +5071,6 @@ "is-number": "^7.0.0" } }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -5881,11 +5208,6 @@ "object-assign": "^4.1.1" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -5904,20 +5226,6 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5940,14 +5248,6 @@ "is-symbol": "^1.0.3" } }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -5957,12 +5257,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/frontend/package.json b/frontend/package.json index 982ed13d..e669abd9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,10 +9,8 @@ "lint": "next lint" }, "dependencies": { - "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", "next": "12.1.2", - "nodemailer": "^6.7.3", "react": "17.0.2", "react-dom": "17.0.2" }, @@ -26,10 +24,5 @@ "eslint-config-next": "12.1.2", "typedoc": "^0.22.12", "typescript": "4.6.3" - }, - "browser": { - "fs": false, - "path": false, - "os": false } } From 579b2fc3f91daab5dff193422e3b60a84625198d Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Tue, 29 Mar 2022 18:35:55 +0200 Subject: [PATCH 041/827] tests: created int. test for project role --- .../orm_integration/integration_setup.ts | 15 +++- .../orm_integration/project_role.test.ts | 80 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 backend/tests/orm_integration/project_role.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index ebcaa355..999bb437 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -93,7 +93,7 @@ beforeAll(async () => { ] }) const students = await prisma.student.findMany(); - // create evaluations + // create job applications await prisma.job_application.createMany({ data : [ { @@ -150,6 +150,19 @@ beforeAll(async () => { } ] }) + + // create roles + await prisma.role.createMany({ + data: [ + { + name: "Developer", + + }, + { + name: "Marketeer" + }, + ], + }); }); afterAll(async () => { diff --git a/backend/tests/orm_integration/project_role.test.ts b/backend/tests/orm_integration/project_role.test.ts new file mode 100644 index 00000000..5fbf1d71 --- /dev/null +++ b/backend/tests/orm_integration/project_role.test.ts @@ -0,0 +1,80 @@ +import {CreateProjectRole, UpdateProjectRole} from "../../orm_functions/orm_types"; +import {createProjectRole, getProjectRolesByProject, getNumberOfRolesByProjectAndRole, + getProjectRoleNamesByProject, updateProjectRole, deleteProjectRole, getNumberOfFreePositions} from "../../orm_functions/project_role"; +import {getAllProjects} from "../../orm_functions/project"; +import {getRolesByName} from "../../orm_functions/role"; + +const projectRole1: UpdateProjectRole = { + projectRoleId: 0, + projectId: 1, + roleId: 1, + positions: 2 +} + +const projectRole2: UpdateProjectRole = { + projectRoleId: 0, + projectId: 1, + roleId: 1, + positions: 3 +} + +it('should create 1 new project where osoc is 2022', async () => { + const projects = await getAllProjects(); + const role = await getRolesByName("Developer"); + const projectRole: CreateProjectRole = { + projectId: projects[0].project_id, + roleId: role!.role_id, + positions: 2 + } + projectRole1.projectId = projects[0].project_id; + projectRole1.roleId = role!.role_id; + projectRole2.projectId = projects[0].project_id; + projectRole2.roleId = role!.role_id; + + const created_project_role = await createProjectRole(projectRole); + projectRole1.projectRoleId = created_project_role.project_role_id; + projectRole2.projectRoleId = created_project_role.project_role_id; + expect(created_project_role).toHaveProperty("project_id", projectRole1.projectId); + expect(created_project_role).toHaveProperty("role_id", projectRole1.roleId); + expect(created_project_role).toHaveProperty("positions", projectRole1.positions); +}); + +it('should return the project role, by searching for its project', async () => { + const searched_project_role = await getProjectRolesByProject(projectRole1.projectId); + expect(searched_project_role[0]).toHaveProperty("project_id", projectRole1.projectId); + expect(searched_project_role[0]).toHaveProperty("role_id", projectRole1.roleId); + expect(searched_project_role[0]).toHaveProperty("positions", projectRole1.positions); +}); + +it('should return the project role, by searching for its project and projectrole', async () => { + const searched_project_role = await getNumberOfRolesByProjectAndRole(projectRole1.projectId, projectRole1.projectRoleId); + expect(searched_project_role[0]).toHaveProperty("project_id", projectRole1.projectId); + expect(searched_project_role[0]).toHaveProperty("role_id", projectRole1.roleId); + expect(searched_project_role[0]).toHaveProperty("positions", projectRole1.positions); +}); + +it('should return the project role and role, by searching for its project role id', async () => { + const searched_project_role = await getProjectRoleNamesByProject(projectRole1.projectId); + expect(searched_project_role[0]).toHaveProperty("project_id", projectRole1.projectId); + expect(searched_project_role[0]).toHaveProperty("role_id", projectRole1.roleId); + expect(searched_project_role[0]).toHaveProperty("positions", projectRole1.positions); +}); + +it('should return the the number of free positions, by searching for its project role id', async () => { + const number_of_positions = await getNumberOfFreePositions(projectRole1.projectRoleId); + expect(number_of_positions).toEqual(2); +}); + +it('should update projectrole based upon project role id', async () => { + const updated_project = await updateProjectRole(projectRole2); + expect(updated_project).toHaveProperty("project_id", projectRole2.projectId); + expect(updated_project).toHaveProperty("role_id", projectRole2.roleId); + expect(updated_project).toHaveProperty("positions", projectRole2.positions); +}); + +it('should delete the project role based upon project role id', async () => { + const deleted_project = await deleteProjectRole(projectRole2.projectRoleId); + expect(deleted_project).toHaveProperty("project_id", projectRole2.projectId); + expect(deleted_project).toHaveProperty("role_id", projectRole2.roleId); + expect(deleted_project).toHaveProperty("positions", projectRole2.positions); +}); From 761914fe296970992ff10b58591f623390bfca60 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Tue, 29 Mar 2022 18:39:35 +0200 Subject: [PATCH 042/827] fix: fixed wrong comment --- backend/tests/orm_integration/project_role.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/tests/orm_integration/project_role.test.ts b/backend/tests/orm_integration/project_role.test.ts index 5fbf1d71..7a614d1e 100644 --- a/backend/tests/orm_integration/project_role.test.ts +++ b/backend/tests/orm_integration/project_role.test.ts @@ -18,7 +18,7 @@ const projectRole2: UpdateProjectRole = { positions: 3 } -it('should create 1 new project where osoc is 2022', async () => { +it('should create 1 new project role with role developer', async () => { const projects = await getAllProjects(); const role = await getRolesByName("Developer"); const projectRole: CreateProjectRole = { From 323abc938058ffb45e136b4cd831a46e8b4b1783 Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Tue, 29 Mar 2022 18:42:14 +0200 Subject: [PATCH 043/827] feat: parsing form to person --- backend/endpoints.ts | 2 +- backend/routes/form.ts | 257 ++++++++++++++++++++++++++-------------- backend/routes/login.ts | 1 + backend/types.ts | 14 +++ docs/api.json | 51 ++++++-- package-lock.json | 40 ++++++- package.json | 5 +- 7 files changed, 265 insertions(+), 105 deletions(-) diff --git a/backend/endpoints.ts b/backend/endpoints.ts index 130f09a1..8b720e61 100644 --- a/backend/endpoints.ts +++ b/backend/endpoints.ts @@ -20,7 +20,7 @@ export function attach(app: express.Application): void { app.use(home + '/coach', coach.getRouter()); app.use(home + '/admin', admin.getRouter()); app.use(home + '/project', project.getRouter()); - app.use(home + '/form', form.getRouter); + app.use(home + '/form', form.getRouter()); }); app.use((req: express.Request, res: express.Response): Promise => diff --git a/backend/routes/form.ts b/backend/routes/form.ts index ac6ccae5..1189d36d 100644 --- a/backend/routes/form.ts +++ b/backend/routes/form.ts @@ -5,28 +5,93 @@ import * as ormSt from '../orm_functions/student'; import * as rq from '../request'; import {Requests, Responses} from '../types'; import * as util from "../utility"; +import {errors} from "../utility"; +import * as validator from 'validator'; /** * This function searches a question with a given key in the form. * @param form The form with the answers. * @returns The question that corresponds with the given key. */ -function filterQuestion(form: Requests.Form, key: string): Requests.Question { - return form.data.fields.filter(question => question.key == key)[0]; +function filterQuestion(form: Requests.Form, key: string): Responses.FormResponse { + const filteredQuestion = form.data.fields.filter(question => question.key == key); + return filteredQuestion.length > 0 ? {data : filteredQuestion[0], error : false} : {data : null, error : true}; } /** - * This function checks if the answer on a certain question is 'yes' or 'no'. - * @param question The question with 'yes' and 'no' as possible answers. - * @returns A boolean that answers the question. + * This function searches the chosen option for a given question. + * @param question The question. + * @returns The option that corresponds with the given answer. */ -function checkYesAnswer(question: Requests.Question): boolean { - if (question.options !== undefined) { - return question.options.filter(option => option.id === question.value)[0] - .text.toLowerCase() - .includes("yes"); - } - return false; +function filterChosenOption(question: Requests.Question): Responses.FormResponse { + if(question.options != undefined) { + const filteredOption = question.options.filter(option => option.id === question.value); + return {data : filteredOption[0], error : false}; + } + return {data : null, error : true} +} + +/** + * This function checks if the answer on a certain question contains a word. + * @param question The question. + * @returns True if the question options contain the word, else false. + */ +function checkWordInAnswer(question: Requests.Question, word : string): Responses.FormResponse { + const chosenOption : Responses.FormResponse = filterChosenOption(question); + return chosenOption.data != null ? {data : chosenOption.data.text.toLowerCase().includes(word), error : false} : {data : null, error : false}; +} + +function checkQuestionExist(questions: Responses.FormResponse[]) : boolean { + const checkErrorInForm : Responses.FormResponse[] = questions.filter(dataError => dataError.data == null); + return checkErrorInForm.length === 0; +} + +/* parse form to person +***********************/ + +/** + * Parse the form to the birth name of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getBirthName(form: Requests.Form) : Promise { + const questionBirthName: Responses.FormResponse = filterQuestion(form, "question_npDErJ"); + const questionExist : boolean = checkQuestionExist([questionBirthName]); + if(!questionExist || questionBirthName.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + return Promise.resolve(questionBirthName.data.value); +} + +/** + * Parse the form to the last name of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getLastName(form: Requests.Form) : Promise { + const questionLastName: Responses.FormResponse = filterQuestion(form, "question_319eXp"); + const questionExist : boolean = checkQuestionExist([questionLastName]); + if(!questionExist || questionLastName.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + return Promise.resolve(questionLastName.data.value); +} + +/** + * Parse the form to the email of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getEmail(form: Requests.Form) : Promise { + const questionEmail: Responses.FormResponse = filterQuestion(form, "question_mY46PB"); + const questionExist : boolean = checkQuestionExist([questionEmail]); + if(!questionExist || questionEmail.data?.value == null || !validator.default.isEmail(questionEmail.data.value)) { + return Promise.reject(errors.cookArgumentError()); + } + return Promise.resolve(validator.default.normalizeEmail(questionEmail.data.value).toString()); } /** @@ -36,32 +101,27 @@ function checkYesAnswer(question: Requests.Question): boolean { * `Promise.resolve`, failures using `Promise.reject`. */ async function jsonToPerson(form: Requests.Form): Promise { - const questionBirthName: Requests.Question = - filterQuestion(form, "question_npDErJ"); - const questionLastName: Requests.Question = - filterQuestion(form, "question_319eXp"); - const questionEmail: Requests.Question = - filterQuestion(form, "question_mY46PB"); - - if (questionBirthName.value == null || questionLastName.value == null || - questionEmail.value == null) { - return Promise.reject(util.errors.cookArgumentError()); - } - - // TODO check email - - return ormP - .createPerson({ - firstname : questionBirthName.value, - lastname : questionLastName.value, - email : questionEmail.value - }) - .then(person => {return Promise.resolve({ - person_id : person.person_id, - firstname : person.firstname, - lastname : person.lastname, - email : questionEmail.value - })}); + return getBirthName(form) + .then(birthNameResponse => { + return getLastName(form) + .then(lastNameResponse => { + return getEmail(form) + .then(emailResponse => { + return ormP + .createPerson({ + firstname : birthNameResponse, + lastname : lastNameResponse, + email : emailResponse + }) + .then(person => Promise.resolve({ + person_id : person.person_id, + firstname : person.firstname, + lastname : person.lastname, + email : emailResponse + })); + }) + }) + }) } /** @@ -70,59 +130,75 @@ async function jsonToPerson(form: Requests.Form): Promise { * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function jsonToStudent(form: Requests.Form, person: Responses.Person): +/*async function jsonToStudent(form: Requests.Form, person: Responses.Person): Promise { - // The pronouns of this student - const questionAddPronouns: Requests.Question = - filterQuestion(form, "question_3yJQMg"); - const questionPreferedPronouns: Requests.Question = - filterQuestion(form, "question_3X4aLg"); - const questionEnterPronouns: Requests.Question = - filterQuestion(form, "question_w8ZBq5"); - - let pronouns: string[] = []; - - if (checkYesAnswer(questionAddPronouns) && - questionPreferedPronouns.options !== undefined) { - const chosenValue = questionPreferedPronouns.options?.filter( - option => option.id === questionPreferedPronouns.value)[0]; - if (chosenValue.text !== "other") { - pronouns = chosenValue.text.split("/"); - } else { - pronouns = questionEnterPronouns.value.split("/"); + + // The pronouns of this student + const questionAddPronouns: Responses.FormResponse = filterQuestion(form, "question_3yJQMg"); + const questionPreferedPronouns: Responses.FormResponse = filterQuestion(form, "question_3X4aLg"); + const questionEnterPronouns: Responses.FormResponse = filterQuestion(form, "question_w8ZBq5"); + + const questionGender: Responses.FormResponse = filterQuestion(form, "question_wg9laO"); + + const questionPhoneNumber: Responses.FormResponse = filterQuestion(form, "question_wd9MEo"); + + const questionCheckNickname: Responses.FormResponse = filterQuestion(form, "question_wME4XM"); + const questionEnterNickname: Responses.FormResponse = filterQuestion(form, "question_mJOPqo"); + + const questionAlumni: Responses.FormResponse = filterQuestion(form, "question_mVzejJ"); + + const questionsExist : boolean = checkQuestionExist( + [questionAddPronouns, questionPreferedPronouns, questionEnterPronouns, questionGender, + questionPhoneNumber, questionCheckNickname, questionEnterPronouns, questionAlumni]); + + if(!questionsExist) { + return Promise.reject(errors.cookArgumentError()); } - } - // The gender of this student - const questionGender: Requests.Question = - filterQuestion(form, "question_wg9laO"); - let gender = ""; - if (questionGender.options !== undefined) { - gender = questionGender.options - .filter(option => option.id === questionGender.value)[0] - .text; - } + if (questionAddPronouns.data?.value == null || questionGender.data?.value == null || + questionPhoneNumber.data?.value == null || questionCheckNickname.data?.value == null || + questionAlumni.data?.value == null) { + return Promise.reject(util.errors.cookArgumentError()); + } + + let pronouns: string[] = []; + + if(checkWordInAnswer(questionAddPronouns.data, "yes")) { + const chosenOption : Responses.FormResponse = filterChosenOption(questionPreferedPronouns.data as Requests.Question); + if(chosenOption.error || chosenOption.data?.id.length === 0 || questionPreferedPronouns.data?.value == null) { + return Promise.reject(util.errors.cookArgumentError()); + } else if(!checkWordInAnswer(questionPreferedPronouns.data, "other") && chosenOption.data?.text != undefined) { + pronouns = chosenOption.data.text.split("/"); + } else { + if(questionEnterPronouns.data?.value == null) { + return Promise.reject(util.errors.cookArgumentError()); + } + pronouns = questionEnterPronouns.data.value.split("/"); + } + } - // The phone number of this student - const questionPhoneNumber: Requests.Question = - filterQuestion(form, "question_wd9MEo"); - const phoneNumber = questionPhoneNumber.value; + // The gender of this student + let gender; + const chosenGender : Requests.Option = filterChosenOption(questionGender); - // The nickname of this student - const questionCheckNicknamePronouns: Requests.Question = - filterQuestion(form, "question_wME4XM"); - const questionEnterNicknamePronouns: Requests.Question = - filterQuestion(form, "question_mJOPqo"); + if(chosenGender.id.length === 0) { + return Promise.reject(errors.cookNonJSON("Invalid form")); + } else { + gender = chosenGender.text; + } - let nickname; + // The phone number of this student + const phoneNumber = questionPhoneNumber.value; - if (checkYesAnswer(questionCheckNicknamePronouns)) { - nickname = questionEnterNicknamePronouns.value; - } + // The nickname of this student + + let nickname; + + if (checkWordInAnswer(questionCheckNickname, "yes")) { + nickname = questionEnterNickname.value; + } // Checks if this student has participated before - const questionAlumni: Requests.Question = - filterQuestion(form, "question_mVzejJ"); let alumni = false; if (questionAlumni.options !== undefined) { @@ -153,7 +229,7 @@ async function jsonToStudent(form: Requests.Form, person: Responses.Person): }) .then(() => { return Promise.resolve({}); }); } -} +}*/ /** * Attempts to parse the answers in the form into a job application entity. @@ -209,23 +285,20 @@ option.id === questionVolunteerInfo.value)[0].text; * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function createForm(req: express.Request): Promise { +/*async function createForm(req: express.Request): Promise { return rq.parseFormRequest(req).then(async form => { // Checks if the student will be in Belgium in July and if the student can // work enough in July. - if (!checkYesAnswer(filterQuestion(form, "question_wkNolR")) || - !checkYesAnswer(filterQuestion(form, "question_mKVEz8"))) { + if (!checkWordInAnswer(filterQuestion(form, "question_wkNolR")) || + !checkWordInAnswer(filterQuestion(form, "question_mKVEz8"))) { return Promise.reject(util.errors.cookNonJSON("Invalid json")); } - // TODO kan je 128 uur werken - // TODO gender verandert naar student - return jsonToPerson(form) .then(person => { return jsonToStudent(form, person); }) .then(() => { return Promise.resolve({}); }); }); -} +}*/ /** * Gets the router for all `/form/` related endpoints. @@ -235,8 +308,8 @@ async function createForm(req: express.Request): Promise { export function getRouter(): express.Router { const router: express.Router = express.Router(); - router.post('/', - (req, res) => util.respOrErrorNoReinject(res, createForm(req))); + /*router.post('/', + (req, res) => util.respOrErrorNoReinject(res, createForm(req)));*/ util.addAllInvalidVerbs(router, [ "/" ]); diff --git a/backend/routes/login.ts b/backend/routes/login.ts index 6f3c878a..78e45c55 100644 --- a/backend/routes/login.ts +++ b/backend/routes/login.ts @@ -15,6 +15,7 @@ import * as util from '../utility'; async function login(req: express.Request): Promise { console.log("Calling login endpoint " + JSON.stringify(req.body)); return parseLoginRequest(req).then( + // TODO normalize email parsed => getPasswordPersonByEmail(parsed.name).then(async pass => { if (pass?.login_user?.account_status != 'ACTIVATED') { return Promise.reject( diff --git a/backend/types.ts b/backend/types.ts index eb1c2523..acbbeb54 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -382,6 +382,20 @@ export type OrError = ApiError|T; * An API response is one of the previous response types. */ export type ApiResponse = Empty|Key|PartialStudent|IdNameList; + +/** + * Either an error while parsing the form or a data value. + */ +export interface FormResponse { + /** + * The data. + */ + data: T | null; + /** + * The error reason. + */ + error: boolean; +} } export namespace Requests { diff --git a/docs/api.json b/docs/api.json index 0ddecef7..355c95f7 100644 --- a/docs/api.json +++ b/docs/api.json @@ -298,7 +298,7 @@ "tags": [ "project" ], - "description": "Attempts to insert a new project in into the system", + "description": "Attempts to insert a new project into the system.", "operationId": "create_project", "parameters": [ { @@ -335,13 +335,20 @@ "description": "The amount of positions for the project", "required": true, "type": "number" + }, + { + "in": "header", + "name": "osocId", + "description": "The id of the osoc event", + "required": true, + "type": "number" } ], "responses": { "200": { "description": "Updated sessionkey as hex string E.g. \"A1BEF4\".", "schema": { - "$ref": "#/definitions/Sessionkey" + "$ref": "#/definitions/CreateProject" } } } @@ -400,7 +407,7 @@ "200": { "description": "Updated sessionkey as hex string E.g. \"A1BEF4\".", "schema": { - "$ref": "#/definitions/All_projects" + "$ref": "#/definitions/CreateProject" } } } @@ -423,42 +430,42 @@ "in": "header", "name": "name", "description": "The name of the project", - "required": true, + "required": false, "type": "string" }, { "in": "header", "name": "osoc_id", "description": "The id of the osoc edition this project is connected to", - "required": true, + "required": false, "type": "number" }, { "in": "header", "name": "partner", "description": "The partner's name of this project", - "required": true, + "required": false, "type": "string" }, { "in": "header", "name": "start_date", "description": "The start date of this project", - "required": true, + "required": false, "type": "string" }, { "in": "header", "name": "end_date", "description": "The end date of this project", - "required": true, + "required": false, "type": "string" }, { "in": "header", "name": "positions", "description": "The amount of positions for the project", - "required": true, + "required": false, "type": "number" } ], @@ -466,7 +473,7 @@ "200": { "description": "The project field contains all updated fields. If no field is updated, an Argument error is thrown. Updated sessionkey as hex string E.g. \"A1BEF4\".", "schema": { - "$ref": "#/definitions/Sessionkey" + "$ref": "#/definitions/CreateProject" } } } @@ -484,6 +491,13 @@ "description": "Your current session key. - Project ID is parsed from the URL.", "required": true, "type": "string" + }, + { + "in": "header", + "name": "id", + "description": "The id of the project.", + "required": true, + "type": "number" } ], "responses": { @@ -1006,12 +1020,29 @@ }, "positions": { "type": "number" + }, + "osoc_id": { + "type": "number" } }, "xml": { "name": "Project" } }, + "CreateProject": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/Project" + }, + "sessionkey": { + "type": "string" + } + }, + "xml": { + "name": "CreateProject" + } + }, "Project_list": { "type": "array", "items": { diff --git a/package-lock.json b/package-lock.json index 7a1fc850..c6732ea0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,11 +5,14 @@ "packages": { "": { "dependencies": { - "eslint-plugin-jest": "^26.1.1" + "eslint-plugin-jest": "^26.1.1", + "normalize-email": "^1.1.1", + "validator": "^13.7.0" }, "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", + "@types/validator": "^13.7.2", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", @@ -736,6 +739,12 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.7.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", + "integrity": "sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", @@ -3614,6 +3623,11 @@ } } }, + "node_modules/normalize-email": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/normalize-email/-/normalize-email-1.1.1.tgz", + "integrity": "sha512-d5gghFO3BceKeZ+csXTsw2/qnuqZa5rHabXTtLg6XYEqlm25G7g0TNrPlB72+6qXFitSTHf+lHcJhm6bQurC+Q==" + }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -5023,6 +5037,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5750,6 +5772,12 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/validator": { + "version": "13.7.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", + "integrity": "sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.16.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz", @@ -7825,6 +7853,11 @@ "use-subscription": "1.5.1" } }, + "normalize-email": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/normalize-email/-/normalize-email-1.1.1.tgz", + "integrity": "sha512-d5gghFO3BceKeZ+csXTsw2/qnuqZa5rHabXTtLg6XYEqlm25G7g0TNrPlB72+6qXFitSTHf+lHcJhm6bQurC+Q==" + }, "normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -8824,6 +8857,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 110a3af9..b11c0814 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", + "@types/validator": "^13.7.2", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", @@ -18,6 +19,8 @@ "prepare": "husky install" }, "dependencies": { - "eslint-plugin-jest": "^26.1.1" + "eslint-plugin-jest": "^26.1.1", + "normalize-email": "^1.1.1", + "validator": "^13.7.0" } } From 52d95c849bc3b2e38d0b84278f881f403db2308e Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Tue, 29 Mar 2022 19:54:46 +0200 Subject: [PATCH 044/827] test: created int. tests for role --- backend/orm_functions/role.ts | 2 +- .../orm_integration/integration_setup.ts | 29 ++++++++- backend/tests/orm_integration/project.test.ts | 52 ++++++++-------- .../orm_integration/project_role.test.ts | 12 ++-- backend/tests/orm_integration/role.test.ts | 61 +++++++++++++++++++ backend/tests/orm_tests/role.test.ts | 4 +- 6 files changed, 124 insertions(+), 36 deletions(-) create mode 100644 backend/tests/orm_integration/role.test.ts diff --git a/backend/orm_functions/role.ts b/backend/orm_functions/role.ts index 515c3d96..88a11b7b 100644 --- a/backend/orm_functions/role.ts +++ b/backend/orm_functions/role.ts @@ -5,7 +5,7 @@ import {UpdateRole} from './orm_types'; * * @param name: the name of the role we want to add to the database */ - export async function createProjectRole(name: string){ + export async function createRole(name: string){ const result = await prisma.role.create({ data: { name: name diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 999bb437..64687345 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -64,7 +64,7 @@ beforeAll(async () => { data: [ { name: "project-test", - osoc_id: osocs[1].osoc_id, + osoc_id: osocs[0].osoc_id, partner: "partner-test", start_date: new Date("2022-05-22"), end_date: new Date("2022-06-31"), @@ -78,6 +78,14 @@ beforeAll(async () => { end_date: new Date("2022-10-23"), positions: 9 }, + { + name: "project-test-3", + osoc_id: osocs[1].osoc_id, + partner: "partner-test-3", + start_date: new Date("2022-09-15"), + end_date: new Date("2022-10-23"), + positions: 9 + } ], }); @@ -163,6 +171,25 @@ beforeAll(async () => { }, ], }); + + const projects = await prisma.project.findFirst(); + const role = await prisma.role.findFirst(); + // create roles + await prisma.project_role.createMany({ + data: [ + { + project_id: projects!.project_id, + role_id: role!.role_id, + positions: 3 + + }, + { + project_id: projects!.project_id, + role_id: role!.role_id, + positions: 1 + }, + ], + }); }); afterAll(async () => { diff --git a/backend/tests/orm_integration/project.test.ts b/backend/tests/orm_integration/project.test.ts index fabe7a3f..47dab36d 100644 --- a/backend/tests/orm_integration/project.test.ts +++ b/backend/tests/orm_integration/project.test.ts @@ -47,13 +47,13 @@ it('should create 1 new project where osoc is 2022', async () => { it('should find all the projects in the db, 3 in total', async () => { const searched_projects = await getAllProjects(); - expect(searched_projects.length).toEqual(3); - expect(searched_projects[2]).toHaveProperty("name", project1.name); - expect(searched_projects[2]).toHaveProperty("osoc_id", project1.osocId); - expect(searched_projects[2]).toHaveProperty("partner", project1.partner); - expect(searched_projects[2]).toHaveProperty("start_date", project1.startDate); - expect(searched_projects[2]).toHaveProperty("end_date", project1.endDate); - expect(searched_projects[2]).toHaveProperty("positions", project1.positions); + expect(searched_projects.length).toEqual(4); + expect(searched_projects[3]).toHaveProperty("name", project1.name); + expect(searched_projects[3]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[3]).toHaveProperty("partner", project1.partner); + expect(searched_projects[3]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[3]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[3]).toHaveProperty("positions", project1.positions); }); it('should return the project, by searching for its name', async () => { @@ -69,12 +69,12 @@ it('should return the project, by searching for its name', async () => { it('should return the project, by searching for its osoc edition', async () => { const osoc = await getOsocByYear(2022); const searched_project = await getProjectsByOsocEdition(osoc!.osoc_id); - expect(searched_project[0]).toHaveProperty("name", project1.name); - expect(searched_project[0]).toHaveProperty("osoc_id", project1.osocId); - expect(searched_project[0]).toHaveProperty("partner", project1.partner); - expect(searched_project[0]).toHaveProperty("start_date", project1.startDate); - expect(searched_project[0]).toHaveProperty("end_date", project1.endDate); - expect(searched_project[0]).toHaveProperty("positions", project1.positions); + expect(searched_project[1]).toHaveProperty("name", project1.name); + expect(searched_project[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_project[1]).toHaveProperty("partner", project1.partner); + expect(searched_project[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_project[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_project[1]).toHaveProperty("positions", project1.positions); }); it('should return the projects, by searching for its partner name', async () => { @@ -119,12 +119,12 @@ it('should return the projects, by searching for all projects starting before da it('should return the projects, by searching for all projects starting after date', async () => { const searched_projects = await getProjectsStartedAfterDate(new Date("2022-07-01")); - expect(searched_projects[1]).toHaveProperty("name", project1.name); - expect(searched_projects[1]).toHaveProperty("osoc_id", project1.osocId); - expect(searched_projects[1]).toHaveProperty("partner", project1.partner); - expect(searched_projects[1]).toHaveProperty("start_date", project1.startDate); - expect(searched_projects[1]).toHaveProperty("end_date", project1.endDate); - expect(searched_projects[1]).toHaveProperty("positions", project1.positions); + expect(searched_projects[2]).toHaveProperty("name", project1.name); + expect(searched_projects[2]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[2]).toHaveProperty("partner", project1.partner); + expect(searched_projects[2]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[2]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[2]).toHaveProperty("positions", project1.positions); }); it('should return the projects, by searching for all projects ending before date', async () => { @@ -169,12 +169,12 @@ it('should return the projects, by searching for all projects with less position it('should return the projects, by searching for all projects with more positions', async () => { const searched_projects = await getProjectsMorePositions(project1.positions - 1); - expect(searched_projects[2]).toHaveProperty("name", project1.name); - expect(searched_projects[2]).toHaveProperty("osoc_id", project1.osocId); - expect(searched_projects[2]).toHaveProperty("partner", project1.partner); - expect(searched_projects[2]).toHaveProperty("start_date", project1.startDate); - expect(searched_projects[2]).toHaveProperty("end_date", project1.endDate); - expect(searched_projects[2]).toHaveProperty("positions", project1.positions); + expect(searched_projects[3]).toHaveProperty("name", project1.name); + expect(searched_projects[3]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_projects[3]).toHaveProperty("partner", project1.partner); + expect(searched_projects[3]).toHaveProperty("start_date", project1.startDate); + expect(searched_projects[3]).toHaveProperty("end_date", project1.endDate); + expect(searched_projects[3]).toHaveProperty("positions", project1.positions); }); it('should update project based upon project id', async () => { @@ -201,7 +201,7 @@ it('should delete the project based upon project id', async () => { }); it('should delete the project based upon project partner', async () => { - const deleted_project = await deleteProjectByPartner("partner-test"); + const deleted_project = await deleteProjectByPartner("partner-test-2"); expect(deleted_project).toHaveProperty("count", 1); }); diff --git a/backend/tests/orm_integration/project_role.test.ts b/backend/tests/orm_integration/project_role.test.ts index 7a614d1e..3f9affd3 100644 --- a/backend/tests/orm_integration/project_role.test.ts +++ b/backend/tests/orm_integration/project_role.test.ts @@ -41,9 +41,9 @@ it('should create 1 new project role with role developer', async () => { it('should return the project role, by searching for its project', async () => { const searched_project_role = await getProjectRolesByProject(projectRole1.projectId); - expect(searched_project_role[0]).toHaveProperty("project_id", projectRole1.projectId); - expect(searched_project_role[0]).toHaveProperty("role_id", projectRole1.roleId); - expect(searched_project_role[0]).toHaveProperty("positions", projectRole1.positions); + expect(searched_project_role[2]).toHaveProperty("project_id", projectRole1.projectId); + expect(searched_project_role[2]).toHaveProperty("role_id", projectRole1.roleId); + expect(searched_project_role[2]).toHaveProperty("positions", projectRole1.positions); }); it('should return the project role, by searching for its project and projectrole', async () => { @@ -55,9 +55,9 @@ it('should return the project role, by searching for its project and projectrole it('should return the project role and role, by searching for its project role id', async () => { const searched_project_role = await getProjectRoleNamesByProject(projectRole1.projectId); - expect(searched_project_role[0]).toHaveProperty("project_id", projectRole1.projectId); - expect(searched_project_role[0]).toHaveProperty("role_id", projectRole1.roleId); - expect(searched_project_role[0]).toHaveProperty("positions", projectRole1.positions); + expect(searched_project_role[2]).toHaveProperty("project_id", projectRole1.projectId); + expect(searched_project_role[2]).toHaveProperty("role_id", projectRole1.roleId); + expect(searched_project_role[2]).toHaveProperty("positions", projectRole1.positions); }); it('should return the the number of free positions, by searching for its project role id', async () => { diff --git a/backend/tests/orm_integration/role.test.ts b/backend/tests/orm_integration/role.test.ts new file mode 100644 index 00000000..c72748dc --- /dev/null +++ b/backend/tests/orm_integration/role.test.ts @@ -0,0 +1,61 @@ +import {UpdateRole} from "../../orm_functions/orm_types"; +import {createRole, getAllRoles, getRolesByName, getRole, + getProjectRoleWithRoleName, updateRole, deleteRole, deleteRoleByName} from "../../orm_functions/role"; +import prisma_project from "../../prisma/prisma"; + + +const role1: UpdateRole = { + roleId: 0, + name: "Data Scientist" +} + +const role2: UpdateRole = { + roleId: 0, + name: "Web Designer" +} + +it('should create 1 new role where', async () => { + const created__role = await createRole("Data Scientist"); + role1.roleId = created__role.role_id; + role2.roleId = created__role.role_id; + expect(created__role).toHaveProperty("role_id", role1.roleId); + expect(created__role).toHaveProperty("name", role1.name); +}); + +it('should find all the roles in the db, 3 in total', async () => { + const searched_roles = await getAllRoles(); + expect(searched_roles.length).toEqual(3); + expect(searched_roles[2]).toHaveProperty("name", role1.name); +}); + + +it('should return the role, by searching for its role id', async () => { + const searched_role = await getRole(role1.roleId); + expect(searched_role).toHaveProperty("name", role1.name); +}); + +it('should return the role, by searching for its role name', async () => { + const searched_role = await getRolesByName(role1.name); + expect(searched_role).toHaveProperty("name", role1.name); +}); + +it('should return the project role, by searching for its role name and project id', async () => { + const project = await prisma_project.project.findFirst() + const searched_role = await getProjectRoleWithRoleName("Developer", project!.project_id); + expect(searched_role).toHaveProperty("positions", 3); +}); + +it('should update role based upon role id', async () => { + const updated_role = await updateRole(role2); + expect(updated_role).toHaveProperty("name", role2.name); +}); + +it('should delete the project based upon role id', async () => { + const deleted_role = await deleteRole(role2.roleId); + expect(deleted_role).toHaveProperty("name", role2.name); +}); + +it('should delete the project based upon role name', async () => { + const deleted_role = await deleteRoleByName("Marketeer"); + expect(deleted_role).toHaveProperty("name", "Marketeer"); +}); diff --git a/backend/tests/orm_tests/role.test.ts b/backend/tests/orm_tests/role.test.ts index 5663d169..57384e31 100644 --- a/backend/tests/orm_tests/role.test.ts +++ b/backend/tests/orm_tests/role.test.ts @@ -1,6 +1,6 @@ import {prismaMock} from "./singleton"; import { UpdateRole } from "../../orm_functions/orm_types"; -import {getProjectRoleWithRoleName, createProjectRole, getAllRoles, +import {getProjectRoleWithRoleName, createRole, getAllRoles, getRole, getRolesByName, updateRole, deleteRole, deleteRoleByName} from "../../orm_functions/role"; @@ -11,7 +11,7 @@ const returnValue = { test("should create a role in the db with the given name, returns the new record", async () => { prismaMock.role.create.mockResolvedValue(returnValue) - await expect(createProjectRole("Developer")).resolves.toEqual(returnValue); + await expect(createRole("Developer")).resolves.toEqual(returnValue); }); From a6363581bc51db988b000b03e040871c226f22d8 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Tue, 29 Mar 2022 20:12:33 +0200 Subject: [PATCH 045/827] test: created int. tests for language --- .../orm_integration/integration_setup.ts | 14 +++++ .../tests/orm_integration/language.test.ts | 51 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 backend/tests/orm_integration/language.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 64687345..1eb56fe1 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -190,6 +190,20 @@ beforeAll(async () => { }, ], }); + + // create languages + await prisma.language.createMany({ + data: [ + { + name: "Dutch", + + }, + { + name: "French" + }, + ], + }); + }); afterAll(async () => { diff --git a/backend/tests/orm_integration/language.test.ts b/backend/tests/orm_integration/language.test.ts new file mode 100644 index 00000000..cc2cdae0 --- /dev/null +++ b/backend/tests/orm_integration/language.test.ts @@ -0,0 +1,51 @@ +import {UpdateLanguage} from "../../orm_functions/orm_types"; +import {createLanguage, getAllLanguages, getLanguage, + getLanguageByName, updateLanguage, deleteLanguage, deleteLanguageByName} from "../../orm_functions/language"; + +const language1: UpdateLanguage = { + languageId: 0, + name: "English" +} + +const language2: UpdateLanguage = { + languageId: 0, + name: "German" +} + +it('should create 1 new language with', async () => { + const created_language = await createLanguage("English"); + language1.languageId = created_language.language_id; + language2.languageId = created_language.language_id; + expect(created_language).toHaveProperty("name", language1.name); +}); + +it('should find all the languages in the db, 3 in total', async () => { + const searched_languages = await getAllLanguages(); + expect(searched_languages.length).toEqual(3); + expect(searched_languages[2]).toHaveProperty("name", language1.name); +}); + +it('should return the language, by searching for its language id', async () => { + const searched_language = await getLanguage(language1.languageId); + expect(searched_language).toHaveProperty("name", language1.name); +}); + +it('should return the language, by searching for its name', async () => { + const searched_language = await getLanguageByName(language1.name); + expect(searched_language).toHaveProperty("name", language1.name); +}); + +it('should update language based upon language id', async () => { + const updated_language = await updateLanguage(language2); + expect(updated_language).toHaveProperty("name", language2.name); +}); + +it('should delete the language based upon language id', async () => { + const deleted_language = await deleteLanguage(language2.languageId); + expect(deleted_language).toHaveProperty("name", language2.name); +}); + +it('should delete the language based upon language name', async () => { + const deleted_language = await deleteLanguageByName("French"); + expect(deleted_language).toHaveProperty("name", "French"); +}); \ No newline at end of file From dfe35eb8bd297433ad070c86d45bdf241a2fdba5 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Tue, 29 Mar 2022 20:13:58 +0200 Subject: [PATCH 046/827] test: first 3 tests for job_application written --- backend/orm_functions/job_application.ts | 5 +- .../orm_integration/integration_setup.ts | 11 +-- .../orm_integration/job_application.test.ts | 89 ++++++++++++++++++- backend/tests/orm_integration/person.test.ts | 12 +-- 4 files changed, 102 insertions(+), 15 deletions(-) diff --git a/backend/orm_functions/job_application.ts b/backend/orm_functions/job_application.ts index e6b8731d..7fcaf908 100644 --- a/backend/orm_functions/job_application.ts +++ b/backend/orm_functions/job_application.ts @@ -23,6 +23,7 @@ import {CreateJobApplication} from "./orm_types"; decision: true, motivation: true, evaluation_id: true, + is_final: true } } } @@ -115,7 +116,7 @@ export async function getLatestApplicationRolesForStudent(studentId: number) { /** * removes all job applications from a given student * - * @param studentId: the student who's applications will be deleted + * @param studentId: the student whose applications will be deleted * @returns the number of deleted job applications {count: number} */ export async function deleteJobApplicationsFromStudent(studentId: number) { @@ -129,7 +130,7 @@ export async function getLatestApplicationRolesForStudent(studentId: number) { /** * change the email status of a given job application * - * @param jobApplicationId: the application who's email status we want to change + * @param jobApplicationId: the application whose email status we want to change * @param emailStatus: the new email status * @returns: the updated database record */ diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index ebcaa355..38b4efc7 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -93,7 +93,8 @@ beforeAll(async () => { ] }) const students = await prisma.student.findMany(); - // create evaluations + + // create job applications await prisma.job_application.createMany({ data : [ { @@ -109,7 +110,7 @@ beforeAll(async () => { edu_institute: "Ugent", edu_year: 2022, email_status: email_status_enum.DRAFT, - created_at: new Date(), + created_at: new Date("December 17, 2021 14:24:00"), }, { student_id: students[0].student_id, @@ -124,7 +125,7 @@ beforeAll(async () => { edu_institute: "Ugent", edu_year: 2023, email_status: email_status_enum.SENT, - created_at: new Date(), + created_at: new Date("December 20, 2021 03:24:00"), } ] }); @@ -143,9 +144,9 @@ beforeAll(async () => { }, { login_user_id: login_users[0].login_user_id, - job_application_id: job_applications[1].job_application_id, + job_application_id: job_applications[0].job_application_id, decision: decision_enum.YES, - motivation: "awesome job applicaton", + motivation: "awesome job application", is_final: true } ] diff --git a/backend/tests/orm_integration/job_application.test.ts b/backend/tests/orm_integration/job_application.test.ts index 0c69a75e..a20f1caf 100644 --- a/backend/tests/orm_integration/job_application.test.ts +++ b/backend/tests/orm_integration/job_application.test.ts @@ -1,4 +1,89 @@ +import { + getStudentEvaluationsFinal, + getStudentEvaluationsTemp, + getStudentEvaluationsTotal +} from "../../orm_functions/job_application"; +import prisma from "../../prisma/prisma"; +import {decision_enum} from "@prisma/client"; -it("should do nothing", async () => { - expect(true).toBeTruthy(); +it("should return all student evaluations for the student with given id", async () => { + const students = await prisma.student.findMany(); + const osocs = await prisma.osoc.findMany(); + + const evaluations = [ + { + decision: decision_enum.MAYBE, + motivation: "low education level", + is_final: false, + }, + { + decision: decision_enum.YES, + motivation: "awesome job application", + is_final: true + } + ] + + const foundApplications = await getStudentEvaluationsTotal(students[0].student_id); + foundApplications.forEach((found_eval) => { + expect(found_eval).toHaveProperty("osoc", {year: osocs[0].year}); + expect(found_eval).toHaveProperty("evaluation"); + for (let i = 0; i < found_eval.evaluation.length; i++) { + const evals = evaluations[i]; + expect(found_eval.evaluation[i]).toHaveProperty("decision", evals.decision); + expect(found_eval.evaluation[i]).toHaveProperty("motivation", evals.motivation); + expect(found_eval.evaluation[i]).toHaveProperty("evaluation_id") + expect(found_eval.evaluation[i]).toHaveProperty("is_final", evals.is_final) + } + }); +}); + +it("should return all final student evaluations for the student with given id", async () => { + const students = await prisma.student.findMany(); + const osocs = await prisma.osoc.findMany(); + + const evaluations = [ + { + decision: decision_enum.YES, + motivation: "awesome job application", + } + ] + + const foundApplications = await getStudentEvaluationsFinal(students[0].student_id); + foundApplications.forEach((found_eval) => { + expect(found_eval).toHaveProperty("osoc", {year: osocs[0].year}); + expect(found_eval).toHaveProperty("evaluation"); + for (let i = 0; i < found_eval.evaluation.length; i++) { + const evals = evaluations[i]; + expect(found_eval.evaluation[i]).toHaveProperty("decision", evals.decision); + expect(found_eval.evaluation[i]).toHaveProperty("motivation", evals.motivation); + expect(found_eval.evaluation[i]).toHaveProperty("evaluation_id"); + } + + }); +}); + +it("should return all suggestion evaluations for the student with given id", async () => { + const students = await prisma.student.findMany(); + const osocs = await prisma.osoc.findMany(); + + const evaluations = [ + { + decision: decision_enum.MAYBE, + motivation: "low education level", + is_final: false, + } + ] + + const foundApplications = await getStudentEvaluationsTemp(students[0].student_id); + foundApplications.forEach((found_eval) => { + expect(found_eval).toHaveProperty("osoc", {year: osocs[0].year}); + expect(found_eval).toHaveProperty("evaluation"); + for (let i = 0; i < found_eval.evaluation.length; i++) { + const evals = evaluations[i]; + expect(found_eval.evaluation[i]).toHaveProperty("decision", evals.decision); + expect(found_eval.evaluation[i]).toHaveProperty("motivation", evals.motivation); + expect(found_eval.evaluation[i]).toHaveProperty("evaluation_id"); + } + + }); }); \ No newline at end of file diff --git a/backend/tests/orm_integration/person.test.ts b/backend/tests/orm_integration/person.test.ts index 1957d8b7..73f526f0 100644 --- a/backend/tests/orm_integration/person.test.ts +++ b/backend/tests/orm_integration/person.test.ts @@ -45,7 +45,7 @@ it('should create 1 new person where email is null', async () => { it('should find all the persons in the db, 2 in total', async () => { const searched_persons = await getAllPersons(); - expect(searched_persons[3]).toHaveProperty("github", person3.github); + expect(searched_persons[3]).toHaveProperty("github", null); expect(searched_persons[3]).toHaveProperty("firstname", person3.firstname); expect(searched_persons[3]).toHaveProperty("lastname", person3.lastname); expect(searched_persons[3]).toHaveProperty("email", person3.email); @@ -53,7 +53,7 @@ it('should find all the persons in the db, 2 in total', async () => { expect(searched_persons[4]).toHaveProperty("github", person4.github); expect(searched_persons[4]).toHaveProperty("firstname", person4.firstname); expect(searched_persons[4]).toHaveProperty("lastname", person4.lastname); - expect(searched_persons[4]).toHaveProperty("email", person4.email); + expect(searched_persons[4]).toHaveProperty("email", null); }); // Can only be tested with a login user, should therefore be tested in the login user tests? @@ -67,7 +67,7 @@ it('should find all the persons in the db, 2 in total', async () => { it('should find person 1 in the db, by searching for its firstname', async () => { const searched_person = await searchPersonByName(person3.firstname); - expect(searched_person[0]).toHaveProperty("github", person3.github); + expect(searched_person[0]).toHaveProperty("github", null); expect(searched_person[0]).toHaveProperty("firstname", person3.firstname); expect(searched_person[0]).toHaveProperty("lastname", person3.lastname); expect(searched_person[0]).toHaveProperty("email", person3.email); @@ -78,13 +78,13 @@ it('should find person 2 in the db, by searching for its lastname', async () => expect(searched_person4[0]).toHaveProperty("github", person4.github); expect(searched_person4[0]).toHaveProperty("firstname", person4.firstname); expect(searched_person4[0]).toHaveProperty("lastname", person4.lastname); - expect(searched_person4[0]).toHaveProperty("email", person4.email); + expect(searched_person4[0]).toHaveProperty("email", null); }); it('should find all the persons in the db with given email, 1 in total', async () => { if (person3.email != undefined) { const searched_persons = await searchPersonByLogin(person3.email); - expect(searched_persons[0]).toHaveProperty("github", person3.github); + expect(searched_persons[0]).toHaveProperty("github", null); expect(searched_persons[0]).toHaveProperty("firstname", person3.firstname); expect(searched_persons[0]).toHaveProperty("lastname", person3.lastname); expect(searched_persons[0]).toHaveProperty("email", person3.email); @@ -97,7 +97,7 @@ it('should find all the persons in the db with given github, 1 in total', async expect(searched_persons[0]).toHaveProperty("github", person4.github); expect(searched_persons[0]).toHaveProperty("firstname", person4.firstname); expect(searched_persons[0]).toHaveProperty("lastname", person4.lastname); - expect(searched_persons[0]).toHaveProperty("email", person4.email); + expect(searched_persons[0]).toHaveProperty("email", null); } }); From bdfb1c3f8b6a476319d920d323675d85b3f4fd50 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Tue, 29 Mar 2022 20:13:58 +0200 Subject: [PATCH 047/827] fix: fixed typo in comment --- backend/tests/orm_integration/role.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/tests/orm_integration/role.test.ts b/backend/tests/orm_integration/role.test.ts index c72748dc..241c848a 100644 --- a/backend/tests/orm_integration/role.test.ts +++ b/backend/tests/orm_integration/role.test.ts @@ -50,12 +50,12 @@ it('should update role based upon role id', async () => { expect(updated_role).toHaveProperty("name", role2.name); }); -it('should delete the project based upon role id', async () => { +it('should delete the role based upon role id', async () => { const deleted_role = await deleteRole(role2.roleId); expect(deleted_role).toHaveProperty("name", role2.name); }); -it('should delete the project based upon role name', async () => { +it('should delete the role based upon role name', async () => { const deleted_role = await deleteRoleByName("Marketeer"); expect(deleted_role).toHaveProperty("name", "Marketeer"); }); From 86afbcb54132816ee405d93658e911606e5ff30c Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Tue, 29 Mar 2022 23:02:55 +0200 Subject: [PATCH 048/827] fix: improvement of the form route (parse form to student) --- backend/routes/form.ts | 234 ++++++++++++++++++++++++++--------------- backend/types.ts | 4 - package-lock.json | 29 +++++ package.json | 4 + 4 files changed, 185 insertions(+), 86 deletions(-) diff --git a/backend/routes/form.ts b/backend/routes/form.ts index 1189d36d..406879e6 100644 --- a/backend/routes/form.ts +++ b/backend/routes/form.ts @@ -15,7 +15,7 @@ import * as validator from 'validator'; */ function filterQuestion(form: Requests.Form, key: string): Responses.FormResponse { const filteredQuestion = form.data.fields.filter(question => question.key == key); - return filteredQuestion.length > 0 ? {data : filteredQuestion[0], error : false} : {data : null, error : true}; + return filteredQuestion.length > 0 ? {data : filteredQuestion[0]} : {data : null}; } /** @@ -26,9 +26,9 @@ function filterQuestion(form: Requests.Form, key: string): Responses.FormRespons function filterChosenOption(question: Requests.Question): Responses.FormResponse { if(question.options != undefined) { const filteredOption = question.options.filter(option => option.id === question.value); - return {data : filteredOption[0], error : false}; + return {data : filteredOption[0]}; } - return {data : null, error : true} + return {data : null} } /** @@ -38,10 +38,10 @@ function filterChosenOption(question: Requests.Question): Responses.FormResponse */ function checkWordInAnswer(question: Requests.Question, word : string): Responses.FormResponse { const chosenOption : Responses.FormResponse = filterChosenOption(question); - return chosenOption.data != null ? {data : chosenOption.data.text.toLowerCase().includes(word), error : false} : {data : null, error : false}; + return chosenOption.data != null ? {data : chosenOption.data.text.toLowerCase().includes(word)} : {data : null}; } -function checkQuestionExist(questions: Responses.FormResponse[]) : boolean { +function checkQuestionsExist(questions: Responses.FormResponse[]) : boolean { const checkErrorInForm : Responses.FormResponse[] = questions.filter(dataError => dataError.data == null); return checkErrorInForm.length === 0; } @@ -57,8 +57,8 @@ function checkQuestionExist(questions: Responses.FormResponse */ function getBirthName(form: Requests.Form) : Promise { const questionBirthName: Responses.FormResponse = filterQuestion(form, "question_npDErJ"); - const questionExist : boolean = checkQuestionExist([questionBirthName]); - if(!questionExist || questionBirthName.data?.value == null) { + const questionsExist : boolean = checkQuestionsExist([questionBirthName]); + if(!questionsExist || questionBirthName.data?.value == null) { return Promise.reject(errors.cookArgumentError()); } return Promise.resolve(questionBirthName.data.value); @@ -72,8 +72,8 @@ function getBirthName(form: Requests.Form) : Promise { */ function getLastName(form: Requests.Form) : Promise { const questionLastName: Responses.FormResponse = filterQuestion(form, "question_319eXp"); - const questionExist : boolean = checkQuestionExist([questionLastName]); - if(!questionExist || questionLastName.data?.value == null) { + const questionsExist : boolean = checkQuestionsExist([questionLastName]); + if(!questionsExist || questionLastName.data?.value == null) { return Promise.reject(errors.cookArgumentError()); } return Promise.resolve(questionLastName.data.value); @@ -87,8 +87,8 @@ function getLastName(form: Requests.Form) : Promise { */ function getEmail(form: Requests.Form) : Promise { const questionEmail: Responses.FormResponse = filterQuestion(form, "question_mY46PB"); - const questionExist : boolean = checkQuestionExist([questionEmail]); - if(!questionExist || questionEmail.data?.value == null || !validator.default.isEmail(questionEmail.data.value)) { + const questionsExist : boolean = checkQuestionsExist([questionEmail]); + if(!questionsExist || questionEmail.data?.value == null || !validator.default.isEmail(questionEmail.data.value)) { return Promise.reject(errors.cookArgumentError()); } return Promise.resolve(validator.default.normalizeEmail(questionEmail.data.value).toString()); @@ -124,50 +124,38 @@ async function jsonToPerson(form: Requests.Form): Promise { }) } +/* parse form to student +***********************/ + /** - * Attempts to parse the answers in the form into a student entity. + * Parse the form to the pronouns of this student. * @param form The form with the answers. * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -/*async function jsonToStudent(form: Requests.Form, person: Responses.Person): - Promise { - - // The pronouns of this student - const questionAddPronouns: Responses.FormResponse = filterQuestion(form, "question_3yJQMg"); +function getPronouns(form: Requests.Form) : Promise { + const questionPronouns: Responses.FormResponse = filterQuestion(form, "question_3yJQMg"); const questionPreferedPronouns: Responses.FormResponse = filterQuestion(form, "question_3X4aLg"); const questionEnterPronouns: Responses.FormResponse = filterQuestion(form, "question_w8ZBq5"); - const questionGender: Responses.FormResponse = filterQuestion(form, "question_wg9laO"); - - const questionPhoneNumber: Responses.FormResponse = filterQuestion(form, "question_wd9MEo"); - - const questionCheckNickname: Responses.FormResponse = filterQuestion(form, "question_wME4XM"); - const questionEnterNickname: Responses.FormResponse = filterQuestion(form, "question_mJOPqo"); + const questionsExist : boolean = checkQuestionsExist([questionPronouns, questionPreferedPronouns, questionEnterPronouns]); + if(!questionsExist || questionPronouns.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } - const questionAlumni: Responses.FormResponse = filterQuestion(form, "question_mVzejJ"); + let pronouns: string[] = []; - const questionsExist : boolean = checkQuestionExist( - [questionAddPronouns, questionPreferedPronouns, questionEnterPronouns, questionGender, - questionPhoneNumber, questionCheckNickname, questionEnterPronouns, questionAlumni]); + const wordInAnswer : Responses.FormResponse = checkWordInAnswer(questionPronouns.data, "yes"); - if(!questionsExist) { + if(wordInAnswer.data == null) { return Promise.reject(errors.cookArgumentError()); } - if (questionAddPronouns.data?.value == null || questionGender.data?.value == null || - questionPhoneNumber.data?.value == null || questionCheckNickname.data?.value == null || - questionAlumni.data?.value == null) { - return Promise.reject(util.errors.cookArgumentError()); - } - - let pronouns: string[] = []; - - if(checkWordInAnswer(questionAddPronouns.data, "yes")) { + if(wordInAnswer) { const chosenOption : Responses.FormResponse = filterChosenOption(questionPreferedPronouns.data as Requests.Question); - if(chosenOption.error || chosenOption.data?.id.length === 0 || questionPreferedPronouns.data?.value == null) { + if(chosenOption.data == null || chosenOption.data?.id.length === 0 || questionPreferedPronouns.data?.value == null || checkWordInAnswer(questionPreferedPronouns.data, "other").data == null) { return Promise.reject(util.errors.cookArgumentError()); - } else if(!checkWordInAnswer(questionPreferedPronouns.data, "other") && chosenOption.data?.text != undefined) { + } else if(!checkWordInAnswer(questionPreferedPronouns.data, "other").data && chosenOption.data?.text != undefined) { pronouns = chosenOption.data.text.split("/"); } else { if(questionEnterPronouns.data?.value == null) { @@ -177,59 +165,141 @@ async function jsonToPerson(form: Requests.Form): Promise { } } - // The gender of this student + return Promise.resolve(pronouns); +} + +/** + * Parse the form to the gender of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getGender(form: Requests.Form) : Promise { + const questionGender: Responses.FormResponse = filterQuestion(form, "question_wg9laO"); + const questionsExist : boolean = checkQuestionsExist([questionGender]); + if(!questionsExist || questionGender.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + let gender; - const chosenGender : Requests.Option = filterChosenOption(questionGender); + const chosenGender : Responses.FormResponse = filterChosenOption(questionGender.data); - if(chosenGender.id.length === 0) { - return Promise.reject(errors.cookNonJSON("Invalid form")); + if(chosenGender.data == null || chosenGender.data.id.length === 0) { + return Promise.reject(errors.cookArgumentError()); } else { - gender = chosenGender.text; + gender = chosenGender.data.text; } - // The phone number of this student - const phoneNumber = questionPhoneNumber.value; + return Promise.resolve(gender); +} - // The nickname of this student +/** + * Parse the form to the phone number of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getPhoneNumber(form: Requests.Form) : Promise { + const questionPhoneNumber: Responses.FormResponse = filterQuestion(form, "question_wd9MEo"); + const questionsExist : boolean = checkQuestionsExist([questionPhoneNumber]); + if(!questionsExist || questionPhoneNumber.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + return Promise.resolve(questionPhoneNumber.data.value); +} - let nickname; +/** + * Parse the form to the email of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getNickname(form: Requests.Form) : Promise { + const questionCheckNickname: Responses.FormResponse = filterQuestion(form, "question_wME4XM"); + const questionEnterNickname: Responses.FormResponse = filterQuestion(form, "question_mJOPqo"); - if (checkWordInAnswer(questionCheckNickname, "yes")) { - nickname = questionEnterNickname.value; + const questionsExist : boolean = checkQuestionsExist([questionCheckNickname, questionEnterNickname]); + if(!questionsExist || questionCheckNickname.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); } - // Checks if this student has participated before - let alumni = false; - - if (questionAlumni.options !== undefined) { - alumni = questionAlumni.options - ?.filter(option => option.id === questionAlumni.value)[0] - .text.includes("yes"); - } - - if (nickname !== undefined) { - return ormSt - .createStudent({ - personId : person.person_id, - gender : gender, - pronouns : pronouns, - phoneNumber : phoneNumber, - nickname : nickname, - alumni : alumni - }) - .then(() => { return Promise.resolve({}); }); - } else { - return ormSt - .createStudent({ - personId : person.person_id, - gender : gender, - pronouns : pronouns, - phoneNumber : phoneNumber, - alumni : alumni - }) - .then(() => { return Promise.resolve({}); }); - } -}*/ + let nickname = null; + + if (checkWordInAnswer(questionCheckNickname.data, "yes")) { + if(questionEnterNickname.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + nickname = questionEnterNickname.data.value; + } + + return Promise.resolve(nickname); +} + +/** + * Parse the form to the email of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getAlumni(form: Requests.Form) : Promise { + const questionCheckAlumni: Responses.FormResponse = filterQuestion(form, "question_mVzejJ"); + + const questionsExist : boolean = checkQuestionsExist([questionCheckAlumni]); + + if(!questionsExist || questionCheckAlumni.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + + const wordInAnswer : boolean | null = checkWordInAnswer(questionCheckAlumni.data, "yes").data; + + if(wordInAnswer == null) { + return Promise.reject(errors.cookArgumentError()); + } else { + return Promise.resolve(wordInAnswer); + } +} + +/** + * Attempts to parse the answers in the form into a student entity. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +async function jsonToStudent(form: Requests.Form, person: Responses.Person): Promise { + return getPronouns(form) + .then(pronounsResponse => { + return getGender(form) + .then(genderResponse => { + return getPhoneNumber(form) + .then(phoneNumberResponse => { + return getNickname(form) + .then(nicknameResponse => { + return getAlumni(form) + .then(alumniResponse => { + if(nicknameResponse == null) { + return ormSt.createStudent({ + personId : person.person_id, + gender : genderResponse, + pronouns : pronounsResponse, + phoneNumber : phoneNumberResponse, + nickname : nicknameResponse, + alumni : alumniResponse + }).then(() => Promise.resolve({})); + } else { + return ormSt.createStudent({ + personId : person.person_id, + gender : genderResponse, + pronouns : pronounsResponse, + phoneNumber : phoneNumberResponse, + alumni : alumniResponse + }).then(() => Promise.resolve({})); + } + }); + }); + }); + }); + }); +} /** * Attempts to parse the answers in the form into a job application entity. diff --git a/backend/types.ts b/backend/types.ts index 717e0333..1e56adf2 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -400,10 +400,6 @@ export interface FormResponse { * The data. */ data: T | null; - /** - * The error reason. - */ - error: boolean; } } diff --git a/package-lock.json b/package-lock.json index 3389c5c5..4e993da5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,9 +4,13 @@ "requires": true, "packages": { "": { + "dependencies": { + "validator": "^13.7.0" + }, "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", + "@types/validator": "^13.7.2", "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", @@ -733,6 +737,12 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.7.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", + "integrity": "sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", @@ -5152,6 +5162,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5879,6 +5897,12 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/validator": { + "version": "13.7.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", + "integrity": "sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", @@ -9041,6 +9065,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 5553b241..a8466ec6 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", + "@types/validator": "^13.7.2", "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", @@ -17,5 +18,8 @@ "scripts": { "prepare": "husky install", "eslint": "eslint . --ext .js,.jsx,.ts,.tsx" + }, + "dependencies": { + "validator": "^13.7.0" } } From 7210f893f513ffd25bdc8406fb813322653e4710 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Mar 2022 03:36:25 +0000 Subject: [PATCH 049/827] npm-frontend(deps): bump react and react-dom in /frontend Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). These dependencies needed to be updated together. Updates `react` from 17.0.2 to 18.0.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v18.0.0/packages/react) Updates `react-dom` from 17.0.2 to 18.0.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v18.0.0/packages/react-dom) --- updated-dependencies: - dependency-name: react dependency-type: direct:production update-type: version-update:semver-major - dependency-name: react-dom dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 100 ++++++++++++++++++------------------- frontend/package.json | 4 +- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6d89033b..44ccc71d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,8 +14,8 @@ "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", - "react": "17.0.2", - "react-dom": "17.0.2" + "react": "18.0.0", + "react-dom": "18.0.0" }, "devDependencies": { "@types/bcrypt": "^5.0.0", @@ -2483,6 +2483,17 @@ "isomorphic-fetch": "^2.2.1" } }, + "node_modules/next/node_modules/use-subscription": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", + "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", + "dependencies": { + "object-assign": "^4.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -2893,28 +2904,26 @@ ] }, "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", + "integrity": "sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz", + "integrity": "sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" + "scheduler": "^0.21.0" }, "peerDependencies": { - "react": "17.0.2" + "react": "18.0.0" } }, "node_modules/react-is": { @@ -3067,12 +3076,11 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", + "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "node_modules/semver": { @@ -3516,17 +3524,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-subscription": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", - "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", - "dependencies": { - "object-assign": "^4.1.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5369,6 +5366,16 @@ "postcss": "8.4.5", "styled-jsx": "5.0.1", "use-subscription": "1.5.1" + }, + "dependencies": { + "use-subscription": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", + "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", + "requires": { + "object-assign": "^4.1.1" + } + } } }, "next-auth": { @@ -5684,22 +5691,20 @@ "dev": true }, "react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", + "integrity": "sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz", + "integrity": "sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==", "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" + "scheduler": "^0.21.0" } }, "react-is": { @@ -5790,12 +5795,11 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", + "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "semver": { @@ -6128,14 +6132,6 @@ "punycode": "^2.1.0" } }, - "use-subscription": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", - "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", - "requires": { - "object-assign": "^4.1.1" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 57a40325..fb3a3f9b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,8 +15,8 @@ "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", - "react": "17.0.2", - "react-dom": "17.0.2" + "react": "18.0.0", + "react-dom": "18.0.0" }, "devDependencies": { "@types/bcrypt": "^5.0.0", From 497f61844b9a4e17feb318d0705dbc060b60ce69 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Wed, 30 Mar 2022 10:30:35 +0200 Subject: [PATCH 050/827] test: created int. tests for attachment --- .../tests/orm_integration/attachment.test.ts | 58 +++++++++++++++++++ .../orm_integration/integration_setup.ts | 16 +++++ 2 files changed, 74 insertions(+) create mode 100644 backend/tests/orm_integration/attachment.test.ts diff --git a/backend/tests/orm_integration/attachment.test.ts b/backend/tests/orm_integration/attachment.test.ts new file mode 100644 index 00000000..9fd8e334 --- /dev/null +++ b/backend/tests/orm_integration/attachment.test.ts @@ -0,0 +1,58 @@ +import prisma from "../../prisma/prisma"; +import {createAttachment, deleteAttachment, getAttachmentById, + deleteAllAttachmentsForApplication} from "../../orm_functions/attachment"; +import { type_enum } from "@prisma/client"; + +const attachment1 = { + attachmentId: 0, + jobApplicationID: 0, + data: "mycvlink.com", + type: "CV_URL" +} + +const attachment2 = { + attachmentId: 0, + jobApplicationID: 0, + data: "myportfoliolink.com", + type: "PORTFOLIO_URL" +} + + +it('should create 1 new attachment linked to a job application', async () => { + const job_application = await prisma.job_application.findFirst(); + if (job_application){ + const created_attachment = await createAttachment(job_application.job_application_id, attachment1.data, (attachment1.type as type_enum)); + attachment1.jobApplicationID = created_attachment.job_application_id; + attachment1.attachmentId = created_attachment.attachment_id; + expect(created_attachment).toHaveProperty("data", attachment1.data); + expect(created_attachment).toHaveProperty("type", attachment1.type); + } +}); + +it('should create 1 new attachment linked to a job application', async () => { + const job_application = await prisma.job_application.findFirst(); + if (job_application){ + const created_attachment = await createAttachment(job_application.job_application_id, attachment2.data, (attachment2.type as type_enum)); + attachment2.jobApplicationID = created_attachment.job_application_id; + attachment2.attachmentId = created_attachment.attachment_id; + expect(created_attachment).toHaveProperty("data", attachment2.data); + expect(created_attachment).toHaveProperty("type", attachment2.type); + } +}); + +it('should return the attachment, by searching for its attachment id', async () => { + const searched_attachment = await getAttachmentById(attachment1.attachmentId); + expect(searched_attachment).toHaveProperty("data", attachment1.data); + expect(searched_attachment).toHaveProperty("type", attachment1.type); +}); + +it('should delete the attachment based upon attachment id', async () => { + const deleted_attachment = await deleteAttachment(attachment1.attachmentId); + expect(deleted_attachment).toHaveProperty("data", attachment1.data); + expect(deleted_attachment).toHaveProperty("type", attachment1.type); +}); + +it('should delete the attachment based upon job application id', async () => { + const deleted_attachments = await deleteAllAttachmentsForApplication(attachment2.jobApplicationID); + expect(deleted_attachments).toHaveProperty("count", 1); +}); diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 51f8ef39..ef901b16 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -205,6 +205,22 @@ beforeAll(async () => { ], }); + // create attachments + await prisma.attachment.createMany({ + data : [ + { + job_application_id: job_applications[1].job_application_id, + data: "test-cv-link.com", + type: "CV_URL" + }, + { + job_application_id: job_applications[1].job_application_id, + data: "test-portfolio-link.com", + type: "PORTFOLIO_URL" + } + ] + }) + }); afterAll(async () => { From 5f0ca6ccfd4476f84d8202559f4ad79a4e7e31f8 Mon Sep 17 00:00:00 2001 From: Jonathan Vanbrabant Date: Wed, 30 Mar 2022 14:48:13 +0200 Subject: [PATCH 051/827] feat: making students screen --- frontend/package-lock.json | 11 +++++++++++ frontend/package.json | 1 + 2 files changed, 12 insertions(+) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 44ccc71d..8c120029 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "bcrypt": "^5.0.1", + "bulma": "^0.9.3", "crypto-js": "^4.1.1", "next": "12.1.2", "next-auth": "^4.3.1", @@ -762,6 +763,11 @@ "node": ">=8" } }, + "node_modules/bulma": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.3.tgz", + "integrity": "sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==" + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -4114,6 +4120,11 @@ "fill-range": "^7.0.1" } }, + "bulma": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.3.tgz", + "integrity": "sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index fb3a3f9b..a1c5f38e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "bcrypt": "^5.0.1", + "bulma": "^0.9.3", "crypto-js": "^4.1.1", "next": "12.1.2", "next-auth": "^4.3.1", From 69ed082fd5e4d63875ac7664adfa3685a29da93a Mon Sep 17 00:00:00 2001 From: Jonathan Vanbrabant Date: Wed, 30 Mar 2022 15:59:02 +0200 Subject: [PATCH 052/827] feat: created StudentCard component --- frontend/components/StudentCard/StudentCard.tsx | 7 +++++++ frontend/pages/students.tsx | 8 ++------ 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 frontend/components/StudentCard/StudentCard.tsx diff --git a/frontend/components/StudentCard/StudentCard.tsx b/frontend/components/StudentCard/StudentCard.tsx new file mode 100644 index 00000000..80a43cb6 --- /dev/null +++ b/frontend/components/StudentCard/StudentCard.tsx @@ -0,0 +1,7 @@ +export const StudentCard: React.FC = () => { + return ( + <> +

Students overview

+ + ) +} \ No newline at end of file diff --git a/frontend/pages/students.tsx b/frontend/pages/students.tsx index 40c4ee4f..752d7c7d 100644 --- a/frontend/pages/students.tsx +++ b/frontend/pages/students.tsx @@ -1,16 +1,12 @@ -/** - * Students overview page - * TODO - */ import {NextPage} from "next"; -import {UnderConstruction} from "../components/UnderConstruction/UnderConstruction"; import {Header} from "../components/Header/Header"; +import {StudentCard} from "../components/StudentCard/StudentCard"; const Students: NextPage = () => { return ( <>
- + ) } From 149cb2e38cbdea784e3c25b44028cdc912a148a0 Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Wed, 30 Mar 2022 16:31:42 +0200 Subject: [PATCH 053/827] fix: form to job application route --- backend/routes/form.ts | 243 +++++++++++++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 55 deletions(-) diff --git a/backend/routes/form.ts b/backend/routes/form.ts index 406879e6..7eddbc61 100644 --- a/backend/routes/form.ts +++ b/backend/routes/form.ts @@ -2,7 +2,6 @@ import express from 'express'; import * as ormP from '../orm_functions/person'; import * as ormSt from '../orm_functions/student'; -import * as rq from '../request'; import {Requests, Responses} from '../types'; import * as util from "../utility"; import {errors} from "../utility"; @@ -125,7 +124,7 @@ async function jsonToPerson(form: Requests.Form): Promise { } /* parse form to student -***********************/ +************************/ /** * Parse the form to the pronouns of this student. @@ -133,7 +132,7 @@ async function jsonToPerson(form: Requests.Form): Promise { * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -function getPronouns(form: Requests.Form) : Promise { +function getPronouns(form: Requests.Form) : Promise { const questionPronouns: Responses.FormResponse = filterQuestion(form, "question_3yJQMg"); const questionPreferedPronouns: Responses.FormResponse = filterQuestion(form, "question_3X4aLg"); const questionEnterPronouns: Responses.FormResponse = filterQuestion(form, "question_w8ZBq5"); @@ -143,7 +142,7 @@ function getPronouns(form: Requests.Form) : Promise { return Promise.reject(errors.cookArgumentError()); } - let pronouns: string[] = []; + let pronouns: string[] | null = null; const wordInAnswer : Responses.FormResponse = checkWordInAnswer(questionPronouns.data, "yes"); @@ -181,16 +180,13 @@ function getGender(form: Requests.Form) : Promise { return Promise.reject(errors.cookArgumentError()); } - let gender; const chosenGender : Responses.FormResponse = filterChosenOption(questionGender.data); if(chosenGender.data == null || chosenGender.data.id.length === 0) { return Promise.reject(errors.cookArgumentError()); - } else { - gender = chosenGender.data.text; } - return Promise.resolve(gender); + return Promise.resolve(chosenGender.data.text); } /** @@ -254,9 +250,9 @@ function getAlumni(form: Requests.Form) : Promise { if(wordInAnswer == null) { return Promise.reject(errors.cookArgumentError()); - } else { - return Promise.resolve(wordInAnswer); } + + return Promise.resolve(wordInAnswer); } /** @@ -276,24 +272,14 @@ async function jsonToStudent(form: Requests.Form, person: Responses.Person): Pro .then(nicknameResponse => { return getAlumni(form) .then(alumniResponse => { - if(nicknameResponse == null) { - return ormSt.createStudent({ - personId : person.person_id, - gender : genderResponse, - pronouns : pronounsResponse, - phoneNumber : phoneNumberResponse, - nickname : nicknameResponse, - alumni : alumniResponse - }).then(() => Promise.resolve({})); - } else { - return ormSt.createStudent({ - personId : person.person_id, - gender : genderResponse, - pronouns : pronounsResponse, - phoneNumber : phoneNumberResponse, - alumni : alumniResponse - }).then(() => Promise.resolve({})); - } + return ormSt.createStudent({ + personId : person.person_id, + gender : genderResponse, + pronouns : pronounsResponse != null ? pronounsResponse : undefined, + phoneNumber : phoneNumberResponse, + nickname : nicknameResponse != null ? nicknameResponse : undefined, + alumni : alumniResponse + }).then(() => Promise.resolve({})); }); }); }); @@ -301,51 +287,198 @@ async function jsonToStudent(form: Requests.Form, person: Responses.Person): Pro }); } +/* parse form to job application +********************************/ + /** - * Attempts to parse the answers in the form into a job application entity. + * Parse the form to the volunteer info of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getVolunteerInfo(form: Requests.Form) : Promise { + const questionCheckVolunteerInfo: Responses.FormResponse = filterQuestion(form, "question_wvPZM0"); + + const questionsExist : boolean = checkQuestionsExist([questionCheckVolunteerInfo]); + + if(!questionsExist || questionCheckVolunteerInfo.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + + const chosenVolunteerInfo : Responses.FormResponse = filterChosenOption(questionCheckVolunteerInfo.data); + + if(chosenVolunteerInfo.data == null || chosenVolunteerInfo.data.id.length === 0) { + return Promise.reject(errors.cookArgumentError()); + } + + return Promise.resolve(chosenVolunteerInfo.data.text); +} + +/** + * Parse the form to the responsibilities of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getResponsibilities(form: Requests.Form) : Promise { + const questionCheckResponsibilities: Responses.FormResponse = filterQuestion(form, "question_wLPr9v"); + + const questionsExist : boolean = checkQuestionsExist([questionCheckResponsibilities]); + + if(!questionsExist) { + return Promise.reject(errors.cookArgumentError()); + } + + return Promise.resolve(questionCheckResponsibilities.data?.value == undefined ? null : questionCheckResponsibilities.data.value); +} + +/** + * Parse the form to the fun fact of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getFunFact(form: Requests.Form) : Promise { + const questionFunFact: Responses.FormResponse = filterQuestion(form, "question_nPzxpV"); + + const questionsExist : boolean = checkQuestionsExist([questionFunFact]); + + if(!questionsExist || questionFunFact.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + + return Promise.resolve(questionFunFact.data.value); +} + +/** + * Parse the form to the choice of being a student coach. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function isStudentCoach(form: Requests.Form) : Promise { + const questionStudentCoach: Responses.FormResponse = filterQuestion(form, "question_nPzxD5"); + + const questionsExist : boolean = checkQuestionsExist([questionStudentCoach]); + + if(!questionsExist || questionStudentCoach.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + + const wordInAnswer : boolean | null = checkWordInAnswer(questionStudentCoach.data, "yes").data; + + if(wordInAnswer == null) { + return Promise.reject(errors.cookArgumentError()); + } + + return Promise.resolve(wordInAnswer); +} + +/** + * Parse the form to the educations of this student. * @param form The form with the answers. * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -/*function jsonToJobApplication(form: Requests.Form, student: -Responses.Student): Promise { - // The volunteer info of the student - const questionVolunteerInfo: Requests.Question = - filterQuestion(form, "question_wvPZM0"); +function getEducations(form: Requests.Form) : Promise { + const questionCheckEducations: Responses.FormResponse = filterQuestion(form, "question_3ExRK4"); - let volunteerInfo : string = ""; + const questionsExist : boolean = checkQuestionsExist([questionCheckEducations]); - if(questionVolunteerInfo.options !== undefined) { - volunteerInfo = questionVolunteerInfo.options?.filter(option => -option.id === questionVolunteerInfo.value)[0].text; + if(!questionsExist || questionCheckEducations.data?.value == null || + questionCheckEducations.data.value.length === 0 || questionCheckEducations.data.value.length > 2) { + return Promise.reject(errors.cookArgumentError()); } - // The responsibilities of the student - const responsibilities: string | null = - filterQuestion(form, "question_wLPr9v").value; + const educations : string[] = []; - // A fun fact of this student - const funFact : string | null = filterQuestion(form, "question_nPzxpV").value; + for(let i = 0; i < questionCheckEducations.data.value.length; i++) { + if(questionCheckEducations.data.options != undefined) { + const filteredOption = questionCheckEducations.data.options.filter(option => option.id === questionCheckEducations.data?.value[i]); + if(filteredOption.length !== 1) { + return Promise.reject(errors.cookArgumentError()); + } + if(filteredOption[0].text.includes("Other")) { + const questionCheckOther: Responses.FormResponse = filterQuestion(form, "question_nro45N"); + const questionsExistOther : boolean = checkQuestionsExist([questionCheckOther]); - // Does this student want to be a student-coach - const questionStudentCoach: Requests.Question = - filterQuestion(form, "question_nPzxD5"); + if(!questionsExistOther || questionCheckOther.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } - let studentCoach : Boolean = false; + educations.push(questionCheckOther.data.value); + } else { + educations.push(filteredOption[0].text); + } + } + } - if(questionStudentCoach.options !== undefined && questionStudentCoach.value !== null && questionStudentCoach.value === "2055442c-a9a6-429d-9ada-045078295f86") { - studentCoach = true; + return Promise.resolve(educations); +} + +/** + * Parse the form to the duration of the education of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getEducationDuration(form: Requests.Form) : Promise { + const questionEducationDuration: Responses.FormResponse = filterQuestion(form, "question_w2KWBj"); + + const questionsExist : boolean = checkQuestionsExist([questionEducationDuration]); + + if(!questionsExist || questionEducationDuration.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); } - // The educations of the student - const questionStudentCoach: Requests.Question = - filterQuestion(form, "question_nPzxD5"); + return Promise.resolve(Number(questionEducationDuration.data.value)); +} - let studentCoach : Boolean = false; +/** + * Parse the form to the current year of the education of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getEducationYear(form: Requests.Form) : Promise { + const questionEducationYear: Responses.FormResponse = filterQuestion(form, "question_3xJqjr"); + + const questionsExist : boolean = checkQuestionsExist([questionEducationYear]); + + if(!questionsExist || questionEducationYear.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); + } + + return Promise.resolve(Number(questionEducationYear.data.value)); +} + +/** + * Parse the form to the university of this student. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +function getEducationUniversity(form: Requests.Form) : Promise { + const questionEducationUniversity: Responses.FormResponse = filterQuestion(form, "question_mRDNdd"); + + const questionsExist : boolean = checkQuestionsExist([questionEducationUniversity]); - if(questionStudentCoach.options !== undefined && questionStudentCoach.value !== null && questionStudentCoach.value === "2055442c-a9a6-429d-9ada-045078295f86") { - studentCoach = true; + if(!questionsExist || questionEducationUniversity.data?.value == null) { + return Promise.reject(errors.cookArgumentError()); } + + return Promise.resolve(questionEducationUniversity.data.value); +} + +// TODO parse education level, email status and created_at + +/** + * Attempts to parse the answers in the form into a job application entity. + * @param form The form with the answers. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ +/*function jsonToJobApplication(form: Requests.Form, student: Responses.Student): Promise { }*/ From 96efd552baaf0382be5852a7eec46e1d14929ebe Mon Sep 17 00:00:00 2001 From: jay-tux Date: Wed, 30 Mar 2022 17:52:38 +0200 Subject: [PATCH 054/827] feat: github login. Works mostly (90%). See todo's in issue #219 --- backend/.gitignore | 3 + backend/config.json | 14 ++++ backend/endpoints.ts | 2 + backend/package-lock.json | 43 +++++++++++- backend/package.json | 3 +- backend/routes/github.ts | 137 ++++++++++++++++++++++++++++++++++++++ backend/types.ts | 8 +++ 7 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 backend/routes/github.ts diff --git a/backend/.gitignore b/backend/.gitignore index 11ddd8db..996b76f5 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,6 @@ node_modules # Keep environment variables out of version control .env + +# Hide the GitHub secret +github.json diff --git a/backend/config.json b/backend/config.json index ade33c82..1cf209c0 100644 --- a/backend/config.json +++ b/backend/config.json @@ -33,6 +33,20 @@ "serverError": { "http": 500, "reason": "Something went wrong while trying to execute your request." + }, + "github": { + "argumentMissing": { + "http": 409, + "reason": "Something whent wrong while authenticating with GitHub." + }, + "illegalState": { + "http": 409, + "reason": "GitHub authentication endpoints are meant for GitHub authentication only. Thank you very much." + }, + "other": { + "http": 422, + "reason": "GitHub data requests failed. Please try again later." + } } }, diff --git a/backend/endpoints.ts b/backend/endpoints.ts index 130f09a1..a516d325 100644 --- a/backend/endpoints.ts +++ b/backend/endpoints.ts @@ -4,6 +4,7 @@ import * as config from './config.json'; import * as admin from './routes/admin'; import * as coach from './routes/coach'; import * as form from './routes/form'; +import * as github from './routes/github'; import * as login from './routes/login'; import * as project from './routes/project'; import * as student from './routes/student'; @@ -21,6 +22,7 @@ export function attach(app: express.Application): void { app.use(home + '/admin', admin.getRouter()); app.use(home + '/project', project.getRouter()); app.use(home + '/form', form.getRouter); + app.use(home + '/github', github.getRouter()); }); app.use((req: express.Request, res: express.Response): Promise => diff --git a/backend/package-lock.json b/backend/package-lock.json index 73ed2d72..6958fbe7 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,6 +15,7 @@ "@types/express": "^4.17.13", "@types/node": "^17.0.21", "@types/uuid": "^8.3.4", + "axios": "^0.26.1", "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", @@ -30,7 +31,7 @@ "jest-mock-extended": "^2.0.4", "node-ts": "^5.1.2", "nodemon": "^2.0.15", - "ts-node": "^10.5.0", + "ts-node": "^10.7.0", "typedoc": "^0.22.13", "typescript": "^4.5.5", "uuid": "^8.3.2" @@ -1764,6 +1765,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/babel-jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", @@ -3227,6 +3236,25 @@ "dev": true, "peer": true }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -8125,6 +8153,14 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, "babel-jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", @@ -9233,6 +9269,11 @@ "dev": true, "peer": true }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index da397416..1fc8787a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,6 +22,7 @@ "@types/express": "^4.17.13", "@types/node": "^17.0.21", "@types/uuid": "^8.3.4", + "axios": "^0.26.1", "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", @@ -37,7 +38,7 @@ "jest-mock-extended": "^2.0.4", "node-ts": "^5.1.2", "nodemon": "^2.0.15", - "ts-node": "^10.5.0", + "ts-node": "^10.7.0", "typedoc": "^0.22.13", "typescript": "^4.5.5", "uuid": "^8.3.2" diff --git a/backend/routes/github.ts b/backend/routes/github.ts new file mode 100644 index 00000000..2f17dbef --- /dev/null +++ b/backend/routes/github.ts @@ -0,0 +1,137 @@ +import axios from 'axios'; +import * as crypto from 'crypto'; +import express from 'express'; + +import * as config from '../config.json'; +import * as github from '../github.json'; +import * as ormLU from '../orm_functions/login_user'; +import * as ormP from '../orm_functions/person'; +import * as ormSK from '../orm_functions/session_key'; +import {Anything, Requests, Responses} from '../types'; +import * as util from '../utility'; + +let states: string[] = []; + +function getHome(): string { + const root = github.auth_callback_url; + // check if dev or production + console.log("Home is: " + root); + return root; +} + +function genState(): string { + const state = crypto.randomBytes(64).join(''); + states.push(state); + return state; +} + +function checkState(state: string) { + if (!states.includes(state)) { + return false; + } + + states = states.filter(x => x != state); + return true; +} + +// Step 1: redirect to github for identity +// Step 2: redirect to github for authentication +// Step 3: set session key, ... + +function ghIdentity(resp: express.Response): void { + let url = "https://github.com/login/oauth/authorize?"; + url += "client_id=" + github.client_id; // add client id + url += "&allow_signup=true"; // allow users to sign up to github itself + url += // set redirect + "&redirect_uri=" + + encodeURIComponent(getHome() + config.global.preferred + + "/github/challenge"); + url += "&state=" + genState(); + console.log("--- REDIRECTING TO GITHUB AT " + url + " ---"); + util.redirect(resp, url); +} + +async function ghExchangeAccessToken(req: express.Request): + Promise { + if (!("code" in req.query)) { + return Promise.reject(config.apiErrors.github.argumentMissing); + } + + if (!("state" in req.query)) { + return Promise.reject(config.apiErrors.github.argumentMissing); + } + + if (!checkState(req.query.state as string)) { + return Promise.reject(config.apiErrors.github.illegalState); + } + + return axios + .post("https://github.com/login/oauth/access_token", { + client_id : github.client_id, + client_secret : github.secret, + code : req.query.code as string, + redirect_uri : getHome() + config.global.preferred + "/github/login" + }, + {headers : {'Accept' : "application/json"}}) + .then(ares => axios.get("https://api.github.com/user", { + headers : {'Authorization' : "token " + ares.data.access_token} + })) + .then(ares => parseGHLogin(ares.data)) + .then(login => ghSignupOrLogin(login)) + .catch(err => { + console.log('GITHUB ERROR ' + err); + return Promise.reject(config.apiErrors.github.other); + }); +} + +function parseGHLogin(data: Anything): Promise { + if ('login' in data && 'name' in data) { + return Promise.resolve( + {login : data.login as string, name : data.name as string}); + } + return Promise.reject(); +} + +async function ghSignupOrLogin(login: Requests.GHLogin): + Promise { + return ormP.getPasswordPersonByEmail(login.login) + .then(person => { + if (person == null || person.login_user == null) + return Promise.reject({is_not_existent : true}); + return Promise.resolve(person.login_user); + }) + .catch(async error => { + if ('is_not_existent' in error && error.is_not_existent) { + return ormP + .createPerson( + {email : login.login, firstname : login.name, lastname : ''}) + .then(person => ormLU.createLoginUser({ + personId : person.person_id, + isAdmin : false, + isCoach : true, + accountStatus : 'PENDING' + })) + .then(res => Promise.resolve({ + password : res.password, + login_user_id : res.login_user_id, + account_status : res.account_status + })); + } else { + return Promise.reject(error); // pass on + } + }) + .then(async loginuser => + ormSK.addSessionKey(loginuser.login_user_id, util.generateKey()) + .then(newkey => Promise.resolve( + {sessionkey : newkey.session_key}))); +} + +export function getRouter(): express.Router { + const router: express.Router = express.Router(); + + router.get('/', (_, rs) => ghIdentity(rs)); + router.get('/challenge', (req, res) => util.respOrErrorNoReinject( + res, ghExchangeAccessToken(req))); + + return router; +} diff --git a/backend/types.ts b/backend/types.ts index a22d59f2..da91ff85 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -378,6 +378,14 @@ export interface Login { pass: string; } +/** + * To log in with GitHub, we require your GitHub login and username/alias. + */ +export interface GHLogin { + login: string; + name: string; +} + export interface KeyRequest { sessionkey: InternalTypes.SessionKey; } From 7222b087c1f3b33b0f8e9edfec4d39cc7e9749d4 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Wed, 30 Mar 2022 18:10:04 +0200 Subject: [PATCH 055/827] fix: signup works due to query change --- backend/routes/github.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routes/github.ts b/backend/routes/github.ts index 2f17dbef..0f36f483 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -104,7 +104,7 @@ async function ghSignupOrLogin(login: Requests.GHLogin): if ('is_not_existent' in error && error.is_not_existent) { return ormP .createPerson( - {email : login.login, firstname : login.name, lastname : ''}) + {github : login.login, firstname : login.name, lastname : ''}) .then(person => ormLU.createLoginUser({ personId : person.person_id, isAdmin : false, From 75ac68f00fcdf22ccdc42215eed49361eff41568 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Wed, 30 Mar 2022 21:09:11 +0200 Subject: [PATCH 056/827] feat: make small changes to db as requested by team members --- backend/orm_functions/orm_types.ts | 12 ++++++------ backend/prisma/schema.prisma | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/orm_functions/orm_types.ts b/backend/orm_functions/orm_types.ts index ea53d545..e8ff40c4 100644 --- a/backend/orm_functions/orm_types.ts +++ b/backend/orm_functions/orm_types.ts @@ -273,7 +273,7 @@ export interface CreateJobApplication { /** * a fun fact about the student */ - funFact?: string | null, + funFact: string, /** * string that has info if the student is available to work, and if he wants to work as volunteer for free or not */ @@ -289,23 +289,23 @@ export interface CreateJobApplication { /** * information about the educations of the student */ - edus?: string[], + edus: string[], /** * information about the education level of the student */ - eduLevel?: string | null, + eduLevel: string, /** * how long this student has been studying for */ - eduDuration?: number | null, + eduDuration: number, /** * expected graduation year */ - eduYear?: number | null, + eduYear: number, /** * institute the student is studying at */ - eduInstitute?: string | null, + eduInstitute: string, /** * information about a confirmation email for the evaluation */ diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index ac33a7c9..ca596372 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -51,14 +51,14 @@ model job_application { student_id Int @default(autoincrement()) student_volunteer_info String responsibilities String? - fun_fact String? + fun_fact String student_coach Boolean osoc_id Int edus String[] - edu_level String? - edu_duration Int? - edu_year Int? - edu_institute String? + edu_level String + edu_duration Int + edu_year Int + edu_institute String email_status email_status_enum created_at DateTime @db.Timestamptz(6) osoc osoc @relation(fields: [osoc_id], references: [osoc_id], onDelete: NoAction, onUpdate: NoAction) From 5dd2f6a73bdd9a235050d74c94a6631fab4eaabd Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Wed, 30 Mar 2022 21:14:38 +0200 Subject: [PATCH 057/827] fix: forgot to add file --- database/startupScripts/create_db.sql | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/database/startupScripts/create_db.sql b/database/startupScripts/create_db.sql index 2b6c2fdc..5df95b8f 100644 --- a/database/startupScripts/create_db.sql +++ b/database/startupScripts/create_db.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS person( person_id SERIAL PRIMARY KEY, email VARCHAR(320) UNIQUE, /* max email length is 320 characters */ - github TEXT UNIQUE, + github TEXT UNIQUE, firstname TEXT NOT NULL, lastname TEXT NOT NULL, CONSTRAINT login CHECK (email IS NOT NULL OR github IS NOT NULL), @@ -22,20 +22,20 @@ CREATE TABLE IF NOT EXISTS student( /* function used in login user to retrieve if email is used in person */ CREATE FUNCTION get_email_used(personId integer, given_password text) -RETURNS BOOLEAN +RETURNS BOOLEAN LANGUAGE plpgsql AS $$ -declare +declare email_used text; begin select email into email_used from person where person_id = personId; - if (email_used IS NOT NULL) and (given_password is null) then - return false; + if (email_used IS NOT NULL) and (given_password is null) then + return false; end if; - return true; + return true; END; $$; @@ -45,7 +45,7 @@ CREATE TYPE account_status_enum as ENUM ('ACTIVATED', 'PENDING', 'DISABLED', 'UN CREATE TABLE IF NOT EXISTS login_user( login_user_id SERIAL PRIMARY KEY, person_id SERIAL NOT NULL UNIQUE REFERENCES person(person_id), - "password" TEXT NULL, + "password" TEXT NULL, is_admin BOOLEAN NOT NULL, is_coach BOOLEAN NOT NULL, account_status account_status_enum NOT NULL, @@ -76,14 +76,14 @@ CREATE TABLE IF NOT EXISTS job_application ( student_id SERIAL NOT NULL REFERENCES student(student_id), student_volunteer_info TEXT NOT NULL, responsibilities TEXT, - fun_fact TEXT, + fun_fact TEXT NOT NULL, student_coach BOOLEAN NOT NULL, osoc_id INT NOT NULL REFERENCES osoc(osoc_id), - edus TEXT [], - edu_level TEXT, - edu_duration INT, - edu_year INT, - edu_institute TEXT, + edus TEXT [] NOT NULL, + edu_level TEXT NOT NULL, + edu_duration INT NOT NULL, + edu_year INT NOT NULL, + edu_institute TEXT NOT NULL, email_status email_status_enum NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL /* used to sort to get the latest application */ ); @@ -116,7 +116,7 @@ CREATE TABLE IF NOT EXISTS project ( description TEXT, start_date DATE NOT NULL, end_date DATE NOT NULL, - positions SMALLINT NOT NULL, + positions SMALLINT NOT NULL, CONSTRAINT dates CHECK (start_date <= end_date), CONSTRAINT valid_positions CHECK (positions > 0) ); From 3db1040e54ac92485e606787e40a54f44f439f27 Mon Sep 17 00:00:00 2001 From: Huan Date: Wed, 30 Mar 2022 21:23:32 +0200 Subject: [PATCH 058/827] feat: users screen first commit --- frontend/components/User/User.module.css | 0 frontend/components/User/User.tsx | 8 ++++++++ frontend/pages/users.tsx | 9 +++------ 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 frontend/components/User/User.module.css create mode 100644 frontend/components/User/User.tsx diff --git a/frontend/components/User/User.module.css b/frontend/components/User/User.module.css new file mode 100644 index 00000000..e69de29b diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx new file mode 100644 index 00000000..3fd10337 --- /dev/null +++ b/frontend/components/User/User.tsx @@ -0,0 +1,8 @@ +import styles from "./User.module.css"; +import React from "react"; + +export const User: React.FC = () => { + return (
+

Users

+
) +} diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 32f20a31..96324fb5 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -1,16 +1,13 @@ -/** - * Users overview page - * TODO - */ import {NextPage} from "next"; -import {UnderConstruction} from "../components/UnderConstruction/UnderConstruction"; import {Header} from "../components/Header/Header"; +import {User} from "../components/User/User"; const Users: NextPage = () => { return ( <>
- + + ) } From 6f857486d199b33ef322ce11ed8063e98accc5a1 Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Wed, 30 Mar 2022 21:32:55 +0200 Subject: [PATCH 059/827] fix: update api and fix spaces in the student.ts route --- backend/routes/student.ts | 85 ++++++++------- docs/API/api.json | 210 +++++++++++++++++++++++++++++++++++++- 2 files changed, 247 insertions(+), 48 deletions(-) diff --git a/backend/routes/student.ts b/backend/routes/student.ts index 64994111..d2119833 100644 --- a/backend/routes/student.ts +++ b/backend/routes/student.ts @@ -15,53 +15,50 @@ import {errors} from '../utility'; * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function listStudents(req: express.Request): - Promise { +async function listStudents(req: express.Request): Promise { return rq.parseStudentAllRequest(req) .then(parsed => util.checkSessionKey(parsed)) .then(parsed => { - const studentList: InternalTypes.Student[] = []; - ormSt.getAllStudents().then( - students => {students.forEach( - student => { - ormJo.getLatestJobApplicationOfStudent(student.student_id) - .then(jobApplication => { - if (jobApplication !== null) { - ormJo.getStudentEvaluationsTotal(student.student_id) - .then(evaluations => { - const languages: string[] = []; - jobApplication.job_application_skill.forEach( - skill => { - ormLa.getLanguage(skill.language_id) - .then(language => { - if (language !== null) { - languages.push(language.name); - } else { - return Promise.reject( - errors.cookInvalidID); - } - })}); - studentList.push({ - firstname : student.person.firstname, - lastname : student.person.lastname, - email : student.person.email, - gender : student.gender, - pronouns : student.pronouns, - phoneNumber : student.phone_number, - nickname : student.nickname, - alumni : student.alumni, - languages : languages, - jobApplication : jobApplication, - evaluations : evaluations - }) - }) - } else { - return Promise.reject(errors.cookInvalidID); - } - })})}); - - return Promise.resolve( - {data : studentList, sessionkey : parsed.data.sessionkey}); + const studentList: InternalTypes.Student[] = []; + return ormSt.getAllStudents().then( + students => {students.forEach( + student => { + ormJo.getLatestJobApplicationOfStudent(student.student_id) + .then(jobApplication => { + if (jobApplication !== null) { + ormJo.getStudentEvaluationsTotal(student.student_id) + .then(evaluations => { + const languages: string[] = []; + jobApplication.job_application_skill.forEach( + skill => { + ormLa.getLanguage(skill.language_id) + .then(language => { + if (language !== null) { + languages.push(language.name); + } else { + return Promise.reject( + errors.cookInvalidID); + } + })}); + studentList.push({ + firstname : student.person.firstname, + lastname : student.person.lastname, + email : student.person.email, + gender : student.gender, + pronouns : student.pronouns, + phoneNumber : student.phone_number, + nickname : student.nickname, + alumni : student.alumni, + languages : languages, + jobApplication : jobApplication, + evaluations : evaluations + }) + }) + } else { + return Promise.reject(errors.cookInvalidID); + } + })})}) + .then(() => Promise.resolve({data : studentList, sessionkey : parsed.data.sessionkey})); }); } diff --git a/docs/API/api.json b/docs/API/api.json index 355c95f7..a4ba0fc5 100644 --- a/docs/API/api.json +++ b/docs/API/api.json @@ -61,7 +61,10 @@ ], "responses": { "200": { - "description": "Updated sessionkey as hex string E.g. \"A1BEF4\"." + "description": "Updated sessionkey as hex string E.g. \"A1BEF4\".", + "schema": { + "$ref": "#/definitions/Login" + } } } }, @@ -850,6 +853,20 @@ "name": "Sessionkey" } }, + "Login": { + "type": "object", + "properties": { + "sessionkey": { + "type": "string" + }, + "is_admin": { + "type": "boolean" + }, + "is_coach": { + "type": "boolean" + } + } + }, "NameId": { "type": "object", "properties": { @@ -890,17 +907,202 @@ "AllStudents": { "type": "object", "properties": { - "data": { - "$ref": "#/definitions/NameIdList" + "firstname": { + "type": "string" }, - "sessionkey": { + "lastname": { + "type": "string" + }, + "email": { + "type": "string" + }, + "gender": { + "type": "string" + }, + "pronouns": { + "type": "array", + "items": { + "type": "string" + } + }, + "phoneNumber": { + "type": "string" + }, + "nickname": { "type": "string" + }, + "alumni": { + "type": "boolean" + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, + "jobApplication": { + "type": "object", + "properties": { + "job_application_id": { + "type": "number" + }, + "edus": { + "type": "array", + "items": { + "type": "string", + "default": "Informatics" + } + }, + "created_at": { + "type": "string", + "example": "2022-03-14 23:10:00+01" + }, + "edu_duration": { + "type": "number", + "default": 5 + }, + "edu_institute": { + "type": "string", + "default": "Ghent university" + }, + "edu_level": { + "type": "string", + "default": "Third bachelors degree" + }, + "edu_year": { + "type": "number", + "default": 3 + }, + "email_status": { + "type": "string", + "enum": [ + "SCHEDULED", + "SENT", + "FAILED", + "NONE", + "DRAFT" + ], + "default": "NONE" + }, + "fun_fact": { + "type": "string", + "default": "I like basketball." + }, + "osoc_id": { + "type": "number" + }, + "responsibilities": { + "type": "string", + "default": "I play basketball from 5pm until 7pm." + }, + "student_coach": { + "type": "boolean" + }, + "student_id": { + "type": "number" + }, + "student_volunteer_info": { + "type": "string", + "default": "Yes, I can work with a student employment agreement in Belgium." + }, + "attachment": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + } + }, + "job_application_skill": { + "type": "array", + "items": { + "$ref": "#/definitions/JobApplicationSkill" + } + }, + "applied_role": { + "type": "array", + "items": { + "$ref": "#/definitions/AppliedRole" + } + } + } } }, "xml": { "name": "AllStudents" } }, + "Attachment": { + "type": "object", + "properties": { + "job_application_id": { + "type": "number" + }, + "attachment_id": { + "type": "number" + }, + "data": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "CV_URL", + "PORTFOLIO_URL", + "FILE_URL", + "MOTIVATION_STRING", + "MOTIVATION_URL" + ] + } + }, + "xml": { + "name": "Attachment" + } + }, + "JobApplicationSkill": { + "type": "object", + "properties": { + "job_application_id": { + "type": "number" + }, + "job_application_skill_id": { + "type": "number" + }, + "skill": { + "type": "string" + }, + "is_best": { + "type": "boolean" + }, + "language_id": { + "type": "number" + }, + "is_preferred": { + "type": "boolean" + }, + "level": { + "type": "number" + } + }, + "xml": { + "name": "JobApplicationSkill" + } + }, + "AppliedRole": { + "type": "object", + "properties": { + "job_application_id": { + "type": "number" + }, + "applied_role_id": { + "type": "number" + }, + "role_id": { + "type": "number" + } + }, + "xml": { + "name": "JobApplicationSkill" + } + }, "Student_Id_contract": { "type": "object", "properties": { From 6ad4f827ecde8ae02e669b9a1a2123c3d0024441 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Wed, 30 Mar 2022 21:35:06 +0200 Subject: [PATCH 060/827] test: created int. tests for job skill --- .../orm_integration/integration_setup.ts | 26 ++++- .../job_application_skill.test.ts | 106 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 backend/tests/orm_integration/job_application_skill.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index ef901b16..0fb1c40c 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -221,11 +221,35 @@ beforeAll(async () => { ] }) + const languages = await prisma.language.findMany(); + // create job application skills + await prisma.job_application_skill.createMany({ + data: [ + { + job_application_id: job_applications[1].job_application_id, + skill: "SQL", + language_id: languages[0].language_id, + level: 5, + is_preferred: false, + is_best: true + + }, + { + job_application_id: job_applications[0].job_application_id, + skill: "Python", + language_id: languages[1].language_id, + level: 4, + is_preferred: false, + is_best: false + }, + ], + }); + }); afterAll(async () => { - const deleteLanguageDetails = prisma.language.deleteMany(); const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); + const deleteLanguageDetails = prisma.language.deleteMany(); const deleteAttachmentDetails = prisma.attachment.deleteMany(); const deleteAppliedRoleDetails = prisma.applied_role.deleteMany(); const deleteEvaluationDetails = prisma.evaluation.deleteMany(); diff --git a/backend/tests/orm_integration/job_application_skill.test.ts b/backend/tests/orm_integration/job_application_skill.test.ts new file mode 100644 index 00000000..47271bb3 --- /dev/null +++ b/backend/tests/orm_integration/job_application_skill.test.ts @@ -0,0 +1,106 @@ +import prisma from "../../prisma/prisma"; +import {createJobApplicationSkill, getAllJobApplicationSkill, + getAllJobApplicationSkillByJobApplication, getJobApplicationSkill, + updateJobApplicationSkill, deleteJobApplicationSkill} from "../../orm_functions/job_application_skill"; +import { CreateJobApplicationSkill, UpdateJobApplicationSkill } from "../../orm_functions/orm_types"; + +const jobApplicationSkill1: UpdateJobApplicationSkill = { + JobApplicationSkillId: 1, + JobApplicationId: 0, + skill: "C++", + languageId: 0, + level: 2, + isPreferred: true, + is_best: true +} + +const jobApplicationSkill2: UpdateJobApplicationSkill = { + JobApplicationSkillId: 1, + JobApplicationId: 0, + skill: "C++", + languageId: 0, + level: 2, + isPreferred: true, + is_best: true +} + +it('should create 1 new job application skill', async () => { + const job_application = await prisma.job_application.findFirst(); + const language = await prisma.language.findFirst(); + + if (job_application && language){ + const jobApplicationSkill: CreateJobApplicationSkill = { + jobApplicationId: job_application.job_application_id, + skill: "C++", + languageId: language.language_id, + level: 2, + isPreferred: true, + isBest: true + } + jobApplicationSkill1.JobApplicationId = job_application.job_application_id; + jobApplicationSkill2.JobApplicationId = job_application.job_application_id; + jobApplicationSkill1.languageId = language.language_id; + jobApplicationSkill2.languageId = language.language_id; + + const created_job_application_skill = await createJobApplicationSkill(jobApplicationSkill); + jobApplicationSkill1.JobApplicationSkillId = created_job_application_skill.job_application_skill_id; + jobApplicationSkill2.JobApplicationSkillId = created_job_application_skill.job_application_skill_id; + expect(created_job_application_skill).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); + expect(created_job_application_skill).toHaveProperty("skill", jobApplicationSkill1.skill); + expect(created_job_application_skill).toHaveProperty("language_id", jobApplicationSkill1.languageId); + expect(created_job_application_skill).toHaveProperty("level", jobApplicationSkill1.level); + expect(created_job_application_skill).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); + expect(created_job_application_skill).toHaveProperty("is_best", jobApplicationSkill1.is_best); + } +}); + +it('should find all the job_applications skills in the db, 3 in total', async () => { + const searched_job_application_skills = await getAllJobApplicationSkill(); + expect(searched_job_application_skills.length).toEqual(3); + expect(searched_job_application_skills[2]).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skills[2]).toHaveProperty("skill", jobApplicationSkill1.skill); + expect(searched_job_application_skills[2]).toHaveProperty("language_id", jobApplicationSkill1.languageId); + expect(searched_job_application_skills[2]).toHaveProperty("level", jobApplicationSkill1.level); + expect(searched_job_application_skills[2]).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); + expect(searched_job_application_skills[2]).toHaveProperty("is_best", jobApplicationSkill1.is_best); +}); + +it('should find all the job_applications skills linked to the job application', async () => { + const searched_job_application_skills = await getAllJobApplicationSkillByJobApplication(jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skills[0]).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skills[0]).toHaveProperty("skill", jobApplicationSkill1.skill); + expect(searched_job_application_skills[0]).toHaveProperty("language_id", jobApplicationSkill1.languageId); + expect(searched_job_application_skills[0]).toHaveProperty("level", jobApplicationSkill1.level); + expect(searched_job_application_skills[0]).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); + expect(searched_job_application_skills[0]).toHaveProperty("is_best", jobApplicationSkill1.is_best); +}); + +it('should find the job_applications skill by its id', async () => { + const searched_job_application_skill = await getJobApplicationSkill(jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skill).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skill).toHaveProperty("skill", jobApplicationSkill1.skill); + expect(searched_job_application_skill).toHaveProperty("language_id", jobApplicationSkill1.languageId); + expect(searched_job_application_skill).toHaveProperty("level", jobApplicationSkill1.level); + expect(searched_job_application_skill).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); + expect(searched_job_application_skill).toHaveProperty("is_best", jobApplicationSkill1.is_best); +}); + +it('should update job application skill based upon id', async () => { + const updated_job_application_skill = await updateJobApplicationSkill(jobApplicationSkill2); + expect(updated_job_application_skill).toHaveProperty("job_application_id", jobApplicationSkill2.JobApplicationId); + expect(updated_job_application_skill).toHaveProperty("skill", jobApplicationSkill2.skill); + expect(updated_job_application_skill).toHaveProperty("language_id", jobApplicationSkill2.languageId); + expect(updated_job_application_skill).toHaveProperty("level", jobApplicationSkill2.level); + expect(updated_job_application_skill).toHaveProperty("is_preferred", jobApplicationSkill2.isPreferred); + expect(updated_job_application_skill).toHaveProperty("is_best", jobApplicationSkill2.is_best); +}); + +it('should delete the job application skill based upon id', async () => { + const deleted_job_application_skill = await deleteJobApplicationSkill(jobApplicationSkill2.JobApplicationSkillId); + expect(deleted_job_application_skill).toHaveProperty("job_application_id", jobApplicationSkill2.JobApplicationId); + expect(deleted_job_application_skill).toHaveProperty("skill", jobApplicationSkill2.skill); + expect(deleted_job_application_skill).toHaveProperty("language_id", jobApplicationSkill2.languageId); + expect(deleted_job_application_skill).toHaveProperty("level", jobApplicationSkill2.level); + expect(deleted_job_application_skill).toHaveProperty("is_preferred", jobApplicationSkill2.isPreferred); + expect(deleted_job_application_skill).toHaveProperty("is_best", jobApplicationSkill2.is_best); +}); From 5ffdc8e7a1be2afb0e1d26f3b35130178441933e Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Wed, 30 Mar 2022 22:05:59 +0200 Subject: [PATCH 061/827] feat: add password reset table, queries for it and unit tests --- backend/orm_functions/password_reset.ts | 53 +++++++++++++++++++ backend/prisma/schema.prisma | 9 ++++ .../tests/orm_tests/password_reset.test.ts | 28 ++++++++++ database/startupScripts/create_db.sql | 7 +++ 4 files changed, 97 insertions(+) create mode 100644 backend/orm_functions/password_reset.ts create mode 100644 backend/tests/orm_tests/password_reset.test.ts diff --git a/backend/orm_functions/password_reset.ts b/backend/orm_functions/password_reset.ts new file mode 100644 index 00000000..8e61f10b --- /dev/null +++ b/backend/orm_functions/password_reset.ts @@ -0,0 +1,53 @@ +import prisma from "../prisma/prisma"; + +/** + * creates a new entry for password reset if none exists yet. + * overwrites the info of the old reset entry if a reset entry already exists + * + * @param loginUserId the id of the loginUser whose passport we want to reset + * @param resetId the unique id to reset the passport of the login user + * @param validUntil timestamp that shows until when the resetId is valid + * @return the created/updated entry from the database in a promise + */ +export async function createOrUpdateReset(loginUserId: number, resetId: string, validUntil: Date) { + return await prisma.password_reset.upsert({ + where: { + login_user_id: loginUserId, + }, + create: { + login_user_id: loginUserId, + reset_id: resetId, + valid_until: validUntil + }, + update : { + reset_id: resetId, + valid_until: validUntil + } + }); +} + +/** + * + * @param loginUserId the id of the loginUser whose reset entry we want to delete + * @returns the deleted entry (or null if there was no entry) inside a promise + */ +export async function deleteResetWithLoginUser(loginUserId: number) { + return await prisma.password_reset.delete({ + where: { + login_user_id: loginUserId + } + }); +} + +/** + * + * @param resetId the resetId of the entry we want to delete + * @returns the deleted entry (or null if there was no entry) inside a promise + */ +export async function deleteResetWithResetId(resetId: string) { + return await prisma.password_reset.delete({ + where : { + reset_id: resetId, + } + }); +} \ No newline at end of file diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index ca596372..4ed61a32 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -97,6 +97,7 @@ model login_user { person person @relation(fields: [person_id], references: [person_id], onDelete: NoAction, onUpdate: NoAction) contract contract[] evaluation evaluation[] + password_reset password_reset? project_user project_user[] session_keys session_keys[] } @@ -177,6 +178,14 @@ model session_keys { login_user login_user @relation(fields: [login_user_id], references: [login_user_id], onDelete: NoAction, onUpdate: NoAction) } +model password_reset { + password_reset_id Int @id @default(autoincrement()) + login_user_id Int @unique @default(autoincrement()) + reset_id String @unique @db.VarChar(128) + valid_until DateTime @db.Timestamptz(6) + login_user login_user @relation(fields: [login_user_id], references: [login_user_id], onDelete: NoAction, onUpdate: NoAction) +} + enum decision_enum { YES NO diff --git a/backend/tests/orm_tests/password_reset.test.ts b/backend/tests/orm_tests/password_reset.test.ts new file mode 100644 index 00000000..a84e0e18 --- /dev/null +++ b/backend/tests/orm_tests/password_reset.test.ts @@ -0,0 +1,28 @@ +import {prismaMock} from "./singleton"; +import { + createOrUpdateReset, + deleteResetWithLoginUser, + deleteResetWithResetId +} from "../../orm_functions/password_reset"; + +const response = { + password_reset_id: 1, + login_user_id: 1, + reset_id: "reset_id", + valid_until: new Date() +} + +test("should return the inserted/updated entry", async () => { + prismaMock.password_reset.upsert.mockResolvedValue(response); + await expect(createOrUpdateReset(response.login_user_id, response.reset_id, response.valid_until)).resolves.toEqual(response); +}); + +test("should return the deleted entry", async () => { + prismaMock.password_reset.delete.mockResolvedValue(response); + await expect(deleteResetWithLoginUser(1)).resolves.toEqual(response); +}) + +test("should return the deleted entry", async () => { + prismaMock.password_reset.delete.mockResolvedValue(response); + await expect(deleteResetWithResetId("reset_id")).resolves.toEqual(response); +}) \ No newline at end of file diff --git a/database/startupScripts/create_db.sql b/database/startupScripts/create_db.sql index 5df95b8f..a97d5e38 100644 --- a/database/startupScripts/create_db.sql +++ b/database/startupScripts/create_db.sql @@ -60,6 +60,13 @@ CREATE TABLE IF NOT EXISTS session_keys( session_key VARCHAR(128) NOT NULL UNIQUE ); +CREATE TABLE IF NOT EXISTS password_reset( + password_reset_id SERIAL PRIMARY KEY, /* unique id for this table */ + login_user_id SERIAL UNIQUE NOT NULL REFERENCES login_user(login_user_id), + reset_id VARCHAR(128) UNIQUE NOT NULL, /* random id generated to use in the link for password reset */ + valid_until TIMESTAMP WITH TIME ZONE NOT NULL +); + CREATE TABLE IF NOT EXISTS osoc( osoc_id SERIAL PRIMARY KEY, From 85a5bf8ebff4486124bf9124e4def3ed9ee5f1c9 Mon Sep 17 00:00:00 2001 From: Huan Date: Wed, 30 Mar 2022 22:54:12 +0200 Subject: [PATCH 062/827] feat: boilerplate for getting the login users frontend --- frontend/components/User/User.tsx | 28 +++++++++++++++++++++++++++- frontend/pages/login.tsx | 5 +++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 3fd10337..37704e97 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -1,7 +1,33 @@ import styles from "./User.module.css"; -import React from "react"; export const User: React.FC = () => { + //boilerplate for the admin/coaches route (pass admin/coach as string) + const getAlluser = async (route: string) => { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route, { + method: 'GET', + //body: JSON.stringify({pass: loginPassword, name: loginEmail}), + body: JSON.stringify({sessionKey: "TODO"}), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }) + .then(response => response.json()).then(json => { + if (!json.success) { + + //TODO logica van niet gelukt + return {success: false}; + } else return json; + }) + .catch(err => { + console.log(err) + //TODO logica van niet gelukt + return {success: false}; + }); + console.log(response) + } + + console.log(getAlluser("admin")) return (

Users

) diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index ea864776..85bf7d90 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -68,12 +68,13 @@ const Login: NextPage = () => { // Fields are not empty if (!error) { // We encrypt the password before sending it to the backend api - const encryptedPassword = crypto.createHash('sha256').update(loginPassword).digest('hex'); + //TODO enable encryption + //const encryptedPassword = crypto.createHash('sha256').update(loginPassword).digest('hex'); const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/login`, { method: 'POST', //body: JSON.stringify({pass: loginPassword, name: loginEmail}), - body: JSON.stringify({pass: encryptedPassword, name: loginEmail}), + body: JSON.stringify({pass: loginPassword, name: loginEmail}), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' From 43c0f0cbcf249b2a6afd0ebd584469969c8a987d Mon Sep 17 00:00:00 2001 From: Huan Date: Wed, 30 Mar 2022 23:10:26 +0200 Subject: [PATCH 063/827] fix: fixed the route and the header of boilerplate User --- frontend/components/User/User.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 37704e97..512f4bf2 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -3,13 +3,13 @@ import styles from "./User.module.css"; export const User: React.FC = () => { //boilerplate for the admin/coaches route (pass admin/coach as string) const getAlluser = async (route: string) => { - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route, { + console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { method: 'GET', - //body: JSON.stringify({pass: loginPassword, name: loginEmail}), - body: JSON.stringify({sessionKey: "TODO"}), headers: { 'Content-Type': 'application/json', - 'Accept': 'application/json' + 'Accept': 'application/json', + 'sessionKey': 'TODO' } }) .then(response => response.json()).then(json => { @@ -27,7 +27,7 @@ export const User: React.FC = () => { console.log(response) } - console.log(getAlluser("admin")) + getAlluser("admin") return (

Users

) From 8fe40cb01b0ac2e6efd54a4075f29b44a5303a16 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Wed, 30 Mar 2022 23:36:18 +0200 Subject: [PATCH 064/827] fix: await every delete in afterAll to fix tests --- backend/package.json | 2 +- .../orm_integration/integration_setup.ts | 158 ++++++++++-------- .../tests/orm_integration/language.test.ts | 22 +-- 3 files changed, 103 insertions(+), 79 deletions(-) diff --git a/backend/package.json b/backend/package.json index da397416..04b101ab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "nodemon index.ts", - "migrate:postgres": "npx dotenv -e prisma/.env.test -- npx prisma migrate dev --name postgres-init", + "migrate:postgres": "npx dotenv -e prisma/.env.test -- npx prisma db push", "dockerIntegration:up": "docker-compose -f docker-compose.integrationtest.yml up -d --build", "docker:down": "docker-compose down", "integrationTests": "npm run dockerIntegration:up && npm run migrate:postgres && jest -i", diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 0fb1c40c..3d75efbe 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -175,27 +175,30 @@ beforeAll(async () => { const projects = await prisma.project.findFirst(); const role = await prisma.role.findFirst(); - // create roles - await prisma.project_role.createMany({ - data: [ - { - project_id: projects!.project_id, - role_id: role!.role_id, - positions: 3 + if (projects && role) { + // create roles + await prisma.project_role.createMany({ + data: [ + { + project_id: projects.project_id, + role_id: role.role_id, + positions: 3 + + }, + { + project_id: projects.project_id, + role_id: role.role_id, + positions: 1 + }, + ], + }); + } - }, - { - project_id: projects!.project_id, - role_id: role!.role_id, - positions: 1 - }, - ], - }); // create languages await prisma.language.createMany({ data: [ - { + { name: "Dutch", }, @@ -204,28 +207,28 @@ beforeAll(async () => { }, ], }); - - // create attachments - await prisma.attachment.createMany({ - data : [ - { - job_application_id: job_applications[1].job_application_id, - data: "test-cv-link.com", - type: "CV_URL" - }, - { - job_application_id: job_applications[1].job_application_id, - data: "test-portfolio-link.com", - type: "PORTFOLIO_URL" - } - ] - }) - + // + // // create attachments + // await prisma.attachment.createMany({ + // data : [ + // { + // job_application_id: job_applications[1].job_application_id, + // data: "test-cv-link.com", + // type: "CV_URL" + // }, + // { + // job_application_id: job_applications[1].job_application_id, + // data: "test-portfolio-link.com", + // type: "PORTFOLIO_URL" + // } + // ] + // }) + // const languages = await prisma.language.findMany(); // create job application skills await prisma.job_application_skill.createMany({ data: [ - { + { job_application_id: job_applications[1].job_application_id, skill: "SQL", language_id: languages[0].language_id, @@ -248,41 +251,60 @@ beforeAll(async () => { }); afterAll(async () => { - const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); - const deleteLanguageDetails = prisma.language.deleteMany(); - const deleteAttachmentDetails = prisma.attachment.deleteMany(); - const deleteAppliedRoleDetails = prisma.applied_role.deleteMany(); - const deleteEvaluationDetails = prisma.evaluation.deleteMany(); - const deleteApplicationDetails = prisma.job_application.deleteMany(); - const deleteSessionKeysDetails = prisma.session_keys.deleteMany(); - const deleteProjectUserDetails = prisma.project_user.deleteMany(); - const deleteContractDetails = prisma.contract.deleteMany(); - const deleteProjectRoleDetails = prisma.project_role.deleteMany(); - const deleteProjectDetails = prisma.project.deleteMany(); - const deleteOsocDetails = prisma.osoc.deleteMany(); - const deleteStudentDetails = prisma.student.deleteMany(); - const deleteLoginUserDetails = prisma.login_user.deleteMany(); - const deleteRoleDetails = prisma.role.deleteMany(); - const deletePersonDetails = prisma.person.deleteMany(); + // TODO: probeer deze gecommente code te hergebruiken ipv alles in losse awaits te doen, maar los werkt voorlopig + + // const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); + // const deleteLanguageDetails = prisma.language.deleteMany(); + // const deleteAttachmentDetails = prisma.attachment.deleteMany(); + // const deleteAppliedRoleDetails = prisma.applied_role.deleteMany(); + // const deleteEvaluationDetails = prisma.evaluation.deleteMany(); + // const deleteApplicationDetails = prisma.job_application.deleteMany(); + // const deleteSessionKeysDetails = prisma.session_keys.deleteMany(); + // const deleteProjectUserDetails = prisma.project_user.deleteMany(); + // const deleteContractDetails = prisma.contract.deleteMany(); + // const deleteProjectRoleDetails = prisma.project_role.deleteMany(); + // const deleteProjectDetails = prisma.project.deleteMany(); + // const deleteOsocDetails = prisma.osoc.deleteMany(); + // const deleteStudentDetails = prisma.student.deleteMany(); + // const deleteLoginUserDetails = prisma.login_user.deleteMany(); + // const deleteRoleDetails = prisma.role.deleteMany(); + // const deletePersonDetails = prisma.person.deleteMany(); + // + // await prisma.$transaction([ + // deleteLanguageDetails, + // deleteJobApplicationSkillDetails, + // deleteAttachmentDetails, + // deleteAppliedRoleDetails, + // deleteEvaluationDetails, + // deleteApplicationDetails, + // deleteSessionKeysDetails, + // deleteProjectUserDetails, + // deleteContractDetails, + // deleteProjectRoleDetails, + // deleteProjectDetails, + // deleteOsocDetails, + // deleteStudentDetails, + // deleteLoginUserDetails, + // deleteRoleDetails, + // deletePersonDetails, + // ]); - await prisma.$transaction([ - deleteLanguageDetails, - deleteJobApplicationSkillDetails, - deleteAttachmentDetails, - deleteAppliedRoleDetails, - deleteEvaluationDetails, - deleteApplicationDetails, - deleteSessionKeysDetails, - deleteProjectUserDetails, - deleteContractDetails, - deleteProjectRoleDetails, - deleteProjectDetails, - deleteOsocDetails, - deleteStudentDetails, - deleteLoginUserDetails, - deleteRoleDetails, - deletePersonDetails, - ]); + await prisma.job_application_skill.deleteMany(); + await prisma.language.deleteMany(); + await prisma.attachment.deleteMany(); + await prisma.applied_role.deleteMany(); + await prisma.evaluation.deleteMany(); + await prisma.job_application.deleteMany(); + await prisma.session_keys.deleteMany(); + await prisma.project_user.deleteMany(); + await prisma.contract.deleteMany(); + await prisma.project_role.deleteMany(); + await prisma.project.deleteMany(); + await prisma.osoc.deleteMany(); + await prisma.student.deleteMany(); + await prisma.login_user.deleteMany(); + await prisma.role.deleteMany(); + await prisma.person.deleteMany(); await prisma.$disconnect() }); \ No newline at end of file diff --git a/backend/tests/orm_integration/language.test.ts b/backend/tests/orm_integration/language.test.ts index cc2cdae0..dbd975ea 100644 --- a/backend/tests/orm_integration/language.test.ts +++ b/backend/tests/orm_integration/language.test.ts @@ -1,6 +1,6 @@ import {UpdateLanguage} from "../../orm_functions/orm_types"; import {createLanguage, getAllLanguages, getLanguage, - getLanguageByName, updateLanguage, deleteLanguage, deleteLanguageByName} from "../../orm_functions/language"; + getLanguageByName, updateLanguage} from "../../orm_functions/language"; const language1: UpdateLanguage = { languageId: 0, @@ -40,12 +40,14 @@ it('should update language based upon language id', async () => { expect(updated_language).toHaveProperty("name", language2.name); }); -it('should delete the language based upon language id', async () => { - const deleted_language = await deleteLanguage(language2.languageId); - expect(deleted_language).toHaveProperty("name", language2.name); -}); - -it('should delete the language based upon language name', async () => { - const deleted_language = await deleteLanguageByName("French"); - expect(deleted_language).toHaveProperty("name", "French"); -}); \ No newline at end of file +//TODO: you can only remove languages that are NOT used in jobApplicationSkill otherwise everything will go wrong :/ + +// it('should delete the language based upon language id', async () => { +// const deleted_language = await deleteLanguage(language2.languageId); +// expect(deleted_language).toHaveProperty("name", language2.name); +// }); +// +// it('should delete the language based upon language name', async () => { +// const deleted_language = await deleteLanguageByName("French"); +// expect(deleted_language).toHaveProperty("name", "French"); +// }); \ No newline at end of file From 039bf5913d267270fe47f981a480c12fc5832a89 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Wed, 30 Mar 2022 23:46:58 +0200 Subject: [PATCH 065/827] fix: small change to github action for automated testing --- .github/workflows/backendCI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backendCI.yml b/.github/workflows/backendCI.yml index dd02f570..f5a41ff2 100644 --- a/.github/workflows/backendCI.yml +++ b/.github/workflows/backendCI.yml @@ -46,7 +46,7 @@ jobs: # push the db scheme to the db container - name: Push schema to docker file - run: npx dotenv -e prisma/.env.test -- npx prisma migrate dev --name postgres-init + run: npx dotenv -e prisma/.env.test -- npx prisma db push working-directory: backend - name: Coverage Report From 86e5b76a54defde821a04aefbdef94a9fa7ffd6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 03:37:09 +0000 Subject: [PATCH 066/827] npm-frontend(deps-dev): bump eslint-config-next in /frontend Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.2 to 12.1.3. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.3/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 30 +++++++++++++++--------------- frontend/package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 44ccc71d..d402edee 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,7 +24,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.2", + "eslint-config-next": "12.1.3", "typedoc": "^0.22.12", "typescript": "4.6.3" } @@ -137,9 +137,9 @@ "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", - "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.3.tgz", + "integrity": "sha512-PYYqnovABbnbKfk1yYiN9Jl5AHGOLb47EcGbJSXJt2nryFW34icOLKkXKIU5jgz700h/31EI0JH+rpQ5b4h9jw==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -1125,12 +1125,12 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", - "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.3.tgz", + "integrity": "sha512-aLtr2VfdvkpbXnqH0Do9HJfMeDq0ZkKlZ8h8g2T88njbE8/5KfJIQSoNnp18M8PUXZYD8kqTOUa1MJ0QU9T1hQ==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.2", + "@next/eslint-plugin-next": "12.1.3", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", @@ -3718,9 +3718,9 @@ "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" }, "@next/eslint-plugin-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", - "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.3.tgz", + "integrity": "sha512-PYYqnovABbnbKfk1yYiN9Jl5AHGOLb47EcGbJSXJt2nryFW34icOLKkXKIU5jgz700h/31EI0JH+rpQ5b4h9jw==", "dev": true, "requires": { "glob": "7.1.7" @@ -4382,12 +4382,12 @@ } }, "eslint-config-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", - "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.3.tgz", + "integrity": "sha512-aLtr2VfdvkpbXnqH0Do9HJfMeDq0ZkKlZ8h8g2T88njbE8/5KfJIQSoNnp18M8PUXZYD8kqTOUa1MJ0QU9T1hQ==", "dev": true, "requires": { - "@next/eslint-plugin-next": "12.1.2", + "@next/eslint-plugin-next": "12.1.3", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", diff --git a/frontend/package.json b/frontend/package.json index fb3a3f9b..e27fcf5b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.2", + "eslint-config-next": "12.1.3", "typedoc": "^0.22.12", "typescript": "4.6.3" }, From be4d5635f865e1d027fc6eac3515fc1820a4d40d Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Thu, 31 Mar 2022 08:50:33 +0200 Subject: [PATCH 067/827] ci: merge branch development into auth-v2 --- frontend/package-lock.json | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 661fda21..d5ed40f8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -3006,19 +3006,6 @@ "react": "^16.8.0 || ^17.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -4624,16 +4611,6 @@ "postcss": "8.4.5", "styled-jsx": "5.0.1", "use-subscription": "1.5.1" - }, - "dependencies": { - "use-subscription": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", - "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", - "requires": { - "object-assign": "^4.1.1" - } - } } }, "object-assign": { @@ -5225,16 +5202,6 @@ "object-assign": "^4.1.1" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", From cac73e137b3f706eb43f59d587871527793d32fa Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 09:02:46 +0200 Subject: [PATCH 068/827] fix: fixes error in tests --- .../orm_integration/integration_setup.ts | 36 +++++++++---------- .../job_application_skill.test.ts | 14 ++++---- .../tests/orm_integration/language.test.ts | 22 ++++++------ 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 3d75efbe..304ea62e 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -207,23 +207,23 @@ beforeAll(async () => { }, ], }); - // - // // create attachments - // await prisma.attachment.createMany({ - // data : [ - // { - // job_application_id: job_applications[1].job_application_id, - // data: "test-cv-link.com", - // type: "CV_URL" - // }, - // { - // job_application_id: job_applications[1].job_application_id, - // data: "test-portfolio-link.com", - // type: "PORTFOLIO_URL" - // } - // ] - // }) - // + + // create attachments + await prisma.attachment.createMany({ + data : [ + { + job_application_id: job_applications[1].job_application_id, + data: "test-cv-link.com", + type: "CV_URL" + }, + { + job_application_id: job_applications[1].job_application_id, + data: "test-portfolio-link.com", + type: "PORTFOLIO_URL" + } + ] + }) + const languages = await prisma.language.findMany(); // create job application skills await prisma.job_application_skill.createMany({ @@ -240,7 +240,7 @@ beforeAll(async () => { { job_application_id: job_applications[0].job_application_id, skill: "Python", - language_id: languages[1].language_id, + language_id: languages[0].language_id, level: 4, is_preferred: false, is_best: false diff --git a/backend/tests/orm_integration/job_application_skill.test.ts b/backend/tests/orm_integration/job_application_skill.test.ts index 47271bb3..242d3a2c 100644 --- a/backend/tests/orm_integration/job_application_skill.test.ts +++ b/backend/tests/orm_integration/job_application_skill.test.ts @@ -67,16 +67,16 @@ it('should find all the job_applications skills in the db, 3 in total', async () it('should find all the job_applications skills linked to the job application', async () => { const searched_job_application_skills = await getAllJobApplicationSkillByJobApplication(jobApplicationSkill1.JobApplicationId); - expect(searched_job_application_skills[0]).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); - expect(searched_job_application_skills[0]).toHaveProperty("skill", jobApplicationSkill1.skill); - expect(searched_job_application_skills[0]).toHaveProperty("language_id", jobApplicationSkill1.languageId); - expect(searched_job_application_skills[0]).toHaveProperty("level", jobApplicationSkill1.level); - expect(searched_job_application_skills[0]).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); - expect(searched_job_application_skills[0]).toHaveProperty("is_best", jobApplicationSkill1.is_best); + expect(searched_job_application_skills[1]).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); + expect(searched_job_application_skills[1]).toHaveProperty("skill", jobApplicationSkill1.skill); + expect(searched_job_application_skills[1]).toHaveProperty("language_id", jobApplicationSkill1.languageId); + expect(searched_job_application_skills[1]).toHaveProperty("level", jobApplicationSkill1.level); + expect(searched_job_application_skills[1]).toHaveProperty("is_preferred", jobApplicationSkill1.isPreferred); + expect(searched_job_application_skills[1]).toHaveProperty("is_best", jobApplicationSkill1.is_best); }); it('should find the job_applications skill by its id', async () => { - const searched_job_application_skill = await getJobApplicationSkill(jobApplicationSkill1.JobApplicationId); + const searched_job_application_skill = await getJobApplicationSkill(jobApplicationSkill1.JobApplicationSkillId); expect(searched_job_application_skill).toHaveProperty("job_application_id", jobApplicationSkill1.JobApplicationId); expect(searched_job_application_skill).toHaveProperty("skill", jobApplicationSkill1.skill); expect(searched_job_application_skill).toHaveProperty("language_id", jobApplicationSkill1.languageId); diff --git a/backend/tests/orm_integration/language.test.ts b/backend/tests/orm_integration/language.test.ts index dbd975ea..c7659669 100644 --- a/backend/tests/orm_integration/language.test.ts +++ b/backend/tests/orm_integration/language.test.ts @@ -1,6 +1,6 @@ import {UpdateLanguage} from "../../orm_functions/orm_types"; import {createLanguage, getAllLanguages, getLanguage, - getLanguageByName, updateLanguage} from "../../orm_functions/language"; + getLanguageByName, updateLanguage, deleteLanguage, deleteLanguageByName} from "../../orm_functions/language"; const language1: UpdateLanguage = { languageId: 0, @@ -40,14 +40,12 @@ it('should update language based upon language id', async () => { expect(updated_language).toHaveProperty("name", language2.name); }); -//TODO: you can only remove languages that are NOT used in jobApplicationSkill otherwise everything will go wrong :/ - -// it('should delete the language based upon language id', async () => { -// const deleted_language = await deleteLanguage(language2.languageId); -// expect(deleted_language).toHaveProperty("name", language2.name); -// }); -// -// it('should delete the language based upon language name', async () => { -// const deleted_language = await deleteLanguageByName("French"); -// expect(deleted_language).toHaveProperty("name", "French"); -// }); \ No newline at end of file +it('should delete the language based upon language id', async () => { + const deleted_language = await deleteLanguage(language2.languageId); + expect(deleted_language).toHaveProperty("name", language2.name); +}); + +it('should delete the language based upon language name', async () => { + const deleted_language = await deleteLanguageByName("French"); + expect(deleted_language).toHaveProperty("name", "French"); +}); From fa0735a5f97977a637802b76a6ee6e088cc89568 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 09:14:30 +0200 Subject: [PATCH 069/827] fix: fixed delete order --- .../orm_integration/integration_setup.ts | 88 ++++++++----------- 1 file changed, 35 insertions(+), 53 deletions(-) diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 304ea62e..82f7a51a 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -251,60 +251,42 @@ beforeAll(async () => { }); afterAll(async () => { - // TODO: probeer deze gecommente code te hergebruiken ipv alles in losse awaits te doen, maar los werkt voorlopig - // const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); - // const deleteLanguageDetails = prisma.language.deleteMany(); - // const deleteAttachmentDetails = prisma.attachment.deleteMany(); - // const deleteAppliedRoleDetails = prisma.applied_role.deleteMany(); - // const deleteEvaluationDetails = prisma.evaluation.deleteMany(); - // const deleteApplicationDetails = prisma.job_application.deleteMany(); - // const deleteSessionKeysDetails = prisma.session_keys.deleteMany(); - // const deleteProjectUserDetails = prisma.project_user.deleteMany(); - // const deleteContractDetails = prisma.contract.deleteMany(); - // const deleteProjectRoleDetails = prisma.project_role.deleteMany(); - // const deleteProjectDetails = prisma.project.deleteMany(); - // const deleteOsocDetails = prisma.osoc.deleteMany(); - // const deleteStudentDetails = prisma.student.deleteMany(); - // const deleteLoginUserDetails = prisma.login_user.deleteMany(); - // const deleteRoleDetails = prisma.role.deleteMany(); - // const deletePersonDetails = prisma.person.deleteMany(); - // - // await prisma.$transaction([ - // deleteLanguageDetails, - // deleteJobApplicationSkillDetails, - // deleteAttachmentDetails, - // deleteAppliedRoleDetails, - // deleteEvaluationDetails, - // deleteApplicationDetails, - // deleteSessionKeysDetails, - // deleteProjectUserDetails, - // deleteContractDetails, - // deleteProjectRoleDetails, - // deleteProjectDetails, - // deleteOsocDetails, - // deleteStudentDetails, - // deleteLoginUserDetails, - // deleteRoleDetails, - // deletePersonDetails, - // ]); - - await prisma.job_application_skill.deleteMany(); - await prisma.language.deleteMany(); - await prisma.attachment.deleteMany(); - await prisma.applied_role.deleteMany(); - await prisma.evaluation.deleteMany(); - await prisma.job_application.deleteMany(); - await prisma.session_keys.deleteMany(); - await prisma.project_user.deleteMany(); - await prisma.contract.deleteMany(); - await prisma.project_role.deleteMany(); - await prisma.project.deleteMany(); - await prisma.osoc.deleteMany(); - await prisma.student.deleteMany(); - await prisma.login_user.deleteMany(); - await prisma.role.deleteMany(); - await prisma.person.deleteMany(); + const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); + const deleteLanguageDetails = prisma.language.deleteMany(); + const deleteAttachmentDetails = prisma.attachment.deleteMany(); + const deleteAppliedRoleDetails = prisma.applied_role.deleteMany(); + const deleteEvaluationDetails = prisma.evaluation.deleteMany(); + const deleteApplicationDetails = prisma.job_application.deleteMany(); + const deleteSessionKeysDetails = prisma.session_keys.deleteMany(); + const deleteProjectUserDetails = prisma.project_user.deleteMany(); + const deleteContractDetails = prisma.contract.deleteMany(); + const deleteProjectRoleDetails = prisma.project_role.deleteMany(); + const deleteProjectDetails = prisma.project.deleteMany(); + const deleteOsocDetails = prisma.osoc.deleteMany(); + const deleteStudentDetails = prisma.student.deleteMany(); + const deleteLoginUserDetails = prisma.login_user.deleteMany(); + const deleteRoleDetails = prisma.role.deleteMany(); + const deletePersonDetails = prisma.person.deleteMany(); + + await prisma.$transaction([ + deleteJobApplicationSkillDetails, + deleteLanguageDetails, + deleteAttachmentDetails, + deleteAppliedRoleDetails, + deleteEvaluationDetails, + deleteApplicationDetails, + deleteSessionKeysDetails, + deleteProjectUserDetails, + deleteContractDetails, + deleteProjectRoleDetails, + deleteProjectDetails, + deleteRoleDetails, + deleteOsocDetails, + deleteStudentDetails, + deleteLoginUserDetails, + deletePersonDetails, + ]); await prisma.$disconnect() }); \ No newline at end of file From aaf417a53b8904d0567460fca9a578156c5458cb Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 09:54:59 +0200 Subject: [PATCH 070/827] test: created int test for evalutation --- .../tests/orm_integration/evaluation.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 backend/tests/orm_integration/evaluation.test.ts diff --git a/backend/tests/orm_integration/evaluation.test.ts b/backend/tests/orm_integration/evaluation.test.ts new file mode 100644 index 00000000..58858445 --- /dev/null +++ b/backend/tests/orm_integration/evaluation.test.ts @@ -0,0 +1,66 @@ +import {CreateEvaluationForStudent, UpdateEvaluationForStudent} from "../../orm_functions/orm_types"; +import {createEvaluationForStudent, checkIfFinalEvaluationExists, + updateEvaluationForStudent, getLoginUserByEvaluationId} from "../../orm_functions/evaluation"; +import prisma from "../../prisma/prisma"; +import {decision_enum} from "@prisma/client"; + +const evaluation1: UpdateEvaluationForStudent = { + evaluation_id: 1, + loginUserId: 1, + decision: decision_enum.YES, + motivation: "Definetly unicorn, all in for it" +} + + +let jobApplicationId = 0; +let createdEvaluation : CreateEvaluationForStudent; + + +it('should create 1 new evaluation', async () => { + const login_user = await prisma.login_user.findFirst(); + const job_application_id = await prisma.job_application.findFirst(); + + if (login_user && job_application_id){ + const evaluation: CreateEvaluationForStudent = { + loginUserId: login_user.login_user_id, + jobApplicationId: job_application_id.job_application_id, + decision: decision_enum.MAYBE, + motivation: "Looks good", + isFinal: true + } + createdEvaluation = evaluation; + evaluation1.loginUserId = evaluation.loginUserId; + jobApplicationId = evaluation.jobApplicationId; + + const created_evaluation = await createEvaluationForStudent(evaluation); + evaluation1.evaluation_id = created_evaluation.evaluation_id; + expect(created_evaluation).toHaveProperty("login_user_id", created_evaluation.login_user_id); + expect(created_evaluation).toHaveProperty("job_application_id", created_evaluation.job_application_id); + expect(created_evaluation).toHaveProperty("decision", created_evaluation.decision); + expect(created_evaluation).toHaveProperty("motivation", created_evaluation.motivation); + expect(created_evaluation).toHaveProperty("is_final", created_evaluation.is_final); + } +}); + +it('should return the evaluation, by searching for the job application id', async () => { + const searched_evaluation = await checkIfFinalEvaluationExists(jobApplicationId); + expect(searched_evaluation).toHaveProperty("evaluation_id", evaluation1.evaluation_id); +}); + +it('should return the evaluation, by searching for the evaluation id', async () => { + const searched_evaluation = await getLoginUserByEvaluationId(evaluation1.evaluation_id); + expect(searched_evaluation).toHaveProperty("login_user_id", createdEvaluation.loginUserId); + expect(searched_evaluation).toHaveProperty("job_application_id", jobApplicationId); + expect(searched_evaluation).toHaveProperty("decision", createdEvaluation.decision); + expect(searched_evaluation).toHaveProperty("motivation", createdEvaluation.motivation); + expect(searched_evaluation).toHaveProperty("is_final", createdEvaluation.isFinal); +}); + +it('should update evaluation based upon evaluation id', async () => { + const updated_evaluation = await updateEvaluationForStudent(evaluation1); + expect(updated_evaluation).toHaveProperty("login_user_id", evaluation1.loginUserId); + expect(updated_evaluation).toHaveProperty("job_application_id", jobApplicationId); + expect(updated_evaluation).toHaveProperty("decision", evaluation1.decision); + expect(updated_evaluation).toHaveProperty("motivation", evaluation1.motivation); + expect(updated_evaluation).toHaveProperty("is_final", createdEvaluation.isFinal); +}); From eaee0056cc7986123f167f32633e0ab9f9f688a5 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 10:12:45 +0200 Subject: [PATCH 071/827] fix: fixed usage of ! --- backend/orm_functions/orm_types.ts | 2 +- .../tests/orm_integration/login_user.test.ts | 50 +++++++++-------- backend/tests/orm_integration/osoc.test.ts | 41 ++++++++------ backend/tests/orm_integration/project.test.ts | 56 ++++++++++--------- .../orm_integration/project_role.test.ts | 33 ++++++----- backend/tests/orm_integration/role.test.ts | 4 +- 6 files changed, 103 insertions(+), 83 deletions(-) diff --git a/backend/orm_functions/orm_types.ts b/backend/orm_functions/orm_types.ts index ea53d545..047041f6 100644 --- a/backend/orm_functions/orm_types.ts +++ b/backend/orm_functions/orm_types.ts @@ -57,7 +57,7 @@ import { contract_status_enum, decision_enum, email_status_enum, account_status_ /** * the person_id of the person the login user will be associated with */ - personId?: number, + personId: number, /** * the password hash of the login user if email is used */ diff --git a/backend/tests/orm_integration/login_user.test.ts b/backend/tests/orm_integration/login_user.test.ts index 507afec6..ce1300cc 100644 --- a/backend/tests/orm_integration/login_user.test.ts +++ b/backend/tests/orm_integration/login_user.test.ts @@ -29,14 +29,16 @@ it('should create 1 new login user', async () => { } const created_person = await createPerson(person0); - login_user.personId = created_person.person_id; + if (created_person){ + login_user.personId = created_person.person_id; - const created_login_user = await createLoginUser(login_user); - login_user_update.loginUserId = created_login_user.login_user_id; - expect(created_login_user).toHaveProperty("password", login_user.password); - expect(created_login_user).toHaveProperty("is_admin", login_user.isAdmin); - expect(created_login_user).toHaveProperty("is_coach", login_user.isCoach); - expect(created_login_user).toHaveProperty("account_status", login_user.accountStatus); + const created_login_user = await createLoginUser(login_user); + login_user_update.loginUserId = created_login_user.login_user_id; + expect(created_login_user).toHaveProperty("password", login_user.password); + expect(created_login_user).toHaveProperty("is_admin", login_user.isAdmin); + expect(created_login_user).toHaveProperty("is_coach", login_user.isCoach); + expect(created_login_user).toHaveProperty("account_status", login_user.accountStatus); + } }); it('should find all the login users in the db, 3 in total', async () => { @@ -49,7 +51,7 @@ it('should find all the login users in the db, 3 in total', async () => { }); it('should return the password, by searching for its person id', async () => { - const searched_password = await getPasswordLoginUserByPerson(login_user.personId!); + const searched_password = await getPasswordLoginUserByPerson(login_user.personId); expect(searched_password).toHaveProperty("password", login_user.password); }); @@ -59,7 +61,7 @@ it('should return the password, by searching for its login user id', async () => }); it('should return the login user, by searching for its person id', async () => { - const searched_login_user = await searchLoginUserByPerson(login_user.personId!); + const searched_login_user = await searchLoginUserByPerson(login_user.personId); expect(searched_login_user).toHaveProperty("password", login_user.password); expect(searched_login_user).toHaveProperty("is_admin", login_user.isAdmin); expect(searched_login_user).toHaveProperty("is_coach", login_user.isCoach); @@ -94,20 +96,22 @@ it('should find all the login users in the db that are admin or coach, 3 in tota }); it('should update login user based upon login user id', async () => { - const searched_login_user = await searchLoginUserByPerson(login_user.personId!); - const loginUserUpdate: UpdateLoginUser = { - loginUserId: searched_login_user!.login_user_id, - password: "last_pass", - isAdmin: false, - isCoach: false, - accountStatus: "DISABLED" + const searched_login_user = await searchLoginUserByPerson(login_user.personId); + if (searched_login_user){ + const loginUserUpdate: UpdateLoginUser = { + loginUserId: searched_login_user.login_user_id, + password: "last_pass", + isAdmin: false, + isCoach: false, + accountStatus: "DISABLED" + } + login_user_update = loginUserUpdate; + const updated_login_user = await updateLoginUser(loginUserUpdate); + expect(updated_login_user).toHaveProperty("password", loginUserUpdate.password); + expect(updated_login_user).toHaveProperty("is_admin", loginUserUpdate.isAdmin); + expect(updated_login_user).toHaveProperty("is_coach", loginUserUpdate.isCoach); + expect(updated_login_user).toHaveProperty("account_status", loginUserUpdate.accountStatus); } - login_user_update = loginUserUpdate; - const updated_login_user = await updateLoginUser(loginUserUpdate); - expect(updated_login_user).toHaveProperty("password", loginUserUpdate.password); - expect(updated_login_user).toHaveProperty("is_admin", loginUserUpdate.isAdmin); - expect(updated_login_user).toHaveProperty("is_coach", loginUserUpdate.isCoach); - expect(updated_login_user).toHaveProperty("account_status", loginUserUpdate.accountStatus); }); it('should delete the login user based upon login user id', async () => { @@ -120,7 +124,7 @@ it('should delete the login user based upon login user id', async () => { }); it('should delete the login user based upon person id', async () => { - const deleted_login_user = await deleteLoginUserByPersonId(login_user.personId!); + const deleted_login_user = await deleteLoginUserByPersonId(login_user.personId); expect(deleted_login_user).toHaveProperty("password", login_user_update.password); expect(deleted_login_user).toHaveProperty("is_admin", login_user_update.isAdmin); expect(deleted_login_user).toHaveProperty("is_coach", login_user_update.isCoach); diff --git a/backend/tests/orm_integration/osoc.test.ts b/backend/tests/orm_integration/osoc.test.ts index 729b3225..65133c2d 100644 --- a/backend/tests/orm_integration/osoc.test.ts +++ b/backend/tests/orm_integration/osoc.test.ts @@ -51,37 +51,44 @@ it("should return all osoc editions after 2022", async () => { }); it("should update the selected osoc", async () => { - const osoc = await getOsocByYear(2024); + const osoc = await getOsocByYear(2024); - const updatedOsoc = await updateOsoc({osocId: osoc!.osoc_id, year: 2025}); - expect(updatedOsoc).toHaveProperty("year", 2025); + if (osoc){ + const updatedOsoc = await updateOsoc({osocId: osoc.osoc_id, year: 2025}); + expect(updatedOsoc).toHaveProperty("year", 2025); - const osocUpdatedFound = await prisma.osoc.findUnique({ - where: { - osoc_id: osoc!.osoc_id - } - }); - expect(osocUpdatedFound).toHaveProperty("year", 2025); + const osocUpdatedFound = await prisma.osoc.findUnique({ + where: { + osoc_id: osoc.osoc_id + } + }); + expect(osocUpdatedFound).toHaveProperty("year", 2025); + } }); it("should delete the osoc", async () => { const osoc = await getOsocByYear(2025); - const removedOsoc = await deleteOsoc(osoc!.osoc_id); - expect(removedOsoc).toHaveProperty("year", osoc!.year); - expect(removedOsoc).toHaveProperty("osoc_id", osoc!.osoc_id); + if (osoc){ + const removedOsoc = await deleteOsoc(osoc.osoc_id); + expect(removedOsoc).toHaveProperty("year", osoc.year); + expect(removedOsoc).toHaveProperty("osoc_id", osoc.osoc_id); - const searchDeleted = await getOsocByYear(osoc!.year); - expect(searchDeleted).toBeNull(); + const searchDeleted = await getOsocByYear(osoc.year); + expect(searchDeleted).toBeNull(); + } }); it("should delete the osoc by year", async () => { const year = 2024; const osoc = await createOsoc(year); - const removedOsoc = await deleteOsocByYear(year); - expect(removedOsoc).toHaveProperty("osoc_id", osoc!.osoc_id); - expect(removedOsoc).toHaveProperty("year", osoc!.year); + + if (osoc){ + const removedOsoc = await deleteOsocByYear(year); + expect(removedOsoc).toHaveProperty("osoc_id", osoc.osoc_id); + expect(removedOsoc).toHaveProperty("year", osoc.year); const searchDeleted = await getOsocByYear(year); expect(searchDeleted).toBeNull(); + } }); \ No newline at end of file diff --git a/backend/tests/orm_integration/project.test.ts b/backend/tests/orm_integration/project.test.ts index 47dab36d..8478d422 100644 --- a/backend/tests/orm_integration/project.test.ts +++ b/backend/tests/orm_integration/project.test.ts @@ -26,23 +26,25 @@ const project2: UpdateProject = { it('should create 1 new project where osoc is 2022', async () => { const osoc = await getOsocByYear(2022); - project1.osocId = osoc!.osoc_id; - const project0: CreateProject = { - name: "test-project", - osocId: osoc!.osoc_id, - partner: "test-partner", - startDate: new Date("2022-07-13"), - endDate: new Date("2022-07-15"), - positions: 7 + if (osoc){ + project1.osocId = osoc.osoc_id; + const project0: CreateProject = { + name: "test-project", + osocId: osoc.osoc_id, + partner: "test-partner", + startDate: new Date("2022-07-13"), + endDate: new Date("2022-07-15"), + positions: 7 + } + + const created_project = await createProject(project0); + expect(created_project).toHaveProperty("name", project0.name); + expect(created_project).toHaveProperty("osoc_id", project0.osocId); + expect(created_project).toHaveProperty("partner", project0.partner); + expect(created_project).toHaveProperty("start_date", project0.startDate); + expect(created_project).toHaveProperty("end_date", project0.endDate); + expect(created_project).toHaveProperty("positions", project0.positions); } - - const created_project = await createProject(project0); - expect(created_project).toHaveProperty("name", project0.name); - expect(created_project).toHaveProperty("osoc_id", project0.osocId); - expect(created_project).toHaveProperty("partner", project0.partner); - expect(created_project).toHaveProperty("start_date", project0.startDate); - expect(created_project).toHaveProperty("end_date", project0.endDate); - expect(created_project).toHaveProperty("positions", project0.positions); }); it('should find all the projects in the db, 3 in total', async () => { @@ -68,13 +70,15 @@ it('should return the project, by searching for its name', async () => { it('should return the project, by searching for its osoc edition', async () => { const osoc = await getOsocByYear(2022); - const searched_project = await getProjectsByOsocEdition(osoc!.osoc_id); - expect(searched_project[1]).toHaveProperty("name", project1.name); - expect(searched_project[1]).toHaveProperty("osoc_id", project1.osocId); - expect(searched_project[1]).toHaveProperty("partner", project1.partner); - expect(searched_project[1]).toHaveProperty("start_date", project1.startDate); - expect(searched_project[1]).toHaveProperty("end_date", project1.endDate); - expect(searched_project[1]).toHaveProperty("positions", project1.positions); + if (osoc){ + const searched_project = await getProjectsByOsocEdition(osoc.osoc_id); + expect(searched_project[1]).toHaveProperty("name", project1.name); + expect(searched_project[1]).toHaveProperty("osoc_id", project1.osocId); + expect(searched_project[1]).toHaveProperty("partner", project1.partner); + expect(searched_project[1]).toHaveProperty("start_date", project1.startDate); + expect(searched_project[1]).toHaveProperty("end_date", project1.endDate); + expect(searched_project[1]).toHaveProperty("positions", project1.positions); + } }); it('should return the projects, by searching for its partner name', async () => { @@ -207,6 +211,8 @@ it('should delete the project based upon project partner', async () => { it('should delete the project based upon osoc id', async () => { const osoc = await getOsocByYear(2023); - const deleted_project = await deleteProjectByOsocEdition(osoc!.osoc_id); - expect(deleted_project).toHaveProperty("count", 1); + if (osoc){ + const deleted_project = await deleteProjectByOsocEdition(osoc.osoc_id); + expect(deleted_project).toHaveProperty("count", 1); + } }); diff --git a/backend/tests/orm_integration/project_role.test.ts b/backend/tests/orm_integration/project_role.test.ts index 3f9affd3..e36f48ca 100644 --- a/backend/tests/orm_integration/project_role.test.ts +++ b/backend/tests/orm_integration/project_role.test.ts @@ -21,22 +21,25 @@ const projectRole2: UpdateProjectRole = { it('should create 1 new project role with role developer', async () => { const projects = await getAllProjects(); const role = await getRolesByName("Developer"); - const projectRole: CreateProjectRole = { - projectId: projects[0].project_id, - roleId: role!.role_id, - positions: 2 - } - projectRole1.projectId = projects[0].project_id; - projectRole1.roleId = role!.role_id; - projectRole2.projectId = projects[0].project_id; - projectRole2.roleId = role!.role_id; - const created_project_role = await createProjectRole(projectRole); - projectRole1.projectRoleId = created_project_role.project_role_id; - projectRole2.projectRoleId = created_project_role.project_role_id; - expect(created_project_role).toHaveProperty("project_id", projectRole1.projectId); - expect(created_project_role).toHaveProperty("role_id", projectRole1.roleId); - expect(created_project_role).toHaveProperty("positions", projectRole1.positions); + if (role){ + const projectRole: CreateProjectRole = { + projectId: projects[0].project_id, + roleId: role.role_id, + positions: 2 + } + projectRole1.projectId = projects[0].project_id; + projectRole1.roleId = role.role_id; + projectRole2.projectId = projects[0].project_id; + projectRole2.roleId = role.role_id; + + const created_project_role = await createProjectRole(projectRole); + projectRole1.projectRoleId = created_project_role.project_role_id; + projectRole2.projectRoleId = created_project_role.project_role_id; + expect(created_project_role).toHaveProperty("project_id", projectRole1.projectId); + expect(created_project_role).toHaveProperty("role_id", projectRole1.roleId); + expect(created_project_role).toHaveProperty("positions", projectRole1.positions); + } }); it('should return the project role, by searching for its project', async () => { diff --git a/backend/tests/orm_integration/role.test.ts b/backend/tests/orm_integration/role.test.ts index 241c848a..0c3ddd83 100644 --- a/backend/tests/orm_integration/role.test.ts +++ b/backend/tests/orm_integration/role.test.ts @@ -40,8 +40,8 @@ it('should return the role, by searching for its role name', async () => { }); it('should return the project role, by searching for its role name and project id', async () => { - const project = await prisma_project.project.findFirst() - const searched_role = await getProjectRoleWithRoleName("Developer", project!.project_id); + const project = await prisma_project.project.findMany(); + const searched_role = await getProjectRoleWithRoleName("Developer", project[0].project_id); expect(searched_role).toHaveProperty("positions", 3); }); From f2e8633ce23ecf5e1bc41b1ba4e35f9422217f65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 08:16:20 +0000 Subject: [PATCH 072/827] npm-root(deps-dev): bump eslint-config-next from 12.1.2 to 12.1.4 Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.2 to 12.1.4. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.4/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3389c5c5..e4898740 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", - "eslint-config-next": "^12.1.2", + "eslint-config-next": "^12.1.4", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", @@ -454,9 +454,9 @@ "peer": true }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", - "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.4.tgz", + "integrity": "sha512-BRy565KVK6Cdy8LHaHTiwctLqBu/RT84RLpESug70BDJzBlV8QBvODyx/j7wGhvYqp9kvstM05lyb6JaTkSCcQ==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -1795,12 +1795,12 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", - "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.4.tgz", + "integrity": "sha512-Uj0jrVjoQbg9qerxRjSHoOOv3PEzoZxpb8G9LYct25fsflP8xIiUq0l4WEu2KSB5owuLv5hie7wSMqPEsHj+bQ==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.2", + "@next/eslint-plugin-next": "12.1.4", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", @@ -5699,9 +5699,9 @@ "peer": true }, "@next/eslint-plugin-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.2.tgz", - "integrity": "sha512-XqYRh6d98dpv2ynoOEC3VeNv99hxRGBuanRDKASfntdAZD9Zp4n+AugmNF0qwOQEHYgG1uvZW3A4Fi6Y/+kCQw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.4.tgz", + "integrity": "sha512-BRy565KVK6Cdy8LHaHTiwctLqBu/RT84RLpESug70BDJzBlV8QBvODyx/j7wGhvYqp9kvstM05lyb6JaTkSCcQ==", "dev": true, "requires": { "glob": "7.1.7" @@ -6633,12 +6633,12 @@ } }, "eslint-config-next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.2.tgz", - "integrity": "sha512-nJgSIB5lXoOsS2yY90i5L5ucI1uko+ykhcQkGkydXqs5GdPprY8Y1NKWW3N0OEzQvFyBcf7GHb/ohB21HoeEsg==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.4.tgz", + "integrity": "sha512-Uj0jrVjoQbg9qerxRjSHoOOv3PEzoZxpb8G9LYct25fsflP8xIiUq0l4WEu2KSB5owuLv5hie7wSMqPEsHj+bQ==", "dev": true, "requires": { - "@next/eslint-plugin-next": "12.1.2", + "@next/eslint-plugin-next": "12.1.4", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", diff --git a/package.json b/package.json index 5553b241..2fdf3537 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "eslint": "^8.12.0", - "eslint-config-next": "^12.1.2", + "eslint-config-next": "^12.1.4", "eslint-plugin-jest": "^26.1.3", "husky": "^7.0.4", "lint-staged": "^12.3.7", From c553ab3b40522b171b3124c2b22f704c99d2cce2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 08:16:22 +0000 Subject: [PATCH 073/827] npm-frontend(deps): bump next from 12.1.2 to 12.1.4 in /frontend Bumps [next](https://github.com/vercel/next.js) from 12.1.2 to 12.1.4. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v12.1.2...v12.1.4) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 251 +++++++++++++++++-------------------- frontend/package.json | 2 +- 2 files changed, 115 insertions(+), 138 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d402edee..a8c71fbc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.2", + "next": "12.1.4", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", @@ -132,9 +132,9 @@ } }, "node_modules/@next/env": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", - "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.4.tgz", + "integrity": "sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg==" }, "node_modules/@next/eslint-plugin-next": { "version": "12.1.3", @@ -146,9 +146,9 @@ } }, "node_modules/@next/swc-android-arm-eabi": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz", - "integrity": "sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.4.tgz", + "integrity": "sha512-FJg/6a3s2YrUaqZ+/DJZzeZqfxbbWrynQMT1C5wlIEq9aDLXCFpPM/PiOyJh0ahxc0XPmi6uo38Poq+GJTuKWw==", "cpu": [ "arm" ], @@ -161,9 +161,9 @@ } }, "node_modules/@next/swc-android-arm64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz", - "integrity": "sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz", + "integrity": "sha512-LXraazvQQFBgxIg3Htny6G5V5he9EK7oS4jWtMdTGIikmD/OGByOv8ZjLuVLZLtVm3UIvaAiGtlQSLecxJoJDw==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz", - "integrity": "sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz", + "integrity": "sha512-SSST/dBymecllZxcqTCcSTCu5o1NKk9I+xcvhn/O9nH6GWjgvGgGkNqLbCarCa0jJ1ukvlBA138FagyrmZ/4rQ==", "cpu": [ "arm64" ], @@ -191,9 +191,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz", - "integrity": "sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz", + "integrity": "sha512-p1lwdX0TVjaoDXQVuAkjtxVBbCL/urgxiMCBwuPDO7TikpXtSRivi+mIzBj5q7ypgICFmIAOW3TyupXeoPRAnA==", "cpu": [ "x64" ], @@ -206,9 +206,9 @@ } }, "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz", - "integrity": "sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz", + "integrity": "sha512-67PZlgkCn3TDxacdVft0xqDCL7Io1/C4xbAs0+oSQ0xzp6OzN2RNpuKjHJrJgKd0DsE1XZ9sCP27Qv0591yfyg==", "cpu": [ "arm" ], @@ -221,9 +221,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz", - "integrity": "sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz", + "integrity": "sha512-OnOWixhhw7aU22TQdQLYrgpgFq0oA1wGgnjAiHJ+St7MLj82KTDyM9UcymAMbGYy6nG/TFOOHdTmRMtCRNOw0g==", "cpu": [ "arm64" ], @@ -236,9 +236,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz", - "integrity": "sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz", + "integrity": "sha512-UoRMzPZnsAavdWtVylYxH8DNC7Uy0i6RrvNwT4PyQVdfANBn2omsUkcH5lgS2O7oaz0nAYLk1vqyZDO7+tJotA==", "cpu": [ "arm64" ], @@ -251,9 +251,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz", - "integrity": "sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz", + "integrity": "sha512-nM+MA/frxlTLUKLJKorctdI20/ugfHRjVEEkcLp/58LGG7slNaP1E5d5dRA1yX6ISjPcQAkywas5VlGCg+uTvA==", "cpu": [ "x64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz", - "integrity": "sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz", + "integrity": "sha512-GoRHxkuW4u4yKw734B9SzxJwVdyEJosaZ62P7ifOwcujTxhgBt3y76V2nNUrsSuopcKI2ZTDjaa+2wd5zyeXbA==", "cpu": [ "x64" ], @@ -281,9 +281,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz", - "integrity": "sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz", + "integrity": "sha512-6TQkQze0ievXwHJcVUrIULwCYVe3ccX6T0JgZ1SiMeXpHxISN7VJF/O8uSCw1JvXZYZ6ud0CJ7nfC5HXivgfPg==", "cpu": [ "arm64" ], @@ -296,9 +296,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz", - "integrity": "sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz", + "integrity": "sha512-CsbX/IXuZ5VSmWCpSetG2HD6VO5FTsO39WNp2IR2Ut/uom9XtLDJAZqjQEnbUTLGHuwDKFjrIO3LkhtROXLE/g==", "cpu": [ "ia32" ], @@ -311,9 +311,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz", - "integrity": "sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz", + "integrity": "sha512-JtYuWzKXKLDMgE/xTcFtCm1MiCIRaAc5XYZfYX3n/ZWSI1SJS/GMm+Su0SAHJgRFavJh6U/p998YwO/iGTIgqQ==", "cpu": [ "x64" ], @@ -2397,15 +2397,14 @@ "dev": true }, "node_modules/next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.2.tgz", - "integrity": "sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.4.tgz", + "integrity": "sha512-DA4g97BM4Z0nKtDvCTm58RxdvoQyYzeg0AeVbh0N4Y/D8ELrNu47lQeEgRGF8hV4eQ+Sal90zxrJQQG/mPQ8CQ==", "dependencies": { - "@next/env": "12.1.2", + "@next/env": "12.1.4", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", - "styled-jsx": "5.0.1", - "use-subscription": "1.5.1" + "styled-jsx": "5.0.1" }, "bin": { "next": "dist/bin/next" @@ -2414,18 +2413,18 @@ "node": ">=12.22.0" }, "optionalDependencies": { - "@next/swc-android-arm-eabi": "12.1.2", - "@next/swc-android-arm64": "12.1.2", - "@next/swc-darwin-arm64": "12.1.2", - "@next/swc-darwin-x64": "12.1.2", - "@next/swc-linux-arm-gnueabihf": "12.1.2", - "@next/swc-linux-arm64-gnu": "12.1.2", - "@next/swc-linux-arm64-musl": "12.1.2", - "@next/swc-linux-x64-gnu": "12.1.2", - "@next/swc-linux-x64-musl": "12.1.2", - "@next/swc-win32-arm64-msvc": "12.1.2", - "@next/swc-win32-ia32-msvc": "12.1.2", - "@next/swc-win32-x64-msvc": "12.1.2" + "@next/swc-android-arm-eabi": "12.1.4", + "@next/swc-android-arm64": "12.1.4", + "@next/swc-darwin-arm64": "12.1.4", + "@next/swc-darwin-x64": "12.1.4", + "@next/swc-linux-arm-gnueabihf": "12.1.4", + "@next/swc-linux-arm64-gnu": "12.1.4", + "@next/swc-linux-arm64-musl": "12.1.4", + "@next/swc-linux-x64-gnu": "12.1.4", + "@next/swc-linux-x64-musl": "12.1.4", + "@next/swc-win32-arm64-msvc": "12.1.4", + "@next/swc-win32-ia32-msvc": "12.1.4", + "@next/swc-win32-x64-msvc": "12.1.4" }, "peerDependencies": { "fibers": ">= 3.1.0", @@ -2483,17 +2482,6 @@ "isomorphic-fetch": "^2.2.1" } }, - "node_modules/next/node_modules/use-subscription": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", - "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", - "dependencies": { - "object-assign": "^4.1.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" - } - }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -3713,9 +3701,9 @@ } }, "@next/env": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.2.tgz", - "integrity": "sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==" + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.4.tgz", + "integrity": "sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg==" }, "@next/eslint-plugin-next": { "version": "12.1.3", @@ -3727,75 +3715,75 @@ } }, "@next/swc-android-arm-eabi": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz", - "integrity": "sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.4.tgz", + "integrity": "sha512-FJg/6a3s2YrUaqZ+/DJZzeZqfxbbWrynQMT1C5wlIEq9aDLXCFpPM/PiOyJh0ahxc0XPmi6uo38Poq+GJTuKWw==", "optional": true }, "@next/swc-android-arm64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz", - "integrity": "sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz", + "integrity": "sha512-LXraazvQQFBgxIg3Htny6G5V5he9EK7oS4jWtMdTGIikmD/OGByOv8ZjLuVLZLtVm3UIvaAiGtlQSLecxJoJDw==", "optional": true }, "@next/swc-darwin-arm64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz", - "integrity": "sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz", + "integrity": "sha512-SSST/dBymecllZxcqTCcSTCu5o1NKk9I+xcvhn/O9nH6GWjgvGgGkNqLbCarCa0jJ1ukvlBA138FagyrmZ/4rQ==", "optional": true }, "@next/swc-darwin-x64": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz", - "integrity": "sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz", + "integrity": "sha512-p1lwdX0TVjaoDXQVuAkjtxVBbCL/urgxiMCBwuPDO7TikpXtSRivi+mIzBj5q7ypgICFmIAOW3TyupXeoPRAnA==", "optional": true }, "@next/swc-linux-arm-gnueabihf": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz", - "integrity": "sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz", + "integrity": "sha512-67PZlgkCn3TDxacdVft0xqDCL7Io1/C4xbAs0+oSQ0xzp6OzN2RNpuKjHJrJgKd0DsE1XZ9sCP27Qv0591yfyg==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz", - "integrity": "sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz", + "integrity": "sha512-OnOWixhhw7aU22TQdQLYrgpgFq0oA1wGgnjAiHJ+St7MLj82KTDyM9UcymAMbGYy6nG/TFOOHdTmRMtCRNOw0g==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz", - "integrity": "sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz", + "integrity": "sha512-UoRMzPZnsAavdWtVylYxH8DNC7Uy0i6RrvNwT4PyQVdfANBn2omsUkcH5lgS2O7oaz0nAYLk1vqyZDO7+tJotA==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz", - "integrity": "sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz", + "integrity": "sha512-nM+MA/frxlTLUKLJKorctdI20/ugfHRjVEEkcLp/58LGG7slNaP1E5d5dRA1yX6ISjPcQAkywas5VlGCg+uTvA==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz", - "integrity": "sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz", + "integrity": "sha512-GoRHxkuW4u4yKw734B9SzxJwVdyEJosaZ62P7ifOwcujTxhgBt3y76V2nNUrsSuopcKI2ZTDjaa+2wd5zyeXbA==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz", - "integrity": "sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz", + "integrity": "sha512-6TQkQze0ievXwHJcVUrIULwCYVe3ccX6T0JgZ1SiMeXpHxISN7VJF/O8uSCw1JvXZYZ6ud0CJ7nfC5HXivgfPg==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz", - "integrity": "sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz", + "integrity": "sha512-CsbX/IXuZ5VSmWCpSetG2HD6VO5FTsO39WNp2IR2Ut/uom9XtLDJAZqjQEnbUTLGHuwDKFjrIO3LkhtROXLE/g==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz", - "integrity": "sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz", + "integrity": "sha512-JtYuWzKXKLDMgE/xTcFtCm1MiCIRaAc5XYZfYX3n/ZWSI1SJS/GMm+Su0SAHJgRFavJh6U/p998YwO/iGTIgqQ==", "optional": true }, "@nodelib/fs.scandir": { @@ -5345,37 +5333,26 @@ "dev": true }, "next": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/next/-/next-12.1.2.tgz", - "integrity": "sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ==", - "requires": { - "@next/env": "12.1.2", - "@next/swc-android-arm-eabi": "12.1.2", - "@next/swc-android-arm64": "12.1.2", - "@next/swc-darwin-arm64": "12.1.2", - "@next/swc-darwin-x64": "12.1.2", - "@next/swc-linux-arm-gnueabihf": "12.1.2", - "@next/swc-linux-arm64-gnu": "12.1.2", - "@next/swc-linux-arm64-musl": "12.1.2", - "@next/swc-linux-x64-gnu": "12.1.2", - "@next/swc-linux-x64-musl": "12.1.2", - "@next/swc-win32-arm64-msvc": "12.1.2", - "@next/swc-win32-ia32-msvc": "12.1.2", - "@next/swc-win32-x64-msvc": "12.1.2", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/next/-/next-12.1.4.tgz", + "integrity": "sha512-DA4g97BM4Z0nKtDvCTm58RxdvoQyYzeg0AeVbh0N4Y/D8ELrNu47lQeEgRGF8hV4eQ+Sal90zxrJQQG/mPQ8CQ==", + "requires": { + "@next/env": "12.1.4", + "@next/swc-android-arm-eabi": "12.1.4", + "@next/swc-android-arm64": "12.1.4", + "@next/swc-darwin-arm64": "12.1.4", + "@next/swc-darwin-x64": "12.1.4", + "@next/swc-linux-arm-gnueabihf": "12.1.4", + "@next/swc-linux-arm64-gnu": "12.1.4", + "@next/swc-linux-arm64-musl": "12.1.4", + "@next/swc-linux-x64-gnu": "12.1.4", + "@next/swc-linux-x64-musl": "12.1.4", + "@next/swc-win32-arm64-msvc": "12.1.4", + "@next/swc-win32-ia32-msvc": "12.1.4", + "@next/swc-win32-x64-msvc": "12.1.4", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", - "styled-jsx": "5.0.1", - "use-subscription": "1.5.1" - }, - "dependencies": { - "use-subscription": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", - "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", - "requires": { - "object-assign": "^4.1.1" - } - } + "styled-jsx": "5.0.1" } }, "next-auth": { diff --git a/frontend/package.json b/frontend/package.json index e27fcf5b..77e16a7b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "dependencies": { "bcrypt": "^5.0.1", "crypto-js": "^4.1.1", - "next": "12.1.2", + "next": "12.1.4", "next-auth": "^4.3.1", "next-auth-client": "^1.5.0", "nodemailer": "^6.7.3", From ab206a79d4ab739a6a044dcd731a2f341f66e7d3 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 10:46:36 +0200 Subject: [PATCH 074/827] feat: updated documentation (deployment guide) for the GH OAuth --- backend/.gitignore | 3 --- backend/github.json | 5 +++++ docs/deploymentGuide.md | 40 +++++++++++++++++++++-------------- docs/gh-oauth-get-values.png | Bin 0 -> 23423 bytes docs/gh-oauth-new-app.png | Bin 0 -> 61753 bytes docs/githubOath.png | Bin 33368 -> 0 bytes 6 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 backend/github.json create mode 100644 docs/gh-oauth-get-values.png create mode 100644 docs/gh-oauth-new-app.png delete mode 100644 docs/githubOath.png diff --git a/backend/.gitignore b/backend/.gitignore index 996b76f5..11ddd8db 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,6 +1,3 @@ node_modules # Keep environment variables out of version control .env - -# Hide the GitHub secret -github.json diff --git a/backend/github.json b/backend/github.json new file mode 100644 index 00000000..d16e7986 --- /dev/null +++ b/backend/github.json @@ -0,0 +1,5 @@ +{ + "client_id": "YOUR_CLIENT_ID", + "secret": "YOUR_CLIENT_SECRET", + "auth_callback_url": "YOUR_AUTH_CALLBACK" +} diff --git a/docs/deploymentGuide.md b/docs/deploymentGuide.md index c2229179..9d4388c0 100644 --- a/docs/deploymentGuide.md +++ b/docs/deploymentGuide.md @@ -48,24 +48,33 @@ openssl rand -base64 32 - The github id's and secrets are explained [later](#githubOAuth) in this document ### GitHub login -To make github login work it is needed that you make a github OAuth application. -You can create a new github OAuth app [here](https://github.com/settings/developers) +GitHub login relies on two configurations: + - One part is on GitHub's servers. + - The other part relies on a config file in the `/backend/` folder. -U can recreate these settings: -![img.png](githubOath.png) +#### GitHub part +To enable GitHub login, we need a GitHub application. These are made by going to [GitHub's Developer Applications page](https://github.com/settings/developers). From there, you can click the `New OAuth App` button to create a new application. -the `Application name` can freely be chosen. -in the `Homepage URL` field you should put the used domain name. -in the `Authorization callback URL`you should replace the first part with your domain name. **Leave the second part of the url untouched (`/api/auth/callback/github`)!** -
- -Finally, we only need to update the client id and client secret in the `/frontend/.env.production` file. -Here you should paste the client ID of the GitHub OAuth application you just made in `GITHUB_ID`. -In this same file you also need to replace the value of `GITHUB_SECRET`. -This secret can be generated in the github OAuth application by pressing `Generate a new client secret`. +![Default settings](./gh-oauth-new-app.png) -If you want to run the application locally make another OAuth application and use as homepageUrl `http://localhost:3000`. -Repeat the same steps once more to change the `GITHUB_ID` and `GITHUB_SECRET` but now in the `/frontend/.env.development`. +You can change the `Application name` to anything you'd like. Be sure to confirm that the `Homepage URL` is correct for your server. The `Application description` can be anything you like. The `Authorization callback URL` should be the `Homepage URL` with `/api-osoc/github/` behind it. + +#### Configuration part +In the `/backend/` folder, edit the `github.json` configuration file. There are three fields used, and most can be copied straight from the GitHub application you just created: +```json +{ + "client_id": "YOUR_CLIENT_ID", + "secret": "YOUR_CLIENT_SECRET", + "auth_callback_url": "YOUR_AUTH_CALLBACK" +} +``` + +You should replace the values with these (see the screenshot below): + - `YOUR_CLIENT_ID` should be the value given as `Client ID` (the first red box in the screenshot). + - `YOUR_CLIENT_SECRET` should be the value given below `Client secrets` (the second red box in the screenshot). You can copy this value by clicking the button right next to it. + - `YOUR_AUTH_CALLBACK` should be the value you filled in for `Authorization callback URL` (from the previous step). + +![How to get the values](./gh-oauth-get-values.png) ## How to deploy @@ -97,4 +106,3 @@ Listing all the images can be done with `docker images`, then you can delete the - `docker rmi "imageId"` Now you can deploy the new version as described in [Deployment](#deployment) - diff --git a/docs/gh-oauth-get-values.png b/docs/gh-oauth-get-values.png new file mode 100644 index 0000000000000000000000000000000000000000..4a19998d43ed5f207fe6b06c67f4fd71096287cc GIT binary patch literal 23423 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 literal 0 HcmV?d00001 diff --git a/docs/gh-oauth-new-app.png b/docs/gh-oauth-new-app.png new file mode 100644 index 0000000000000000000000000000000000000000..5927d444be78a71a163b7fe5036ced6d82f518e6 GIT binary patch literal 61753 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 T({$tnz?bqf^``|-OoRRhZ)z$y literal 0 HcmV?d00001 diff --git a/docs/githubOath.png b/docs/githubOath.png deleted file mode 100644 index b9df06f726041ab25c8f82a4b26df39702e8a598..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33368 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 xfe!fB2tqwv|7{EYjQ>% Date: Thu, 31 Mar 2022 10:49:20 +0200 Subject: [PATCH 075/827] fix: re-hide github.json secrets, while retaining document structure --- backend/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/.gitignore b/backend/.gitignore index 11ddd8db..fd033781 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,6 @@ node_modules # Keep environment variables out of version control .env + +# keep my actual GitHub secret hidden +backend/github.json From 89b1b6c0673798bf524d2018d238dc8ebf188866 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 10:50:34 +0200 Subject: [PATCH 076/827] fix: typo --- backend/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/.gitignore b/backend/.gitignore index fd033781..8837b5d1 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -3,4 +3,4 @@ node_modules .env # keep my actual GitHub secret hidden -backend/github.json +github.json From 7157ca04537d114cc4bef36fea462f17eedc26c4 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 10:55:30 +0200 Subject: [PATCH 077/827] fix: git is retarded when using gitignore and already-pushed files --- backend/github.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 backend/github.json diff --git a/backend/github.json b/backend/github.json deleted file mode 100644 index d16e7986..00000000 --- a/backend/github.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "client_id": "YOUR_CLIENT_ID", - "secret": "YOUR_CLIENT_SECRET", - "auth_callback_url": "YOUR_AUTH_CALLBACK" -} From a0d32a9ba76dc6f0ce875677452d762cb37f825e Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 11:32:50 +0200 Subject: [PATCH 078/827] feat: create password reset requests (no emails sent yet) --- backend/config.json | 4 ++++ backend/request.ts | 7 +++++++ backend/routes/reset.ts | 44 +++++++++++++++++++++++++++++++++++++++++ backend/types.ts | 4 ++++ 4 files changed, 59 insertions(+) create mode 100644 backend/routes/reset.ts diff --git a/backend/config.json b/backend/config.json index ade33c82..3394a6b0 100644 --- a/backend/config.json +++ b/backend/config.json @@ -33,6 +33,10 @@ "serverError": { "http": 500, "reason": "Something went wrong while trying to execute your request." + }, + "inavlidEmailReset": { + "http": 400, + "reason": "No such email is present. Please check your email." } }, diff --git a/backend/request.ts b/backend/request.ts index 59962526..44f551b2 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -409,6 +409,13 @@ export async function parseFormRequest(req: express.Request): }); } +export async function parseRequestResetRequest(req: express.Request): + Promise { + return hasFields(req, [ "email" ], types.neither).then(() => Promise.resolve({ + email : req.body.email + })); +} + /** * A request to `DELETE /login/` only requires a session key * {@link parseKeyRequest}. diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts new file mode 100644 index 00000000..8b882bfc --- /dev/null +++ b/backend/routes/reset.ts @@ -0,0 +1,44 @@ +import express from 'express'; + +import * as config from '../config.json'; +// import * as ormLU from '../orm_functions/login_user'; +import * as ormPR from '../orm_functions/password_reset'; +import * as ormP from '../orm_functions/person'; +import * as rq from '../request'; +import {Responses} from '../types'; +import * as util from '../utility'; + +async function requestReset(req: express.Request): Promise { + return rq.parseRequestResetRequest(req) + .then((parsed) => ormP.getPasswordPersonByEmail(parsed.email)) + .then(async (person) => { + if (person == null || person.login_user == null) { + return Promise.reject(config.apiErrors.inavlidEmailReset); + } + const date: Date = new Date(Date.now()); + date.setHours(date.getHours() + 24); + return ormPR + .createOrUpdateReset(person.login_user.login_user_id, + util.generateKey(), date) + .then(() => { + // somehow send an email + return Promise.resolve({}); + }) + }); +} + +async function resetPassword() { + // +} + +export function getRouter(): express.Router { + const router: express.Router = express.Router(); + + router.post('/', + (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); + util.route(router, "post", "/:id", resetPassword); + + util.addAllInvalidVerbs(router, [ "/", "/:id" ]); + + return router; +} diff --git a/backend/types.ts b/backend/types.ts index 74f08930..51fe908e 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -488,6 +488,10 @@ export interface Question { export interface Option { id: string, text: string } + +export interface ReqReset { + email: string; +} } /** From 6d73a8c9debe64e47224624debe30743c00d23e4 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 11:38:47 +0200 Subject: [PATCH 079/827] feat: skeleton route for all endpoints --- backend/routes/reset.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 8b882bfc..e3e5d026 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -27,6 +27,10 @@ async function requestReset(req: express.Request): Promise { }); } +async function checkCode() { + // +} + async function resetPassword() { // } @@ -36,6 +40,7 @@ export function getRouter(): express.Router { router.post('/', (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); + util.route(router, 'get', '/:id', checkCode); util.route(router, "post", "/:id", resetPassword); util.addAllInvalidVerbs(router, [ "/", "/:id" ]); From c92e80b96d0ad5d5248d09eb02eb3d8e4b90b89c Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 11:58:23 +0200 Subject: [PATCH 080/827] test: created int tests for contract --- .../tests/orm_integration/contract.test.ts | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 backend/tests/orm_integration/contract.test.ts diff --git a/backend/tests/orm_integration/contract.test.ts b/backend/tests/orm_integration/contract.test.ts new file mode 100644 index 00000000..6c476267 --- /dev/null +++ b/backend/tests/orm_integration/contract.test.ts @@ -0,0 +1,84 @@ +import prisma from "../../prisma/prisma"; +import {createContract, updateContract, removeContract, + removeContractsFromStudent} from "../../orm_functions/contract"; +import {CreateContract, UpdateContract} from '../../orm_functions/orm_types'; +import { contract_status_enum} from "@prisma/client" + +const contract1 : UpdateContract = { + contractId: 0, + information: "Contract details", + loginUserId: 0, + contractStatus: contract_status_enum.SENT +} + +const contract2 : UpdateContract = { + contractId: 0, + information: "New Contract details", + loginUserId: 0, + contractStatus: contract_status_enum.SIGNED +} + +it('should create 1 new contract linked to a student', async () => { + const student = await prisma.student.findFirst(); + const project_role = await prisma.project_role.findFirst() + const login_user = await prisma.login_user.findFirst(); + if (student && project_role && login_user){ + const contract : CreateContract = { + studentId: student.student_id, + projectRoleId: project_role.project_role_id, + information: "Contract details", + loginUserId: login_user.login_user_id, + contractStatus: contract_status_enum.SENT + } + const created_contract = await createContract(contract); + contract1.contractId = created_contract.contract_id; + contract2.contractId = created_contract.contract_id; + contract1.loginUserId = created_contract.created_by_login_user_id; + contract2.loginUserId = created_contract.created_by_login_user_id; + expect(created_contract).toHaveProperty("contract_id", contract1.contractId); + expect(created_contract).toHaveProperty("information", contract1.information); + expect(created_contract).toHaveProperty("created_by_login_user_id", contract1.loginUserId); + expect(created_contract).toHaveProperty("contract_status", contract1.contractStatus); + } +}); + +it('should create 1 new contract linked to a student', async () => { + const student = await prisma.student.findFirst(); + const project_role = await prisma.project_role.findFirst() + const login_user = await prisma.login_user.findFirst(); + if (student && project_role && login_user){ + const contract : CreateContract = { + studentId: student.student_id, + projectRoleId: project_role.project_role_id, + information: "Student Contract details", + loginUserId: login_user.login_user_id, + contractStatus: contract_status_enum.CANCELLED + } + const created_contract = await createContract(contract); + expect(created_contract).toHaveProperty("information", contract.information); + expect(created_contract).toHaveProperty("created_by_login_user_id", contract.loginUserId); + expect(created_contract).toHaveProperty("contract_status", contract.contractStatus); + } +}); + +it('should update contract based upon contract id', async () => { + const updated_contract = await updateContract(contract2); + expect(updated_contract).toHaveProperty("contract_id", contract2.contractId); + expect(updated_contract).toHaveProperty("information", contract2.information); + expect(updated_contract).toHaveProperty("created_by_login_user_id", contract2.loginUserId); + expect(updated_contract).toHaveProperty("contract_status", contract2.contractStatus); +}); + +it('should delete the contract based upon contract id', async () => { + const deleted_contract = await removeContract(contract2.contractId); + expect(deleted_contract).toHaveProperty("contract_id", contract2.contractId); + expect(deleted_contract).toHaveProperty("information", contract2.information); + expect(deleted_contract).toHaveProperty("created_by_login_user_id", contract2.loginUserId); + expect(deleted_contract).toHaveProperty("contract_status", contract2.contractStatus); +}); + +it('should delete the contracts based upon student', async () => { + const students = await prisma.student.findMany() + const deleted_contracts = await removeContractsFromStudent(students[0].student_id); + expect(deleted_contracts).toHaveProperty("count", 1); +}); From 33cd83bc4a8af878fd5635336c31cd9e9725ad40 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 14:25:48 +0200 Subject: [PATCH 081/827] feat: email temporarily --- backend/config.json | 4 +++ backend/email.ts | 36 +++++++++++++++++++++++++ backend/github.json | 6 +++++ backend/package-lock.json | 31 ++++++++++++++++++++++ backend/package.json | 2 ++ backend/routes/reset.ts | 55 +++++++++++++++++++++------------------ backend/types.ts | 6 +++++ 7 files changed, 115 insertions(+), 25 deletions(-) create mode 100644 backend/email.ts create mode 100644 backend/github.json diff --git a/backend/config.json b/backend/config.json index 3394a6b0..599138a9 100644 --- a/backend/config.json +++ b/backend/config.json @@ -45,5 +45,9 @@ "", "/api-osoc" ], "preferred": "/api-osoc" + }, + + "email": { + "from": "'OSOC2 Test Account' " } } diff --git a/backend/email.ts b/backend/email.ts new file mode 100644 index 00000000..20101c8b --- /dev/null +++ b/backend/email.ts @@ -0,0 +1,36 @@ +import * as nodemailer from 'nodemailer'; + +import * as config from './config.json'; +import {Email} from './types'; + +// stateful function +const {mailer} = new class { + acct: nodemailer.TestAccount|null = null; + transp: nodemailer.Transporter|null = null; + + mailer = async (mail: Email) => { + if (this.acct == null) { + this.acct = await nodemailer.createTestAccount(); + } + if (this.transp == null) { + this.transp = nodemailer.createTransport({ + host : "smtp.ethereal.email", + port : 587, + secure : false, // true for 465, false for other ports + auth : { + user : this.acct.user, // generated ethereal user + pass : this.acct.pass, // generated ethereal password + } + }); + } + + return this.transp.sendMail({ + from : config.email.from, + to : mail.to, + subject : mail.subject, + html : mail.html + }); + }; +} + +export default mailer; diff --git a/backend/github.json b/backend/github.json new file mode 100644 index 00000000..dfd8b142 --- /dev/null +++ b/backend/github.json @@ -0,0 +1,6 @@ +{ + "client_id": "36264ada9edd7f1ef96a", + "secret": "6f946ad9b524cf185e385dcf7c5189da0bc8404e", + "auth_callback_url": "http://127.0.0.1:4096" +} + diff --git a/backend/package-lock.json b/backend/package-lock.json index 73ed2d72..f3f001c0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -14,10 +14,12 @@ "@types/cors": "^2.8.12", "@types/express": "^4.17.13", "@types/node": "^17.0.21", + "@types/nodemailer": "^6.4.4", "@types/uuid": "^8.3.4", "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", + "nodemailer": "^6.7.3", "passport": "^0.5.2", "prisma": "3.11", "ts-jest": "^27.1.3" @@ -1328,6 +1330,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" }, + "node_modules/@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prettier": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", @@ -5011,6 +5021,14 @@ "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", + "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "2.0.15", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", @@ -7807,6 +7825,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==" }, + "@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "requires": { + "@types/node": "*" + } + }, "@types/prettier": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", @@ -10578,6 +10604,11 @@ } } }, + "nodemailer": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.3.tgz", + "integrity": "sha512-KUdDsspqx89sD4UUyUKzdlUOper3hRkDVkrKh/89G+d9WKsU5ox51NWS4tB1XR5dPUdR4SP0E3molyEfOvSa3g==" + }, "nodemon": { "version": "2.0.15", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", diff --git a/backend/package.json b/backend/package.json index da397416..48890b0d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -21,10 +21,12 @@ "@types/cors": "^2.8.12", "@types/express": "^4.17.13", "@types/node": "^17.0.21", + "@types/nodemailer": "^6.4.4", "@types/uuid": "^8.3.4", "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", + "nodemailer": "^6.7.3", "passport": "^0.5.2", "prisma": "3.11", "ts-jest": "^27.1.3" diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index e3e5d026..7de78f59 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -1,6 +1,7 @@ import express from 'express'; import * as config from '../config.json'; +import mailer from '../email'; // import * as ormLU from '../orm_functions/login_user'; import * as ormPR from '../orm_functions/password_reset'; import * as ormP from '../orm_functions/person'; @@ -9,39 +10,43 @@ import {Responses} from '../types'; import * as util from '../utility'; async function requestReset(req: express.Request): Promise { - return rq.parseRequestResetRequest(req) - .then((parsed) => ormP.getPasswordPersonByEmail(parsed.email)) - .then(async (person) => { - if (person == null || person.login_user == null) { - return Promise.reject(config.apiErrors.inavlidEmailReset); - } - const date: Date = new Date(Date.now()); - date.setHours(date.getHours() + 24); - return ormPR - .createOrUpdateReset(person.login_user.login_user_id, - util.generateKey(), date) - .then(() => { - // somehow send an email - return Promise.resolve({}); - }) - }); + return rq.parseRequestResetRequest(req).then( + (parsed) => + ormP.getPasswordPersonByEmail(parsed.email).then(async (person) => { + if (person == null || person.login_user == null) { + return Promise.reject(config.apiErrors.inavlidEmailReset); + } + const date: Date = new Date(Date.now()); + date.setHours(date.getHours() + 24); + return ormPR + .createOrUpdateReset(person.login_user.login_user_id, + util.generateKey(), date) + .then(async (code) => { + return mailer({ + to : parsed.email, + subject : 'OSOC2 Recovery Code', + html : '

' + code.reset_id + '

' + }) + .then(() => Promise.resolve({})); + }) + })); } -async function checkCode() { - // -} - -async function resetPassword() { - // -} +// async function checkCode() { +// // +// } +// +// async function resetPassword() { +// // +// } export function getRouter(): express.Router { const router: express.Router = express.Router(); router.post('/', (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); - util.route(router, 'get', '/:id', checkCode); - util.route(router, "post", "/:id", resetPassword); + //util.route(router, 'get', '/:id', checkCode); + //util.route(router, "post", "/:id", resetPassword); util.addAllInvalidVerbs(router, [ "/", "/:id" ]); diff --git a/backend/types.ts b/backend/types.ts index 51fe908e..c2ed14b4 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -519,3 +519,9 @@ export type RouteCallback = export interface Anything { [key: string]: unknown; } + +export interface Email { + to: string; + subject: string; + html: string; +} From f97381fda91a35e9f7a7a5cd657fa2874011d06b Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Thu, 31 Mar 2022 14:49:09 +0200 Subject: [PATCH 082/827] feat: add redirect to github button and add dynamic route /login --- frontend/pages/{login.tsx => login/index.tsx} | 17 +++++++++-------- frontend/pages/login/sessionkey.tsx | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) rename frontend/pages/{login.tsx => login/index.tsx} (97%) create mode 100644 frontend/pages/login/sessionkey.tsx diff --git a/frontend/pages/login.tsx b/frontend/pages/login/index.tsx similarity index 97% rename from frontend/pages/login.tsx rename to frontend/pages/login/index.tsx index 0ca43da6..592e2106 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login/index.tsx @@ -1,22 +1,22 @@ import type {NextPage} from 'next' -import styles from '../styles/login.module.css' +import styles from '../../styles/login.module.css' import Image from "next/image" -import GitHubLogo from "../public/images/github-logo.svg" +import GitHubLogo from "../../public/images/github-logo.svg" import {SyntheticEvent, useContext, useState} from "react"; -import {Modal} from "../components/Modal/Modal"; +import {Modal} from "../../components/Modal/Modal"; import {useRouter} from "next/router"; -import {Header} from "../components/Header/Header"; +import {Header} from "../../components/Header/Header"; import * as crypto from 'crypto'; -import SessionContext from "../contexts/sessionProvider"; +import SessionContext from "../../contexts/sessionProvider"; -const Login: NextPage = () => { +const Index: NextPage = () => { const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) const router = useRouter() - // Login field values with corresponding error messages + // Index field values with corresponding error messages const [loginEmail, setLoginEmail] = useState(""); const [loginEmailError, setLoginEmailError] = useState(""); const [loginPassword, setLoginPassword] = useState(""); @@ -200,6 +200,7 @@ const Login: NextPage = () => { */ const githubLogin = async (e: SyntheticEvent) => { e.preventDefault(); + window.location.href = "http://localhost:4096/api-osoc/github" // TODO -- How are we supposed to send the data to the backend? } @@ -340,7 +341,7 @@ const Login: NextPage = () => {
) } -export default Login; +export default Index; export type SignInResult = { diff --git a/frontend/pages/login/sessionkey.tsx b/frontend/pages/login/sessionkey.tsx new file mode 100644 index 00000000..0a3a6c73 --- /dev/null +++ b/frontend/pages/login/sessionkey.tsx @@ -0,0 +1,10 @@ +import {NextPage} from "next"; +import {useRouter} from "next/router"; + +const Sessionkey: NextPage = () => { + const router = useRouter() + const sessionKey = router.query + return

{sessionKey}

+} + +export default Sessionkey; From 3239704ee075fa365c3a17e656621852ae10097a Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Thu, 31 Mar 2022 14:58:23 +0200 Subject: [PATCH 083/827] fix: we now get the pid from /login/sesionkey --- frontend/pages/login/[pid].tsx | 15 +++++++++++++++ frontend/pages/login/sessionkey.tsx | 10 ---------- 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 frontend/pages/login/[pid].tsx delete mode 100644 frontend/pages/login/sessionkey.tsx diff --git a/frontend/pages/login/[pid].tsx b/frontend/pages/login/[pid].tsx new file mode 100644 index 00000000..5c0b863f --- /dev/null +++ b/frontend/pages/login/[pid].tsx @@ -0,0 +1,15 @@ +import {NextPage} from "next"; +import {useRouter} from "next/router"; + +/** + * Can be used when redirect from a different site to log in given credentials in the URL + * Being of form /login/sessionkey?admin=boolean&coach=boolean + * @constructor + */ +const Pid: NextPage = () => { + const router = useRouter() + const { pid } = router.query // pid is the session key + return

{pid}

+} + +export default Pid; diff --git a/frontend/pages/login/sessionkey.tsx b/frontend/pages/login/sessionkey.tsx deleted file mode 100644 index 0a3a6c73..00000000 --- a/frontend/pages/login/sessionkey.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import {NextPage} from "next"; -import {useRouter} from "next/router"; - -const Sessionkey: NextPage = () => { - const router = useRouter() - const sessionKey = router.query - return

{sessionKey}

-} - -export default Sessionkey; From 6b6dc531fca548e6800d775ef7d5d889ef0d1714 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 15:14:04 +0200 Subject: [PATCH 084/827] feat: backend now redirects back to frontend --- backend/config.json | 3 ++- backend/routes/github.ts | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/backend/config.json b/backend/config.json index 1cf209c0..ede07dd4 100644 --- a/backend/config.json +++ b/backend/config.json @@ -54,6 +54,7 @@ "homes": [ "", "/api-osoc" ], - "preferred": "/api-osoc" + "preferred": "/api-osoc", + "frontend": "http://localhost:3000" } } diff --git a/backend/routes/github.ts b/backend/routes/github.ts index 0f36f483..abcac745 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -51,8 +51,8 @@ function ghIdentity(resp: express.Response): void { util.redirect(resp, url); } -async function ghExchangeAccessToken(req: express.Request): - Promise { +async function ghExchangeAccessToken(req: express.Request, + res: express.Response): Promise { if (!("code" in req.query)) { return Promise.reject(config.apiErrors.github.argumentMissing); } @@ -78,6 +78,10 @@ async function ghExchangeAccessToken(req: express.Request): })) .then(ares => parseGHLogin(ares.data)) .then(login => ghSignupOrLogin(login)) + .then(result => util.redirect(res, config.global.frontend + "/login/" + + result.sessionkey + + "?is_admin=" + result.is_admin + + "&is_coach=" + result.is_coach)) .catch(err => { console.log('GITHUB ERROR ' + err); return Promise.reject(config.apiErrors.github.other); @@ -93,7 +97,7 @@ function parseGHLogin(data: Anything): Promise { } async function ghSignupOrLogin(login: Requests.GHLogin): - Promise { + Promise { return ormP.getPasswordPersonByEmail(login.login) .then(person => { if (person == null || person.login_user == null) @@ -114,7 +118,9 @@ async function ghSignupOrLogin(login: Requests.GHLogin): .then(res => Promise.resolve({ password : res.password, login_user_id : res.login_user_id, - account_status : res.account_status + account_status : res.account_status, + is_admin : false, + is_coach : true })); } else { return Promise.reject(error); // pass on @@ -122,16 +128,19 @@ async function ghSignupOrLogin(login: Requests.GHLogin): }) .then(async loginuser => ormSK.addSessionKey(loginuser.login_user_id, util.generateKey()) - .then(newkey => Promise.resolve( - {sessionkey : newkey.session_key}))); + .then(newkey => Promise.resolve({ + sessionkey : newkey.session_key, + is_admin : loginuser.is_admin, + is_coach : loginuser.is_coach + }))); } export function getRouter(): express.Router { const router: express.Router = express.Router(); router.get('/', (_, rs) => ghIdentity(rs)); - router.get('/challenge', (req, res) => util.respOrErrorNoReinject( - res, ghExchangeAccessToken(req))); + router.get('/challenge', + async (req, res) => await ghExchangeAccessToken(req, res)); return router; } From d0c861319efb9e5d0e401f3fc6e9ef3be81473eb Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Thu, 31 Mar 2022 15:18:10 +0200 Subject: [PATCH 085/827] feat: add endpoints for login in with url and error catching --- frontend/pages/login/[pid].tsx | 28 +++++++++++++++++++++++++--- frontend/pages/login/index.tsx | 16 +++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/frontend/pages/login/[pid].tsx b/frontend/pages/login/[pid].tsx index 5c0b863f..7a428c44 100644 --- a/frontend/pages/login/[pid].tsx +++ b/frontend/pages/login/[pid].tsx @@ -1,15 +1,37 @@ import {NextPage} from "next"; import {useRouter} from "next/router"; +import {useContext, useEffect} from "react"; +import SessionContext from "../../contexts/sessionProvider"; /** * Can be used when redirect from a different site to log in given credentials in the URL - * Being of form /login/sessionkey?admin=boolean&coach=boolean + * Being of form /login/sessionkey?is_admin=boolean&is_coach=boolean * @constructor */ const Pid: NextPage = () => { + const router = useRouter() - const { pid } = router.query // pid is the session key - return

{pid}

+ const {setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) + const { pid, is_admin, is_coach } = router.query // pid is the session key + + useEffect(() => { + if (pid !== undefined && typeof pid === 'string' && setSessionKey !== undefined) { + setSessionKey(pid) + } + if (is_admin !== undefined && is_admin === 'true' && setIsAdmin !== undefined) { + setIsAdmin(true) + } + if (is_coach !== undefined && is_coach === 'true' && setIsCoach !== undefined) { + setIsCoach(true) + } + + // redirect to /students + if (pid !== undefined && typeof pid === 'string') { + router.push("/students").then() + } + }) + + return <> } export default Pid; diff --git a/frontend/pages/login/index.tsx b/frontend/pages/login/index.tsx index 592e2106..7eaf68c4 100644 --- a/frontend/pages/login/index.tsx +++ b/frontend/pages/login/index.tsx @@ -2,7 +2,7 @@ import type {NextPage} from 'next' import styles from '../../styles/login.module.css' import Image from "next/image" import GitHubLogo from "../../public/images/github-logo.svg" -import {SyntheticEvent, useContext, useState} from "react"; +import {SyntheticEvent, useContext, useEffect, useState} from "react"; import {Modal} from "../../components/Modal/Modal"; import {useRouter} from "next/router"; import {Header} from "../../components/Header/Header"; @@ -12,10 +12,20 @@ import SessionContext from "../../contexts/sessionProvider"; const Index: NextPage = () => { - const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) - const router = useRouter() + // Sets an error message when the `loginError` query paramater is present + useEffect(() => { + const { loginError } = router.query + if (loginError != undefined) { + if (typeof loginError === 'string') { + setLoginBackendError(loginError) + } + } + }, [router.query]) + + const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) + // Index field values with corresponding error messages const [loginEmail, setLoginEmail] = useState(""); const [loginEmailError, setLoginEmailError] = useState(""); From 691e99ddbe288b764b4e9cd9da5278c3fbab17b4 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 15:31:11 +0200 Subject: [PATCH 086/827] fix: extra orm_function to search by GH (unique) ~ log in with already-registered GitHub account --- backend/orm_functions/person.ts | 23 +++++++++++++++++++++++ backend/routes/github.ts | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/backend/orm_functions/person.ts b/backend/orm_functions/person.ts index 57d59a82..a1e4dff6 100644 --- a/backend/orm_functions/person.ts +++ b/backend/orm_functions/person.ts @@ -46,6 +46,29 @@ export async function getPasswordPersonByEmail(email: string) { }); } +/** + * + * @param github: this is the GitHub username of the person we are looking up in + * the database + * @returns: password of the login user matching with the person + */ +export async function getPasswordPersonByGithub(github: string) { + return await prisma.person.findUnique({ + where : {github : github}, + select : { + login_user : { + select : { + password : true, + login_user_id : true, + account_status : true, + is_admin : true, + is_coach : true + } + } + } + }); +} + /** * * @param name: This is the name of the person we are looking, can be firstname diff --git a/backend/routes/github.ts b/backend/routes/github.ts index abcac745..d8eae17d 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -98,7 +98,7 @@ function parseGHLogin(data: Anything): Promise { async function ghSignupOrLogin(login: Requests.GHLogin): Promise { - return ormP.getPasswordPersonByEmail(login.login) + return ormP.getPasswordPersonByGithub(login.login) .then(person => { if (person == null || person.login_user == null) return Promise.reject({is_not_existent : true}); From c9812cb1265e022c28348fc4bfb9773cddf2f740 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Thu, 31 Mar 2022 15:35:06 +0200 Subject: [PATCH 087/827] fix: use env variables for the api link --- frontend/pages/login/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/login/index.tsx b/frontend/pages/login/index.tsx index 7eaf68c4..01a7a4aa 100644 --- a/frontend/pages/login/index.tsx +++ b/frontend/pages/login/index.tsx @@ -210,7 +210,7 @@ const Index: NextPage = () => { */ const githubLogin = async (e: SyntheticEvent) => { e.preventDefault(); - window.location.href = "http://localhost:4096/api-osoc/github" + window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/github` // TODO -- How are we supposed to send the data to the backend? } From 97dab28ee8169428ed367d5216d44e3dee14035d Mon Sep 17 00:00:00 2001 From: Jonathan Vanbrabant Date: Thu, 31 Mar 2022 15:37:21 +0200 Subject: [PATCH 088/827] feat: students data inladen via props --- frontend/pages/students.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontend/pages/students.tsx b/frontend/pages/students.tsx index 752d7c7d..9b2a5bf0 100644 --- a/frontend/pages/students.tsx +++ b/frontend/pages/students.tsx @@ -1,6 +1,18 @@ import {NextPage} from "next"; import {Header} from "../components/Header/Header"; import {StudentCard} from "../components/StudentCard/StudentCard"; +import { GetStaticProps } from 'next'; + + + +export const getStaticProps: GetStaticProps = async () => { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`); + const students = (await res.json()); + console.log(students); + return { + props: students + } +} const Students: NextPage = () => { return ( From a70c7bd5d621967716039026733e06fa5a9929ae Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 15:42:51 +0200 Subject: [PATCH 089/827] feat: errors redirect to FE page --- backend/config.json | 1 - backend/routes/github.ts | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/config.json b/backend/config.json index ede07dd4..9a3f95b9 100644 --- a/backend/config.json +++ b/backend/config.json @@ -44,7 +44,6 @@ "reason": "GitHub authentication endpoints are meant for GitHub authentication only. Thank you very much." }, "other": { - "http": 422, "reason": "GitHub data requests failed. Please try again later." } } diff --git a/backend/routes/github.ts b/backend/routes/github.ts index d8eae17d..a3d0d81d 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -84,7 +84,9 @@ async function ghExchangeAccessToken(req: express.Request, "&is_coach=" + result.is_coach)) .catch(err => { console.log('GITHUB ERROR ' + err); - return Promise.reject(config.apiErrors.github.other); + util.redirect(res, config.global.frontend + "/login?loginError=" + + config.apiErrors.github.other.reason); + return Promise.resolve(); }); } From 2b7838312b34b5eda0fadac89df6290aed8929b0 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 16:05:14 +0200 Subject: [PATCH 090/827] fix: moved some more data into config files --- backend/config.json | 3 ++- backend/endpoints.ts | 2 ++ backend/routes/reset.ts | 15 ++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backend/config.json b/backend/config.json index 599138a9..e4fda642 100644 --- a/backend/config.json +++ b/backend/config.json @@ -48,6 +48,7 @@ }, "email": { - "from": "'OSOC2 Test Account' " + "from": "'OSOC2 Test Account' ", + "header": "OSOC2 Recovery Code" } } diff --git a/backend/endpoints.ts b/backend/endpoints.ts index 130f09a1..8daf448b 100644 --- a/backend/endpoints.ts +++ b/backend/endpoints.ts @@ -6,6 +6,7 @@ import * as coach from './routes/coach'; import * as form from './routes/form'; import * as login from './routes/login'; import * as project from './routes/project'; +import * as reset from './routes/reset'; import * as student from './routes/student'; import * as util from './utility'; @@ -21,6 +22,7 @@ export function attach(app: express.Application): void { app.use(home + '/admin', admin.getRouter()); app.use(home + '/project', project.getRouter()); app.use(home + '/form', form.getRouter); + app.use(home + '/reset', reset.getRouter()); }); app.use((req: express.Request, res: express.Response): Promise => diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 7de78f59..effc0e66 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -1,4 +1,5 @@ import express from 'express'; +import * as nodemailer from 'nodemailer'; import * as config from '../config.json'; import mailer from '../email'; @@ -24,11 +25,15 @@ async function requestReset(req: express.Request): Promise { .then(async (code) => { return mailer({ to : parsed.email, - subject : 'OSOC2 Recovery Code', + subject : config.email.header, html : '

' + code.reset_id + '

' }) - .then(() => Promise.resolve({})); - }) + .then(data => { + console.log(data); + console.log(nodemailer.getTestMessageUrl(data)); + return Promise.resolve({}); + }); + }); })); } @@ -45,8 +50,8 @@ export function getRouter(): express.Router { router.post('/', (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); - //util.route(router, 'get', '/:id', checkCode); - //util.route(router, "post", "/:id", resetPassword); + // util.route(router, 'get', '/:id', checkCode); + // util.route(router, "post", "/:id", resetPassword); util.addAllInvalidVerbs(router, [ "/", "/:id" ]); From 014e845c5e94a05f707ea78f4a7a692f56ad94f8 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 16:14:37 +0200 Subject: [PATCH 091/827] fix: unrelated fix -> error message don't display , ... anymore but actually replace those values --- backend/config.json | 6 +++--- backend/utility.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/config.json b/backend/config.json index e4fda642..933872f1 100644 --- a/backend/config.json +++ b/backend/config.json @@ -20,15 +20,15 @@ }, "nonExistent": { "http": 404, - "reason": "The endpoint requested ($url) does not exist." + "reason": "The endpoint requested (~url) does not exist." }, "invalidVerb": { "http": 405, - "reason": "This HTTP verb ($verb) is not supported for this endpoint ($url)" + "reason": "This HTTP verb (~verb) is not supported for this endpoint (~url)" }, "nonJSONRequest": { "http": 406, - "reason": "All endpoints only support JSON ($mime requested)." + "reason": "All endpoints only support JSON (~mime requested)." }, "serverError": { "http": 500, diff --git a/backend/utility.ts b/backend/utility.ts index 929f4624..df1e0931 100644 --- a/backend/utility.ts +++ b/backend/utility.ts @@ -31,22 +31,22 @@ export const errors: Errors = { cookNonExistent(url: string) { return { http : config.apiErrors.nonExistent.http, - reason : config.apiErrors.nonExistent.reason.replace(/$url/, url) + reason : config.apiErrors.nonExistent.reason.replace(/~url/, url) }; }, cookInvalidVerb(req: express.Request) { return { http : config.apiErrors.invalidVerb.http, - reason : config.apiErrors.invalidVerb.reason.replace(/$verb/, req.method) - .replace(/$url/, req.url) + reason : config.apiErrors.invalidVerb.reason.replace(/~verb/, req.method) + .replace(/~url/, req.url) }; }, cookNonJSON(mime: string) { return { http : config.apiErrors.nonJSONRequest.http, - reason : config.apiErrors.nonJSONRequest.reason.replace(/$mime/, mime) + reason : config.apiErrors.nonJSONRequest.reason.replace(/~mime/, mime) }; }, From a5650e142466c2df6adda2701072a7d04b7299cb Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 16:36:49 +0200 Subject: [PATCH 092/827] feat: reset now validates codes --- backend/orm_functions/password_reset.ts | 55 +++++++++++++------------ backend/request.ts | 7 ++++ backend/routes/reset.ts | 19 ++++++--- backend/types.ts | 4 ++ 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/backend/orm_functions/password_reset.ts b/backend/orm_functions/password_reset.ts index 8e61f10b..c3e5efa6 100644 --- a/backend/orm_functions/password_reset.ts +++ b/backend/orm_functions/password_reset.ts @@ -9,34 +9,35 @@ import prisma from "../prisma/prisma"; * @param validUntil timestamp that shows until when the resetId is valid * @return the created/updated entry from the database in a promise */ -export async function createOrUpdateReset(loginUserId: number, resetId: string, validUntil: Date) { - return await prisma.password_reset.upsert({ - where: { - login_user_id: loginUserId, - }, - create: { - login_user_id: loginUserId, - reset_id: resetId, - valid_until: validUntil - }, - update : { - reset_id: resetId, - valid_until: validUntil - } - }); +export async function createOrUpdateReset(loginUserId: number, resetId: string, + validUntil: Date) { + return await prisma.password_reset.upsert({ + where : { + login_user_id : loginUserId, + }, + create : { + login_user_id : loginUserId, + reset_id : resetId, + valid_until : validUntil + }, + update : {reset_id : resetId, valid_until : validUntil} + }); +} + +export async function findResetByCode(resetCode: string) { + return await prisma.password_reset.findUnique( + {where : {reset_id : resetCode}}); } /** * - * @param loginUserId the id of the loginUser whose reset entry we want to delete + * @param loginUserId the id of the loginUser whose reset entry we want to + * delete * @returns the deleted entry (or null if there was no entry) inside a promise */ export async function deleteResetWithLoginUser(loginUserId: number) { - return await prisma.password_reset.delete({ - where: { - login_user_id: loginUserId - } - }); + return await prisma.password_reset.delete( + {where : {login_user_id : loginUserId}}); } /** @@ -45,9 +46,9 @@ export async function deleteResetWithLoginUser(loginUserId: number) { * @returns the deleted entry (or null if there was no entry) inside a promise */ export async function deleteResetWithResetId(resetId: string) { - return await prisma.password_reset.delete({ - where : { - reset_id: resetId, - } - }); -} \ No newline at end of file + return await prisma.password_reset.delete({ + where : { + reset_id : resetId, + } + }); +} diff --git a/backend/request.ts b/backend/request.ts index 44f551b2..328452e0 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -416,6 +416,13 @@ export async function parseRequestResetRequest(req: express.Request): })); } +export async function parseCheckResetCodeRequest(req: express.Request): + Promise { + if (!("id" in req.params)) + return Promise.reject(errors.cookArgumentError()); + return Promise.resolve({code : req.params.id}); +} + /** * A request to `DELETE /login/` only requires a session key * {@link parseKeyRequest}. diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index effc0e66..9b43c162 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -37,10 +37,18 @@ async function requestReset(req: express.Request): Promise { })); } -// async function checkCode() { -// // -// } -// +async function checkCode(req: express.Request): Promise { + return rq.parseCheckResetCodeRequest(req) + .then(parsed => ormPR.findResetByCode(parsed.code)) + .then(res => { + if (res == null || res.valid_until < new Date(Date.now())) + return Promise.reject(); + + return Promise.resolve({}); + }) + .catch(() => Promise.reject(util.errors.cookArgumentError())); +} + // async function resetPassword() { // // // } @@ -50,7 +58,8 @@ export function getRouter(): express.Router { router.post('/', (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); - // util.route(router, 'get', '/:id', checkCode); + router.get('/:id', + (req, res) => util.respOrErrorNoReinject(res, checkCode(req))); // util.route(router, "post", "/:id", resetPassword); util.addAllInvalidVerbs(router, [ "/", "/:id" ]); diff --git a/backend/types.ts b/backend/types.ts index c2ed14b4..050942b3 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -492,6 +492,10 @@ export interface Option { export interface ReqReset { email: string; } + +export interface ResetCheckCode { + code: string; +} } /** From 0b0b4ca3b641b373c33cdcdf500330b514baa623 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 18:54:11 +0200 Subject: [PATCH 093/827] feat: password reset changes the password & logs in with a new session key --- backend/request.ts | 7 +++++++ backend/routes/reset.ts | 37 ++++++++++++++++++++++++++++++++----- backend/types.ts | 5 +++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/backend/request.ts b/backend/request.ts index 328452e0..ccf98dc4 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -423,6 +423,13 @@ export async function parseCheckResetCodeRequest(req: express.Request): return Promise.resolve({code : req.params.id}); } +export async function parseResetPasswordRequest(req: express.Request): + Promise { + if (!("id" in req.params) || !("password" in req.body)) + return Promise.reject(errors.cookArgumentError()); + return Promise.resolve({code : req.params.id, password : req.body.password}); +} + /** * A request to `DELETE /login/` only requires a session key * {@link parseKeyRequest}. diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 9b43c162..98592681 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -3,9 +3,10 @@ import * as nodemailer from 'nodemailer'; import * as config from '../config.json'; import mailer from '../email'; -// import * as ormLU from '../orm_functions/login_user'; +import * as ormLU from '../orm_functions/login_user'; import * as ormPR from '../orm_functions/password_reset'; import * as ormP from '../orm_functions/person'; +import * as ormSK from '../orm_functions/session_key'; import * as rq from '../request'; import {Responses} from '../types'; import * as util from '../utility'; @@ -49,9 +50,34 @@ async function checkCode(req: express.Request): Promise { .catch(() => Promise.reject(util.errors.cookArgumentError())); } -// async function resetPassword() { -// // -// } +async function resetPassword(req: express.Request): Promise { + return rq.parseResetPasswordRequest(req).then( + parsed => ormPR.findResetByCode(parsed.code).then(async code => { + if (code == null || code.valid_until < new Date(Date.now())) + return Promise.reject(util.errors.cookArgumentError()); + + return ormLU.getLoginUserById(code.login_user_id) + .then(user => { + if (user == null) + return Promise.reject(); + console.log("Updating user " + JSON.stringify(user) + + "'s password to " + parsed.password); + return ormLU.updateLoginUser({ + loginUserId : user.login_user_id, + isAdmin : user.is_admin, + isCoach : user.is_coach, + accountStatus : user?.account_status, + password : parsed.password + }); + }) + .then(user => { + console.log(JSON.stringify(user)); + return ormSK.addSessionKey(user.login_user_id, + util.generateKey()); + }) + .then(key => Promise.resolve({sessionkey : key.session_key})); + })); +} export function getRouter(): express.Router { const router: express.Router = express.Router(); @@ -60,7 +86,8 @@ export function getRouter(): express.Router { (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); router.get('/:id', (req, res) => util.respOrErrorNoReinject(res, checkCode(req))); - // util.route(router, "post", "/:id", resetPassword); + router.post("/:id", (req, res) => + util.respOrErrorNoReinject(res, resetPassword(req))); util.addAllInvalidVerbs(router, [ "/", "/:id" ]); diff --git a/backend/types.ts b/backend/types.ts index 050942b3..1fb3915d 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -496,6 +496,11 @@ export interface ReqReset { export interface ResetCheckCode { code: string; } + +export interface ResetPassword { + code: string; + password: string; +} } /** From c668eefd0edf80e9eaddc8adadbcc95d9c6e034c Mon Sep 17 00:00:00 2001 From: jay-tux Date: Thu, 31 Mar 2022 18:57:12 +0200 Subject: [PATCH 094/827] feat: password reset invalidates the code --- backend/routes/reset.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 98592681..a4e50128 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -75,7 +75,10 @@ async function resetPassword(req: express.Request): Promise { return ormSK.addSessionKey(user.login_user_id, util.generateKey()); }) - .then(key => Promise.resolve({sessionkey : key.session_key})); + .then(async key => { + return ormPR.deleteResetWithResetId(code.reset_id) + .then(() => Promise.resolve({sessionkey : key.session_key})); + }); })); } From 0b76e3c0a8e6a78265bfa5484974e62c9eeae4a8 Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Thu, 31 Mar 2022 19:32:46 +0200 Subject: [PATCH 095/827] test: created int. tests for student --- backend/orm_functions/student.ts | 1 + .../orm_integration/integration_setup.ts | 9 +- backend/tests/orm_integration/student.test.ts | 98 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 backend/tests/orm_integration/student.test.ts diff --git a/backend/orm_functions/student.ts b/backend/orm_functions/student.ts index 31d4abd0..0ea6e263 100644 --- a/backend/orm_functions/student.ts +++ b/backend/orm_functions/student.ts @@ -62,6 +62,7 @@ export async function updateStudent(student: UpdateStudent) { phone_number: student.phoneNumber, nickname: student.nickname, alumni: student.alumni, + gender: student.gender }, include: { person: true, diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 82f7a51a..75e74d5f 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -97,7 +97,14 @@ beforeAll(async () => { pronouns: ["He", "Him"], phone_number: "112", alumni: false, - } + }, + { + person_id: persons[1].person_id, + gender: "Female", + pronouns: ["She", "Her"], + phone_number: "107", + alumni: true, + } ] }) const students = await prisma.student.findMany(); diff --git a/backend/tests/orm_integration/student.test.ts b/backend/tests/orm_integration/student.test.ts new file mode 100644 index 00000000..eac3d7a1 --- /dev/null +++ b/backend/tests/orm_integration/student.test.ts @@ -0,0 +1,98 @@ +import {CreateStudent, UpdateStudent} from "../../orm_functions/orm_types"; +import prisma from "../../prisma/prisma"; +import {createStudent, getAllStudents, getStudent, + updateStudent, searchStudentByGender, deleteStudent} from "../../orm_functions/student"; + +const student1: UpdateStudent = { + studentId: 0, + gender: "Male", + pronouns: ["He", "Him"], + phoneNumber: "013456789", + nickname: "Superman", + alumni: true +}; + +const student2: UpdateStudent = { + studentId: 0, + gender: "Female", + pronouns: ["She", "Her"], + phoneNumber: "9876543210", + nickname: "Superwoman", + alumni: true +}; + +it('should create 1 new student', async () => { + const person = await prisma.person.findMany(); + if (person){ + const student: CreateStudent = { + personId: person[0].person_id, + gender: "Male", + pronouns: ["He", "Him"], + phoneNumber: "013456789", + nickname: "Superman", + alumni: true + } + + const created_student = await createStudent(student); + student1.studentId = created_student.student_id; + student2.studentId = created_student.student_id; + expect(created_student).toHaveProperty("student_id", created_student.student_id); + expect(created_student).toHaveProperty("gender", created_student.gender); + expect(created_student).toHaveProperty("pronouns", created_student.pronouns); + expect(created_student).toHaveProperty("phone_number", created_student.phone_number); + expect(created_student).toHaveProperty("nickname", created_student.nickname); + expect(created_student).toHaveProperty("alumni", created_student.alumni); + } +}); + +it('should find all the students in the db, 3 in total', async () => { + const searched_students = await getAllStudents(); + expect(searched_students.length).toEqual(3); + expect(searched_students[2]).toHaveProperty("student_id", student1.studentId); + expect(searched_students[2]).toHaveProperty("gender", student1.gender); + expect(searched_students[2]).toHaveProperty("pronouns", student1.pronouns); + expect(searched_students[2]).toHaveProperty("phone_number", student1.phoneNumber); + expect(searched_students[2]).toHaveProperty("nickname", student1.nickname); + expect(searched_students[2]).toHaveProperty("alumni", student1.alumni); +}); + +it('should return the student, by searching for its id', async () => { + const searched_student = await getStudent(student1.studentId); + expect(searched_student).toHaveProperty("student_id", student1.studentId); + expect(searched_student).toHaveProperty("gender", student1.gender); + expect(searched_student).toHaveProperty("pronouns", student1.pronouns); + expect(searched_student).toHaveProperty("phone_number", student1.phoneNumber); + expect(searched_student).toHaveProperty("nickname", student1.nickname); + expect(searched_student).toHaveProperty("alumni", student1.alumni); +}); + +it('should return the students, by searching for gender', async () => { + const searched_student = await searchStudentByGender("Male"); + expect(searched_student.length).toEqual(2); + expect(searched_student[1]).toHaveProperty("student_id", student1.studentId); + expect(searched_student[1]).toHaveProperty("gender", student1.gender); + expect(searched_student[1]).toHaveProperty("pronouns", student1.pronouns); + expect(searched_student[1]).toHaveProperty("phone_number", student1.phoneNumber); + expect(searched_student[1]).toHaveProperty("nickname", student1.nickname); + expect(searched_student[1]).toHaveProperty("alumni", student1.alumni); +}); + +it('should update student based upon student id', async () => { + const updated_student = await updateStudent(student2); + expect(updated_student).toHaveProperty("student_id", student2.studentId); + expect(updated_student).toHaveProperty("gender", student2.gender); + expect(updated_student).toHaveProperty("pronouns", student2.pronouns); + expect(updated_student).toHaveProperty("phone_number", student2.phoneNumber); + expect(updated_student).toHaveProperty("nickname", student2.nickname); + expect(updated_student).toHaveProperty("alumni", student2.alumni); +}); + +it('should delete the student based upon student id', async () => { + const deleted_student = await deleteStudent(student2.studentId); + expect(deleted_student).toHaveProperty("student_id", student2.studentId); + expect(deleted_student).toHaveProperty("gender", student2.gender); + expect(deleted_student).toHaveProperty("pronouns", student2.pronouns); + expect(deleted_student).toHaveProperty("phone_number", student2.phoneNumber); + expect(deleted_student).toHaveProperty("nickname", student2.nickname); + expect(deleted_student).toHaveProperty("alumni", student2.alumni); +}); From f141e36fdf9effffa41ddffb3b5370549baf3fef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 03:34:55 +0000 Subject: [PATCH 096/827] npm-frontend(deps-dev): bump eslint-config-next in /frontend Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 12.1.3 to 12.1.4. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v12.1.4/packages/eslint-config-next) --- updated-dependencies: - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 30 +++++++++++++++--------------- frontend/package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c720488a..e63eb6d6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,7 +20,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.3", + "eslint-config-next": "12.1.4", "typedoc": "^0.22.12", "typescript": "4.6.3" } @@ -96,9 +96,9 @@ "integrity": "sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg==" }, "node_modules/@next/eslint-plugin-next": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.3.tgz", - "integrity": "sha512-PYYqnovABbnbKfk1yYiN9Jl5AHGOLb47EcGbJSXJt2nryFW34icOLKkXKIU5jgz700h/31EI0JH+rpQ5b4h9jw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.4.tgz", + "integrity": "sha512-BRy565KVK6Cdy8LHaHTiwctLqBu/RT84RLpESug70BDJzBlV8QBvODyx/j7wGhvYqp9kvstM05lyb6JaTkSCcQ==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -982,12 +982,12 @@ } }, "node_modules/eslint-config-next": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.3.tgz", - "integrity": "sha512-aLtr2VfdvkpbXnqH0Do9HJfMeDq0ZkKlZ8h8g2T88njbE8/5KfJIQSoNnp18M8PUXZYD8kqTOUa1MJ0QU9T1hQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.4.tgz", + "integrity": "sha512-Uj0jrVjoQbg9qerxRjSHoOOv3PEzoZxpb8G9LYct25fsflP8xIiUq0l4WEu2KSB5owuLv5hie7wSMqPEsHj+bQ==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "12.1.3", + "@next/eslint-plugin-next": "12.1.4", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", @@ -3126,9 +3126,9 @@ "integrity": "sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg==" }, "@next/eslint-plugin-next": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.3.tgz", - "integrity": "sha512-PYYqnovABbnbKfk1yYiN9Jl5AHGOLb47EcGbJSXJt2nryFW34icOLKkXKIU5jgz700h/31EI0JH+rpQ5b4h9jw==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.4.tgz", + "integrity": "sha512-BRy565KVK6Cdy8LHaHTiwctLqBu/RT84RLpESug70BDJzBlV8QBvODyx/j7wGhvYqp9kvstM05lyb6JaTkSCcQ==", "dev": true, "requires": { "glob": "7.1.7" @@ -3716,12 +3716,12 @@ } }, "eslint-config-next": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.3.tgz", - "integrity": "sha512-aLtr2VfdvkpbXnqH0Do9HJfMeDq0ZkKlZ8h8g2T88njbE8/5KfJIQSoNnp18M8PUXZYD8kqTOUa1MJ0QU9T1hQ==", + "version": "12.1.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.4.tgz", + "integrity": "sha512-Uj0jrVjoQbg9qerxRjSHoOOv3PEzoZxpb8G9LYct25fsflP8xIiUq0l4WEu2KSB5owuLv5hie7wSMqPEsHj+bQ==", "dev": true, "requires": { - "@next/eslint-plugin-next": "12.1.3", + "@next/eslint-plugin-next": "12.1.4", "@rushstack/eslint-patch": "1.0.8", "@typescript-eslint/parser": "5.10.1", "eslint-import-resolver-node": "0.3.4", diff --git a/frontend/package.json b/frontend/package.json index e967cd0e..df7600e1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,7 @@ "@types/react": "17.0.43", "cross-env": "^7.0.3", "eslint": "8.12.0", - "eslint-config-next": "12.1.3", + "eslint-config-next": "12.1.4", "typedoc": "^0.22.12", "typescript": "4.6.3" } From 2d82314d7bdf5f601e571d0af37c888e1b56a5d6 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Fri, 1 Apr 2022 17:42:30 +0200 Subject: [PATCH 097/827] test: write all integration tests for job_application --- backend/orm_functions/job_application.ts | 4 +- backend/orm_functions/orm_types.ts | 2 +- .../orm_integration/integration_setup.ts | 71 +++- .../orm_integration/job_application.test.ts | 324 +++++++++++++++++- backend/tests/orm_integration/person.test.ts | 76 ++-- backend/tests/orm_integration/role.test.ts | 4 +- backend/tests/orm_integration/student.test.ts | 14 +- .../tests/orm_tests/job_application.test.ts | 2 +- 8 files changed, 435 insertions(+), 62 deletions(-) diff --git a/backend/orm_functions/job_application.ts b/backend/orm_functions/job_application.ts index 7fcaf908..5becc980 100644 --- a/backend/orm_functions/job_application.ts +++ b/backend/orm_functions/job_application.ts @@ -180,14 +180,14 @@ export async function createJobApplication(jobApplication: CreateJobApplication) edu_year: jobApplication.eduYear, edu_institute: jobApplication.eduInstitute, email_status: jobApplication.emailStatus, - created_at: jobApplication.created_at + created_at: new Date(jobApplication.createdAt) } }); } /** * - * @param studentId: the student who's job applications we are looking for + * @param studentId: the student whose job applications we are looking for * @returns the found job applications of the given student */ export async function getLatestJobApplicationOfStudent(studentId: number) { diff --git a/backend/orm_functions/orm_types.ts b/backend/orm_functions/orm_types.ts index 047041f6..34dfa10e 100644 --- a/backend/orm_functions/orm_types.ts +++ b/backend/orm_functions/orm_types.ts @@ -313,7 +313,7 @@ export interface CreateJobApplication { /** * keeps track of when we received this application (used to pick the latest one) */ - created_at: string // this has to be a timezone formatted string: eg '2022-03-14 23:10:00+01' + createdAt: string // this has to be a timezone formatted string: eg '2022-03-14 23:10:00+01' } /** diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 75e74d5f..21be98d8 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -19,6 +19,11 @@ beforeAll(async () => { email: 'testmail2@mail.com', firstname: "first", lastname: "last" + }, + { + email: 'studentmail@mail.com', + firstname: 'student', + lastname: 'student' } ], }); @@ -104,7 +109,14 @@ beforeAll(async () => { pronouns: ["She", "Her"], phone_number: "107", alumni: true, - } + }, + { + person_id: persons[3].person_id, + gender: "Female", + pronouns: ["She", "Her"], + phone_number: "111", + alumni: false + } ] }) const students = await prisma.student.findMany(); @@ -123,7 +135,7 @@ beforeAll(async () => { edu_level: "higher education", edu_duration: 5, edu_institute: "Ugent", - edu_year: 2022, + edu_year: 4, email_status: email_status_enum.DRAFT, created_at: new Date("December 17, 2021 14:24:00"), }, @@ -138,9 +150,39 @@ beforeAll(async () => { edu_level: "MaNaMa", edu_duration: 8, edu_institute: "Ugent", - edu_year: 2023, + edu_year: 7, email_status: email_status_enum.SENT, created_at: new Date("December 20, 2021 03:24:00"), + }, + { + student_id: students[2].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "no responsibilities", + fun_fact: "this is a fun fact", + student_coach: false, + osoc_id: osocs[0].osoc_id, + edus: ["something something"], + edu_level: "higher education", + edu_duration: 5, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.DRAFT, + created_at: new Date("December 25, 2021 14:24:00"), + }, + { + student_id: students[2].student_id, + student_volunteer_info: "I'd like to volunteer", + responsibilities: "no responsibilities2", + fun_fact: "this is a fun fact too", + student_coach: true, + osoc_id: osocs[1].osoc_id, + edus: ["higher education"], + edu_level: "MaNaMa", + edu_duration: 8, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.SENT, + created_at: new Date("December 31, 2021 03:24:00") } ] }); @@ -177,6 +219,12 @@ beforeAll(async () => { { name: "Marketeer" }, + { + name: "Frontend" + }, + { + name: "Backend" + } ], }); @@ -200,6 +248,23 @@ beforeAll(async () => { ], }); } + const roles = await prisma.role.findMany(); + await prisma.applied_role.createMany({ + data: [ + { + job_application_id: job_applications[0].job_application_id, + role_id: roles[2].role_id + }, + { + job_application_id: job_applications[1].job_application_id, + role_id: roles[2].role_id + }, + { + job_application_id: job_applications[1].job_application_id, + role_id: roles[3].role_id + } + ] + }) // create languages diff --git a/backend/tests/orm_integration/job_application.test.ts b/backend/tests/orm_integration/job_application.test.ts index a20f1caf..c6b6089a 100644 --- a/backend/tests/orm_integration/job_application.test.ts +++ b/backend/tests/orm_integration/job_application.test.ts @@ -1,14 +1,17 @@ import { + changeEmailStatusOfJobApplication, createJobApplication, deleteJobApplication, + deleteJobApplicationsFromStudent, getJobApplication, getJobApplicationByYear, + getLatestApplicationRolesForStudent, getLatestJobApplicationOfStudent, getStudentEvaluationsFinal, getStudentEvaluationsTemp, getStudentEvaluationsTotal } from "../../orm_functions/job_application"; import prisma from "../../prisma/prisma"; -import {decision_enum} from "@prisma/client"; +import {decision_enum, email_status_enum} from "@prisma/client"; +import {CreateJobApplication} from "../../orm_functions/orm_types"; it("should return all student evaluations for the student with given id", async () => { - const students = await prisma.student.findMany(); - const osocs = await prisma.osoc.findMany(); + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); const evaluations = [ { @@ -38,8 +41,8 @@ it("should return all student evaluations for the student with given id", async }); it("should return all final student evaluations for the student with given id", async () => { - const students = await prisma.student.findMany(); - const osocs = await prisma.osoc.findMany(); + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); + const evaluations = [ { @@ -63,8 +66,7 @@ it("should return all final student evaluations for the student with given id", }); it("should return all suggestion evaluations for the student with given id", async () => { - const students = await prisma.student.findMany(); - const osocs = await prisma.osoc.findMany(); + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); const evaluations = [ { @@ -86,4 +88,310 @@ it("should return all suggestion evaluations for the student with given id", asy } }); -}); \ No newline at end of file +}); + +it("should return the last roles a student applied for", async () => { + const [students, roles] = await Promise.all([prisma.student.findMany(), prisma.role.findMany()]); + + const roles_expected = [roles[2].role_id, roles[3].role_id] + + const applied_roles = await getLatestApplicationRolesForStudent(students[0].student_id); + if (applied_roles) { + applied_roles.applied_role.forEach(({role_id}, index) => { + expect(role_id).toEqual(roles_expected[index]) + }) + expect(applied_roles.applied_role.length).toEqual(roles_expected.length); + } +}); + +it("should delete all the job applications of the given student", async () => { + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); + + const applics = [ + { + student_id: students[2].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "no responsibilities", + fun_fact: "this is a fun fact", + student_coach: false, + osoc_id: osocs[0].osoc_id, + edus: ["something something"], + edu_level: "higher education", + edu_duration: 5, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.DRAFT, + created_at: new Date("December 25, 2021 14:24:00"), + }, + { + student_id: students[2].student_id, + student_volunteer_info: "I'd like to volunteer", + responsibilities: "no responsibilities2", + fun_fact: "this is a fun fact too", + student_coach: true, + osoc_id: osocs[1].osoc_id, + edus: ["higher education"], + edu_level: "MaNaMa", + edu_duration: 8, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.SENT, + created_at: new Date("December 31, 2021 03:24:00"), + } + ] + + const deleted = await deleteJobApplicationsFromStudent(students[2].student_id); + expect(deleted.count).toEqual(applics.length); + + // add the deleted data back + await prisma.job_application.createMany({ + data : applics + }); +}); + +it("should update the email status of the job application", async () => { + const [students, osocs, applics] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany(), prisma.job_application.findMany()]); + + const applic = { + student_id: students[2].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "no responsibilities", + fun_fact: "this is a fun fact", + student_coach: false, + osoc_id: osocs[0].osoc_id, + edus: ["something something"], + edu_level: "higher education", + edu_duration: 5, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.DRAFT, + created_at: new Date("December 25, 2021 14:24:00"), + } + const changed = await changeEmailStatusOfJobApplication(applics[2].job_application_id, email_status_enum.SENT); + expect(changed).toHaveProperty("student_id", applic.student_id); + expect(changed).toHaveProperty("student_volunteer_info", applic.student_volunteer_info); + expect(changed).toHaveProperty("responsibilities", applic.responsibilities); + expect(changed).toHaveProperty("fun_fact", applic.fun_fact); + expect(changed).toHaveProperty("student_coach", applic.student_coach); + expect(changed).toHaveProperty("osoc_id", applic.osoc_id); + expect(changed).toHaveProperty("edus", applic.edus); + expect(changed).toHaveProperty("edu_level", applic.edu_level); + expect(changed).toHaveProperty("edu_duration", applic.edu_duration); + expect(changed).toHaveProperty("edu_institute", applic.edu_institute); + expect(changed).toHaveProperty("edu_year", applic.edu_year); + expect(changed).toHaveProperty("created_at", applic.created_at); + expect(changed).toHaveProperty("email_status", email_status_enum.SENT); + expect(changed).toHaveProperty("job_application_id", applics[2].job_application_id); +}); + +it("should delete the job application", async () => { + const [students, osocs, applics] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany(), prisma.job_application.findMany()]); + + const app = { + student_id: students[2].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "no responsibilities", + fun_fact: "this is a fun fact", + student_coach: false, + osoc_id: osocs[0].osoc_id, + edus: ["something something"], + edu_level: "higher education", + edu_duration: 5, + edu_institute: "Ugent", + edu_year: 3, + email_status: email_status_enum.SENT, + created_at: new Date("December 25, 2021 14:24:00"), + } + + const deleted = await deleteJobApplication(applics[3].job_application_id); + expect(deleted).toHaveProperty("student_id", app.student_id); + expect(deleted).toHaveProperty("student_volunteer_info", app.student_volunteer_info); + expect(deleted).toHaveProperty("responsibilities", app.responsibilities); + expect(deleted).toHaveProperty("fun_fact", app.fun_fact); + expect(deleted).toHaveProperty("student_coach", app.student_coach); + expect(deleted).toHaveProperty("osoc_id", app.osoc_id); + expect(deleted).toHaveProperty("edus", app.edus); + expect(deleted).toHaveProperty("edu_level", app.edu_level); + expect(deleted).toHaveProperty("edu_duration", app.edu_duration); + expect(deleted).toHaveProperty("edu_institute", app.edu_institute); + expect(deleted).toHaveProperty("edu_year", app.edu_year); + expect(deleted).toHaveProperty("created_at", app.created_at); + expect(deleted).toHaveProperty("email_status", app.email_status); + + await prisma.job_application.create({ + data: app + }); +}); + +it("should create a new job_application", async () => { + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); + + const app = { + student_id: students[2].student_id, + student_volunteer_info: "no volunteer", + responsibilities: "nothing", + fun_fact: "this is a fun fact", + student_coach: true, + osoc_id: osocs[0].osoc_id, + edus: ["something edu"], + edu_level: "higher education", + edu_duration: 3, + edu_institute: "Hogent", + edu_year: 2, + email_status: email_status_enum.DRAFT, + created_at: new Date("January 2, 2022 14:24:00"), + } + + const input: CreateJobApplication = { + studentId: students[2].student_id, + studentVolunteerInfo: "no volunteer", + responsibilities: "nothing", + funFact: "this is a fun fact", + studentCoach: true, + osocId: osocs[0].osoc_id, + edus: ["something edu"], + eduLevel: "higher education", + eduDuration: 3, + eduInstitute: "Hogent", + eduYear: 2, + emailStatus: email_status_enum.DRAFT, + createdAt: "January 2, 2022 14:24:00", + } + + const created = await createJobApplication(input); + expect(created).toHaveProperty("student_id", app.student_id); + expect(created).toHaveProperty("student_volunteer_info", app.student_volunteer_info); + expect(created).toHaveProperty("responsibilities", app.responsibilities); + expect(created).toHaveProperty("fun_fact", app.fun_fact); + expect(created).toHaveProperty("student_coach", app.student_coach); + expect(created).toHaveProperty("osoc_id", app.osoc_id); + expect(created).toHaveProperty("edus", app.edus); + expect(created).toHaveProperty("edu_level", app.edu_level); + expect(created).toHaveProperty("edu_duration", app.edu_duration); + expect(created).toHaveProperty("edu_institute", app.edu_institute); + expect(created).toHaveProperty("edu_year", app.edu_year); + expect(created).toHaveProperty("created_at", app.created_at); + expect(created).toHaveProperty("email_status", app.email_status); + + await deleteJobApplication(created.job_application_id); +}); + +it("should return the most recent job application of a student", async () => { + const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); + + const expected = { + student_id: students[0].student_id, + student_volunteer_info: "I'd like to volunteer", + responsibilities: "no responsibilities2", + fun_fact: "this is a fun fact too", + student_coach: true, + osoc_id: osocs[0].osoc_id, + edus: ["higher education"], + edu_level: "MaNaMa", + edu_duration: 8, + edu_institute: "Ugent", + edu_year: 7, + email_status: email_status_enum.SENT, + created_at: new Date("December 20, 2021 03:24:00"), + } + + const found = await getLatestJobApplicationOfStudent(expected.student_id); + + const attachments = await prisma.attachment.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + const applied_roles = await prisma.applied_role.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + const job_application_skill = await prisma.job_application_skill.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + + expect(found).toHaveProperty("student_id", expected.student_id); + expect(found).toHaveProperty("student_volunteer_info", expected.student_volunteer_info); + expect(found).toHaveProperty("responsibilities", expected.responsibilities); + expect(found).toHaveProperty("fun_fact", expected.fun_fact); + expect(found).toHaveProperty("student_coach", expected.student_coach); + expect(found).toHaveProperty("osoc_id", expected.osoc_id); + expect(found).toHaveProperty("edus", expected.edus); + expect(found).toHaveProperty("edu_level", expected.edu_level); + expect(found).toHaveProperty("edu_duration", expected.edu_duration); + expect(found).toHaveProperty("edu_institute", expected.edu_institute); + expect(found).toHaveProperty("edu_year", expected.edu_year); + expect(found).toHaveProperty("created_at", expected.created_at); + expect(found).toHaveProperty("email_status", expected.email_status); + expect(found).toHaveProperty("attachment", attachments); + expect(found).toHaveProperty("job_application_skill", job_application_skill); + expect(found).toHaveProperty("applied_role", applied_roles); +}); + +it("should return the job application", async () => { + const applications = await prisma.job_application.findMany(); + + const expected = applications[0] + + + const found = await getJobApplication(applications[0].job_application_id); + + const attachments = await prisma.attachment.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + const applied_roles = await prisma.applied_role.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + const job_application_skill = await prisma.job_application_skill.findMany({ + where: { + job_application_id: found?.job_application_id + } + }); + + expect(found).toHaveProperty("student_id", expected.student_id); + expect(found).toHaveProperty("student_volunteer_info", expected.student_volunteer_info); + expect(found).toHaveProperty("responsibilities", expected.responsibilities); + expect(found).toHaveProperty("fun_fact", expected.fun_fact); + expect(found).toHaveProperty("student_coach", expected.student_coach); + expect(found).toHaveProperty("osoc_id", expected.osoc_id); + expect(found).toHaveProperty("edus", expected.edus); + expect(found).toHaveProperty("edu_level", expected.edu_level); + expect(found).toHaveProperty("edu_duration", expected.edu_duration); + expect(found).toHaveProperty("edu_institute", expected.edu_institute); + expect(found).toHaveProperty("edu_year", expected.edu_year); + expect(found).toHaveProperty("created_at", expected.created_at); + expect(found).toHaveProperty("email_status", expected.email_status); + expect(found).toHaveProperty("attachment", attachments); + expect(found).toHaveProperty("job_application_skill", job_application_skill); + expect(found).toHaveProperty("applied_role", applied_roles); +}); + +it("should return all job applications of a year", async () => { + + const found = await getJobApplicationByYear(2022); + + // just check if all "extra" fields are there + found.forEach((app) => { + expect(app).toHaveProperty("attachment"); + expect(app.attachment).toBeTruthy(); + expect(app).toHaveProperty("job_application_skill"); + expect(app.job_application_skill).toBeTruthy(); + expect(app).toHaveProperty("applied_role"); + expect(app.applied_role).toBeTruthy(); + }); + + // only 3 applications for the given osoc edition + expect(found.length).toEqual(3); +}); diff --git a/backend/tests/orm_integration/person.test.ts b/backend/tests/orm_integration/person.test.ts index 73f526f0..910a5b11 100644 --- a/backend/tests/orm_integration/person.test.ts +++ b/backend/tests/orm_integration/person.test.ts @@ -3,15 +3,15 @@ import {createPerson, getAllPersons, searchPersonByName, searchPersonByLogin, updatePerson, deletePersonById} from "../../orm_functions/person"; -const person3 : CreatePerson = { +const person4 : CreatePerson = { email: "test@email.be", firstname: "first_name", lastname: "last_name", } -const person4: CreatePerson = { +const person5: CreatePerson = { github: "testhub.com", - firstname: "person4", + firstname: "person5", lastname: "second name", } @@ -32,7 +32,7 @@ it('should create 1 new person where github is null', async () => { it('should create 1 new person where email is null', async () => { const person1: CreatePerson = { github: "testhub.com", - firstname: "person4", + firstname: "person5", lastname: "second name", } @@ -45,64 +45,64 @@ it('should create 1 new person where email is null', async () => { it('should find all the persons in the db, 2 in total', async () => { const searched_persons = await getAllPersons(); - expect(searched_persons[3]).toHaveProperty("github", null); - expect(searched_persons[3]).toHaveProperty("firstname", person3.firstname); - expect(searched_persons[3]).toHaveProperty("lastname", person3.lastname); - expect(searched_persons[3]).toHaveProperty("email", person3.email); - - expect(searched_persons[4]).toHaveProperty("github", person4.github); + expect(searched_persons[4]).toHaveProperty("github", null); expect(searched_persons[4]).toHaveProperty("firstname", person4.firstname); expect(searched_persons[4]).toHaveProperty("lastname", person4.lastname); - expect(searched_persons[4]).toHaveProperty("email", null); + expect(searched_persons[4]).toHaveProperty("email", person4.email); + + expect(searched_persons[5]).toHaveProperty("github", person5.github); + expect(searched_persons[5]).toHaveProperty("firstname", person5.firstname); + expect(searched_persons[5]).toHaveProperty("lastname", person5.lastname); + expect(searched_persons[5]).toHaveProperty("email", null); }); // Can only be tested with a login user, should therefore be tested in the login user tests? /*it('should find person 1 in the db, by searching for its email', async () => { - const searched_person = await getPasswordPersonByEmail(person3.email!); - expect(searched_person).toHaveProperty("github", person3.github); - expect(searched_person).toHaveProperty("firstname", person3.firstname); - expect(searched_person).toHaveProperty("lastname", person3.lastname); - expect(searched_person).toHaveProperty("email", person3.email); + const searched_person = await getPasswordPersonByEmail(person4.email!); + expect(searched_person).toHaveProperty("github", person4.github); + expect(searched_person).toHaveProperty("firstname", person4.firstname); + expect(searched_person).toHaveProperty("lastname", person4.lastname); + expect(searched_person).toHaveProperty("email", person4.email); });*/ it('should find person 1 in the db, by searching for its firstname', async () => { - const searched_person = await searchPersonByName(person3.firstname); + const searched_person = await searchPersonByName(person4.firstname); expect(searched_person[0]).toHaveProperty("github", null); - expect(searched_person[0]).toHaveProperty("firstname", person3.firstname); - expect(searched_person[0]).toHaveProperty("lastname", person3.lastname); - expect(searched_person[0]).toHaveProperty("email", person3.email); + expect(searched_person[0]).toHaveProperty("firstname", person4.firstname); + expect(searched_person[0]).toHaveProperty("lastname", person4.lastname); + expect(searched_person[0]).toHaveProperty("email", person4.email); }); it('should find person 2 in the db, by searching for its lastname', async () => { - const searched_person4 = await searchPersonByName(person4.lastname); - expect(searched_person4[0]).toHaveProperty("github", person4.github); - expect(searched_person4[0]).toHaveProperty("firstname", person4.firstname); - expect(searched_person4[0]).toHaveProperty("lastname", person4.lastname); + const searched_person4 = await searchPersonByName(person5.lastname); + expect(searched_person4[0]).toHaveProperty("github", person5.github); + expect(searched_person4[0]).toHaveProperty("firstname", person5.firstname); + expect(searched_person4[0]).toHaveProperty("lastname", person5.lastname); expect(searched_person4[0]).toHaveProperty("email", null); }); it('should find all the persons in the db with given email, 1 in total', async () => { - if (person3.email != undefined) { - const searched_persons = await searchPersonByLogin(person3.email); + if (person4.email != undefined) { + const searched_persons = await searchPersonByLogin(person4.email); expect(searched_persons[0]).toHaveProperty("github", null); - expect(searched_persons[0]).toHaveProperty("firstname", person3.firstname); - expect(searched_persons[0]).toHaveProperty("lastname", person3.lastname); - expect(searched_persons[0]).toHaveProperty("email", person3.email); + expect(searched_persons[0]).toHaveProperty("firstname", person4.firstname); + expect(searched_persons[0]).toHaveProperty("lastname", person4.lastname); + expect(searched_persons[0]).toHaveProperty("email", person4.email); } }); it('should find all the persons in the db with given github, 1 in total', async () => { - if (person4.github) { - const searched_persons = await searchPersonByLogin(person4.github); - expect(searched_persons[0]).toHaveProperty("github", person4.github); - expect(searched_persons[0]).toHaveProperty("firstname", person4.firstname); - expect(searched_persons[0]).toHaveProperty("lastname", person4.lastname); + if (person5.github) { + const searched_persons = await searchPersonByLogin(person5.github); + expect(searched_persons[0]).toHaveProperty("github", person5.github); + expect(searched_persons[0]).toHaveProperty("firstname", person5.firstname); + expect(searched_persons[0]).toHaveProperty("lastname", person5.lastname); expect(searched_persons[0]).toHaveProperty("email", null); } }); it('should update person based upon personid', async () => { - const searched_person3 = await searchPersonByName(person3.firstname); + const searched_person3 = await searchPersonByName(person4.firstname); const personUpdate: UpdatePerson = { personId: searched_person3[0].person_id, email: "new@email.be", @@ -117,9 +117,9 @@ it('should update person based upon personid', async () => { }); it('should delete the person based upon personid', async () => { - const searched_person4 = await searchPersonByName(person4.lastname); - const deleted_person = await deletePersonById(searched_person4[0].person_id); - expect(deleted_person).toHaveProperty("person_id", searched_person4[0].person_id); + const searched_person5 = await searchPersonByName(person5.lastname); + const deleted_person = await deletePersonById(searched_person5[0].person_id); + expect(deleted_person).toHaveProperty("person_id", searched_person5[0].person_id); expect(deleted_person).toHaveProperty("github", deleted_person.github); expect(deleted_person).toHaveProperty("firstname", deleted_person.firstname); expect(deleted_person).toHaveProperty("lastname", deleted_person.lastname); diff --git a/backend/tests/orm_integration/role.test.ts b/backend/tests/orm_integration/role.test.ts index 0c3ddd83..df51f224 100644 --- a/backend/tests/orm_integration/role.test.ts +++ b/backend/tests/orm_integration/role.test.ts @@ -24,8 +24,8 @@ it('should create 1 new role where', async () => { it('should find all the roles in the db, 3 in total', async () => { const searched_roles = await getAllRoles(); - expect(searched_roles.length).toEqual(3); - expect(searched_roles[2]).toHaveProperty("name", role1.name); + expect(searched_roles.length).toEqual(5); + expect(searched_roles[4]).toHaveProperty("name", role1.name); }); diff --git a/backend/tests/orm_integration/student.test.ts b/backend/tests/orm_integration/student.test.ts index eac3d7a1..1f93e610 100644 --- a/backend/tests/orm_integration/student.test.ts +++ b/backend/tests/orm_integration/student.test.ts @@ -47,13 +47,13 @@ it('should create 1 new student', async () => { it('should find all the students in the db, 3 in total', async () => { const searched_students = await getAllStudents(); - expect(searched_students.length).toEqual(3); - expect(searched_students[2]).toHaveProperty("student_id", student1.studentId); - expect(searched_students[2]).toHaveProperty("gender", student1.gender); - expect(searched_students[2]).toHaveProperty("pronouns", student1.pronouns); - expect(searched_students[2]).toHaveProperty("phone_number", student1.phoneNumber); - expect(searched_students[2]).toHaveProperty("nickname", student1.nickname); - expect(searched_students[2]).toHaveProperty("alumni", student1.alumni); + expect(searched_students.length).toEqual(4); + expect(searched_students[3]).toHaveProperty("student_id", student1.studentId); + expect(searched_students[3]).toHaveProperty("gender", student1.gender); + expect(searched_students[3]).toHaveProperty("pronouns", student1.pronouns); + expect(searched_students[3]).toHaveProperty("phone_number", student1.phoneNumber); + expect(searched_students[3]).toHaveProperty("nickname", student1.nickname); + expect(searched_students[3]).toHaveProperty("alumni", student1.alumni); }); it('should return the student, by searching for its id', async () => { diff --git a/backend/tests/orm_tests/job_application.test.ts b/backend/tests/orm_tests/job_application.test.ts index c545426d..9e85e610 100644 --- a/backend/tests/orm_tests/job_application.test.ts +++ b/backend/tests/orm_tests/job_application.test.ts @@ -69,7 +69,7 @@ test("should delete job application", async () => { test("should create a job application", async () => { const jobApplicaton: CreateJobApplication = { - created_at: "", + createdAt: "", eduDuration: 5, eduInstitute: "ugent", eduLevel: "good", From 63b468029b3b634af90fdbc5890fe869a2bd0ba5 Mon Sep 17 00:00:00 2001 From: Jonathan Vanbrabant Date: Sat, 2 Apr 2022 10:12:19 +0200 Subject: [PATCH 098/827] fix: can't seem to get 'GET student/all' to work --- frontend/pages/students.tsx | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/frontend/pages/students.tsx b/frontend/pages/students.tsx index 9b2a5bf0..43f81fa4 100644 --- a/frontend/pages/students.tsx +++ b/frontend/pages/students.tsx @@ -1,20 +1,31 @@ import {NextPage} from "next"; import {Header} from "../components/Header/Header"; import {StudentCard} from "../components/StudentCard/StudentCard"; -import { GetStaticProps } from 'next'; +import SessionContext from "../contexts/sessionProvider"; +import {useContext, useEffect} from "react"; +const Students: NextPage = () => { + const {sessionKey} = useContext(SessionContext); + //const [students, setStudents] = useState>([]); -export const getStaticProps: GetStaticProps = async () => { - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`); - const students = (await res.json()); - console.log(students); - return { - props: students - } -} + useEffect(() => { + const fetchData = async () => { + //console.log(sessionKey); + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`, { + method: 'GET', + body: JSON.stringify({sessionkey: sessionKey}), //TODO Autherize the user + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + }); + console.log(response) + } -const Students: NextPage = () => { + fetchData(); + }); + return ( <>
From c1a01c23d9a168fe93f4a2c6bcb1752ece4dee69 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Sat, 2 Apr 2022 11:23:35 +0200 Subject: [PATCH 099/827] test: code cleanup of job_application integration tests --- .../orm_integration/job_application.test.ts | 212 +++++++++--------- 1 file changed, 102 insertions(+), 110 deletions(-) diff --git a/backend/tests/orm_integration/job_application.test.ts b/backend/tests/orm_integration/job_application.test.ts index c6b6089a..05f9bd4a 100644 --- a/backend/tests/orm_integration/job_application.test.ts +++ b/backend/tests/orm_integration/job_application.test.ts @@ -10,6 +10,58 @@ import prisma from "../../prisma/prisma"; import {decision_enum, email_status_enum} from "@prisma/client"; import {CreateJobApplication} from "../../orm_functions/orm_types"; +/** + * aid function to compare most fields of the expected job application and the found job application + * @param expected the application we expect as response from the database + * @param found the actual response from the database + */ +function job_application_check(expected: { + job_application_id?: number; + student_id: number; + student_volunteer_info: string; + responsibilities: string | null; + fun_fact: string | null; + student_coach: boolean; + osoc_id: number; + edus: string[]; + edu_level: string | null; + edu_duration: number | null; + edu_institute: string | null; + edu_year: number | null; + created_at: Date; + email_status: email_status_enum; + }, + found: { + job_application_id: number + student_id: number; + student_volunteer_info: string; + responsibilities: string | null; + fun_fact: string | null; + student_coach: boolean; + osoc_id: number; + edus: string[]; + edu_level: string | null; + edu_duration: number | null; + edu_institute: string | null; + edu_year: number | null; + created_at: Date; + email_status: email_status_enum; + } +) { + expect(found).toHaveProperty("student_id", expected.student_id); + expect(found).toHaveProperty("student_volunteer_info", expected.student_volunteer_info); + expect(found).toHaveProperty("responsibilities", expected.responsibilities); + expect(found).toHaveProperty("fun_fact", expected.fun_fact); + expect(found).toHaveProperty("student_coach", expected.student_coach); + expect(found).toHaveProperty("osoc_id", expected.osoc_id); + expect(found).toHaveProperty("edus", expected.edus); + expect(found).toHaveProperty("edu_level", expected.edu_level); + expect(found).toHaveProperty("edu_duration", expected.edu_duration); + expect(found).toHaveProperty("edu_institute", expected.edu_institute); + expect(found).toHaveProperty("edu_year", expected.edu_year); + expect(found).toHaveProperty("created_at", expected.created_at); +} + it("should return all student evaluations for the student with given id", async () => { const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); @@ -145,7 +197,7 @@ it("should delete all the job applications of the given student", async () => { // add the deleted data back await prisma.job_application.createMany({ - data : applics + data: applics }); }); @@ -168,22 +220,12 @@ it("should update the email status of the job application", async () => { created_at: new Date("December 25, 2021 14:24:00"), } const changed = await changeEmailStatusOfJobApplication(applics[2].job_application_id, email_status_enum.SENT); - expect(changed).toHaveProperty("student_id", applic.student_id); - expect(changed).toHaveProperty("student_volunteer_info", applic.student_volunteer_info); - expect(changed).toHaveProperty("responsibilities", applic.responsibilities); - expect(changed).toHaveProperty("fun_fact", applic.fun_fact); - expect(changed).toHaveProperty("student_coach", applic.student_coach); - expect(changed).toHaveProperty("osoc_id", applic.osoc_id); - expect(changed).toHaveProperty("edus", applic.edus); - expect(changed).toHaveProperty("edu_level", applic.edu_level); - expect(changed).toHaveProperty("edu_duration", applic.edu_duration); - expect(changed).toHaveProperty("edu_institute", applic.edu_institute); - expect(changed).toHaveProperty("edu_year", applic.edu_year); - expect(changed).toHaveProperty("created_at", applic.created_at); + job_application_check(applic, changed); expect(changed).toHaveProperty("email_status", email_status_enum.SENT); expect(changed).toHaveProperty("job_application_id", applics[2].job_application_id); }); + it("should delete the job application", async () => { const [students, osocs, applics] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany(), prisma.job_application.findMany()]); @@ -204,20 +246,8 @@ it("should delete the job application", async () => { } const deleted = await deleteJobApplication(applics[3].job_application_id); - expect(deleted).toHaveProperty("student_id", app.student_id); - expect(deleted).toHaveProperty("student_volunteer_info", app.student_volunteer_info); - expect(deleted).toHaveProperty("responsibilities", app.responsibilities); - expect(deleted).toHaveProperty("fun_fact", app.fun_fact); - expect(deleted).toHaveProperty("student_coach", app.student_coach); - expect(deleted).toHaveProperty("osoc_id", app.osoc_id); - expect(deleted).toHaveProperty("edus", app.edus); - expect(deleted).toHaveProperty("edu_level", app.edu_level); - expect(deleted).toHaveProperty("edu_duration", app.edu_duration); - expect(deleted).toHaveProperty("edu_institute", app.edu_institute); - expect(deleted).toHaveProperty("edu_year", app.edu_year); - expect(deleted).toHaveProperty("created_at", app.created_at); + job_application_check(app, deleted); expect(deleted).toHaveProperty("email_status", app.email_status); - await prisma.job_application.create({ data: app }); @@ -243,39 +273,52 @@ it("should create a new job_application", async () => { } const input: CreateJobApplication = { - studentId: students[2].student_id, - studentVolunteerInfo: "no volunteer", - responsibilities: "nothing", - funFact: "this is a fun fact", - studentCoach: true, - osocId: osocs[0].osoc_id, - edus: ["something edu"], - eduLevel: "higher education", - eduDuration: 3, - eduInstitute: "Hogent", - eduYear: 2, - emailStatus: email_status_enum.DRAFT, - createdAt: "January 2, 2022 14:24:00", + studentId: students[2].student_id, + studentVolunteerInfo: "no volunteer", + responsibilities: "nothing", + funFact: "this is a fun fact", + studentCoach: true, + osocId: osocs[0].osoc_id, + edus: ["something edu"], + eduLevel: "higher education", + eduDuration: 3, + eduInstitute: "Hogent", + eduYear: 2, + emailStatus: email_status_enum.DRAFT, + createdAt: "January 2, 2022 14:24:00", } const created = await createJobApplication(input); - expect(created).toHaveProperty("student_id", app.student_id); - expect(created).toHaveProperty("student_volunteer_info", app.student_volunteer_info); - expect(created).toHaveProperty("responsibilities", app.responsibilities); - expect(created).toHaveProperty("fun_fact", app.fun_fact); - expect(created).toHaveProperty("student_coach", app.student_coach); - expect(created).toHaveProperty("osoc_id", app.osoc_id); - expect(created).toHaveProperty("edus", app.edus); - expect(created).toHaveProperty("edu_level", app.edu_level); - expect(created).toHaveProperty("edu_duration", app.edu_duration); - expect(created).toHaveProperty("edu_institute", app.edu_institute); - expect(created).toHaveProperty("edu_year", app.edu_year); - expect(created).toHaveProperty("created_at", app.created_at); + job_application_check(app, created); expect(created).toHaveProperty("email_status", app.email_status); await deleteJobApplication(created.job_application_id); }); +/** + * aid function to get some data used in the tests + * + */ +function getDataAssociatedWithApplication(job_application_id: number | undefined) { + return Promise.all([ + prisma.attachment.findMany({ + where: { + job_application_id: job_application_id + } + }), + prisma.applied_role.findMany({ + where: { + job_application_id: job_application_id + } + }), + prisma.job_application_skill.findMany({ + where: { + job_application_id: job_application_id + } + }) + ]); +} + it("should return the most recent job application of a student", async () => { const [students, osocs] = await Promise.all([prisma.student.findMany(), prisma.osoc.findMany()]); @@ -297,37 +340,11 @@ it("should return the most recent job application of a student", async () => { const found = await getLatestJobApplicationOfStudent(expected.student_id); - const attachments = await prisma.attachment.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); - - const applied_roles = await prisma.applied_role.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); + const [attachments, applied_roles, job_application_skill] = await getDataAssociatedWithApplication(found?.job_application_id) - const job_application_skill = await prisma.job_application_skill.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); - - - expect(found).toHaveProperty("student_id", expected.student_id); - expect(found).toHaveProperty("student_volunteer_info", expected.student_volunteer_info); - expect(found).toHaveProperty("responsibilities", expected.responsibilities); - expect(found).toHaveProperty("fun_fact", expected.fun_fact); - expect(found).toHaveProperty("student_coach", expected.student_coach); - expect(found).toHaveProperty("osoc_id", expected.osoc_id); - expect(found).toHaveProperty("edus", expected.edus); - expect(found).toHaveProperty("edu_level", expected.edu_level); - expect(found).toHaveProperty("edu_duration", expected.edu_duration); - expect(found).toHaveProperty("edu_institute", expected.edu_institute); - expect(found).toHaveProperty("edu_year", expected.edu_year); - expect(found).toHaveProperty("created_at", expected.created_at); + if (found) { + job_application_check(expected, found); + } expect(found).toHaveProperty("email_status", expected.email_status); expect(found).toHaveProperty("attachment", attachments); expect(found).toHaveProperty("job_application_skill", job_application_skill); @@ -342,36 +359,11 @@ it("should return the job application", async () => { const found = await getJobApplication(applications[0].job_application_id); - const attachments = await prisma.attachment.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); - - const applied_roles = await prisma.applied_role.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); + const [attachments, applied_roles, job_application_skill] = await getDataAssociatedWithApplication(found?.job_application_id) - const job_application_skill = await prisma.job_application_skill.findMany({ - where: { - job_application_id: found?.job_application_id - } - }); - - expect(found).toHaveProperty("student_id", expected.student_id); - expect(found).toHaveProperty("student_volunteer_info", expected.student_volunteer_info); - expect(found).toHaveProperty("responsibilities", expected.responsibilities); - expect(found).toHaveProperty("fun_fact", expected.fun_fact); - expect(found).toHaveProperty("student_coach", expected.student_coach); - expect(found).toHaveProperty("osoc_id", expected.osoc_id); - expect(found).toHaveProperty("edus", expected.edus); - expect(found).toHaveProperty("edu_level", expected.edu_level); - expect(found).toHaveProperty("edu_duration", expected.edu_duration); - expect(found).toHaveProperty("edu_institute", expected.edu_institute); - expect(found).toHaveProperty("edu_year", expected.edu_year); - expect(found).toHaveProperty("created_at", expected.created_at); + if (found) { + job_application_check(expected, found) + } expect(found).toHaveProperty("email_status", expected.email_status); expect(found).toHaveProperty("attachment", attachments); expect(found).toHaveProperty("job_application_skill", job_application_skill); From f8a9ab5beb95aa39866b845507eda5696f90a04f Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Sat, 2 Apr 2022 12:23:33 +0200 Subject: [PATCH 100/827] feat: created routes for student roles --- backend/request.ts | 20 ++++++++++++++++++ backend/routes/student.ts | 43 +++++++++++++++++++++++++++++++++++++++ backend/types.ts | 5 +++++ 3 files changed, 68 insertions(+) diff --git a/backend/request.ts b/backend/request.ts index a44376b6..5accfaac 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -409,6 +409,21 @@ export async function parseFormRequest(req: express.Request): }); } +/** + * Parses a request to `POST /student/role`. + * @param req The request to check. + * @returns A Promise resolving to the parsed data or rejecting with an + * Argument or Unauthenticated error. + */ +export async function parseStudentRoleRequest(req: express.Request): + Promise { + return hasFields(req, [ "name" ], types.neither) + .then(() => Promise.resolve({ + sessionkey : req.body.sessionkey, + name : req.body.name + })); +} + /** * A request to `DELETE /login/` only requires a session key * {@link parseKeyRequest}. @@ -419,6 +434,11 @@ export const parseLogoutRequest = parseKeyRequest; * {@link parseKeyRequest}. */ export const parseStudentAllRequest = parseKeyRequest; +/** + * A request to `GET /roles/all` only requires a session key + * {@link parseKeyRequest}. + */ + export const parseRolesAllRequest = parseKeyRequest; /** * A request to `GET /coach/all` only requires a session key * {@link parseKeyRequest}. diff --git a/backend/routes/student.ts b/backend/routes/student.ts index d2119833..795ec62f 100644 --- a/backend/routes/student.ts +++ b/backend/routes/student.ts @@ -4,6 +4,7 @@ import * as ormEv from '../orm_functions/evaluation'; import * as ormJo from '../orm_functions/job_application'; import * as ormLa from '../orm_functions/language'; import * as ormSt from '../orm_functions/student'; +import * as ormRo from '../orm_functions/role'; import * as rq from '../request'; import {InternalTypes, Responses} from '../types'; import * as util from '../utility'; @@ -300,6 +301,44 @@ async function searchStudents(req: express.Request): return Promise.resolve({data : [], sessionkey : req.body.sessionkey}); } +/** + * Attempts to create a new role in the system. + * @param req The Express.js request to extract all required data from. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ + async function createStudentRole(req: express.Request): + Promise { + return rq.parseStudentRoleRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(async parsed => { + return ormRo + .createProjectRole(parsed.data.name) + .then(role => {return Promise.resolve({ + data : { + name: role.name, + role_id: role.role_id + }, + sessionkey : parsed.data.sessionkey + })}); + }); +} + +/** + * Attempts to list all roles in the system. + * @param req The Express.js request to extract all required data from. + * @returns See the API documentation. Successes are passed using + * `Promise.resolve`, failures using `Promise.reject`. + */ + async function listStudentRoles(req: express.Request): Promise { + return rq.parseRolesAllRequest(req) + .then(parsed => util.checkSessionKey(parsed)) + .then(parsed => { + return ormRo.getAllRoles() + .then((roles) => Promise.resolve({data : roles, sessionkey : parsed.data.sessionkey})); + }); + } + /** * Gets the router for all `/student/` related endpoints. * @returns An Express.js {@link express.Router} routing all `/student/` @@ -324,6 +363,10 @@ export function getRouter(): express.Router { util.route(router, "get", "/search", searchStudents); + util.route(router, "post", "/roles", createStudentRole); + + util.route(router, "get", "roles/all", listStudentRoles); + util.addAllInvalidVerbs( router, [ "/", "/all", "/:id", "/:id/suggest", "/:id/confirm", "/search" ]); diff --git a/backend/types.ts b/backend/types.ts index 1e56adf2..6f78989e 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -501,6 +501,11 @@ export interface Form { eventId: string, eventType: string, createdAt: string, data: DataForm } +export interface Role extends KeyRequest { + name: string +} + + export interface DataForm { fields: Array } From 23ecbdf3bbf3ad956a2d6388c3faf40f63848b8d Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Sat, 2 Apr 2022 12:30:44 +0200 Subject: [PATCH 101/827] fix: the return type of the createStudentRole function in the student-route has been fixed --- backend/routes/student.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/routes/student.ts b/backend/routes/student.ts index 795ec62f..c440863c 100644 --- a/backend/routes/student.ts +++ b/backend/routes/student.ts @@ -308,7 +308,7 @@ async function searchStudents(req: express.Request): * `Promise.resolve`, failures using `Promise.reject`. */ async function createStudentRole(req: express.Request): - Promise { + Promise> { return rq.parseStudentRoleRequest(req) .then(parsed => util.checkSessionKey(parsed)) .then(async parsed => { @@ -317,7 +317,7 @@ async function searchStudents(req: express.Request): .then(role => {return Promise.resolve({ data : { name: role.name, - role_id: role.role_id + id: role.role_id }, sessionkey : parsed.data.sessionkey })}); From ac2b79881bd0c2705130b3937ada0ed43b553b24 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Sat, 2 Apr 2022 13:10:27 +0200 Subject: [PATCH 102/827] test: write integration tests for session key --- .../orm_integration/integration_setup.ts | 13 ++++++++ .../tests/orm_integration/session_key.test.ts | 33 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 backend/tests/orm_integration/session_key.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index 21be98d8..bc543bd5 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -320,6 +320,19 @@ beforeAll(async () => { ], }); + await prisma.session_keys.createMany({ + data: [ + { + login_user_id: login_users[0].login_user_id, + session_key: "key" + }, + { + login_user_id: login_users[0].login_user_id, + session_key: "key2" + } + ] + + }); }); afterAll(async () => { diff --git a/backend/tests/orm_integration/session_key.test.ts b/backend/tests/orm_integration/session_key.test.ts new file mode 100644 index 00000000..421b3bd0 --- /dev/null +++ b/backend/tests/orm_integration/session_key.test.ts @@ -0,0 +1,33 @@ +import prisma from "../../prisma/prisma"; +import {addSessionKey, checkSessionKey} from "../../orm_functions/session_key"; + +it("should create a new session key for the given login user", async () =>{ + const loginUsers = await prisma.login_user.findMany(); + + const newKey = "newKey"; + + const created = await addSessionKey(loginUsers[0].login_user_id, newKey); + + expect(created).toHaveProperty("login_user_id", loginUsers[0].login_user_id); + expect(created).toHaveProperty("session_key", newKey); +}); + +it("should return the loginUser associated with the key", async () => { + const loginUsers = await prisma.login_user.findMany(); + + const found_user = await checkSessionKey("key"); + expect(found_user).toHaveProperty("login_user_id", loginUsers[0].login_user_id); +}); + +it("should return an error because the key doesn't exist", async () => { + try { + await checkSessionKey("doesn't exist"); + // should not get executed because checkSessionKey should throw error because key doesn't exist + expect(false).toBeTruthy() + } catch (e) { + expect(e).toBeInstanceOf(Error); + } +}); + + + From 32e2a2003a3c13e34d5e6b3b8ffe2ad93035960b Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Sat, 2 Apr 2022 13:14:42 +0200 Subject: [PATCH 103/827] docs: add documentation to session_key.ts --- backend/orm_functions/session_key.ts | 71 +++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/backend/orm_functions/session_key.ts b/backend/orm_functions/session_key.ts index 4796d6c2..14569773 100644 --- a/backend/orm_functions/session_key.ts +++ b/backend/orm_functions/session_key.ts @@ -1,29 +1,68 @@ import prisma from '../prisma/prisma'; +/** + * adds session key to loginUser + * + * @param loginUserId the id of the loginUser for whom we are adding a session key + * @param key the new session key + * @returns the new record in the database in a promise + */ export async function addSessionKey(loginUserId: number, key: string) { - const result = await prisma.session_keys.create( - {data : {login_user_id : loginUserId, session_key : key}}); - return result; + return await prisma.session_keys.create({ + data: { + login_user_id: loginUserId, + session_key: key + } + }); } +/** + * checks if the session key exists + * + * @param key: the key whose validity we want to check + * @returns the loginUser associated with the key in a promise if the keys is valid otherwise an error in a promise + */ export async function checkSessionKey(key: string) { - const result = await prisma.session_keys.findUnique({ - where : {session_key : key}, - select : {login_user_id : true}, - rejectOnNotFound : true - }); - return result; + return await prisma.session_keys.findUnique({ + where: { + session_key: key + }, + select: { + login_user_id: true + }, + rejectOnNotFound: true + }); } +/** + * + * @param key: the old key we want to overwrite + * @param newkey: the key that we use to replace the old key + * @returns the updated record in a promise + */ export async function changeSessionKey(key: string, newkey: string) { - const result = await prisma.session_keys.update( - {where : {session_key : key}, data : {session_key : newkey}}); - return result; + return await prisma.session_keys.update({ + where: { + session_key: key + }, + data: { + session_key: newkey + } + }); } +/** + * deletes all session keys of the user that has the given key + * + * @param key a key of a user whose keys we want to delete + * @returns the number of deleted records in a promise + */ export async function removeAllKeysForUser(key: string) { - const result = await checkSessionKey(key).then( - uid => prisma.session_keys.deleteMany( - {where : {login_user_id : uid.login_user_id}})); - return result; + return await checkSessionKey(key).then( + uid => prisma.session_keys.deleteMany({ + where: { + login_user_id: uid.login_user_id + } + }) + ); } From 0223902fac59e0b1042bc1ddfdccc115a293fac4 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Sat, 2 Apr 2022 13:34:34 +0200 Subject: [PATCH 104/827] test: finish writing integration tests for session_key --- .../tests/orm_integration/session_key.test.ts | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/backend/tests/orm_integration/session_key.test.ts b/backend/tests/orm_integration/session_key.test.ts index 421b3bd0..e18218f7 100644 --- a/backend/tests/orm_integration/session_key.test.ts +++ b/backend/tests/orm_integration/session_key.test.ts @@ -1,5 +1,5 @@ import prisma from "../../prisma/prisma"; -import {addSessionKey, checkSessionKey} from "../../orm_functions/session_key"; +import {addSessionKey, changeSessionKey, checkSessionKey, removeAllKeysForUser} from "../../orm_functions/session_key"; it("should create a new session key for the given login user", async () =>{ const loginUsers = await prisma.login_user.findMany(); @@ -29,5 +29,55 @@ it("should return an error because the key doesn't exist", async () => { } }); +it("should overwrite the old key with the new key", async () => { + const existing_keys = await prisma.session_keys.findMany(); + + const newkey = "newkey" + const updated = await changeSessionKey(existing_keys[0].session_key, newkey); + expect(updated).toHaveProperty("login_user_id", existing_keys[0].login_user_id); + expect(updated).toHaveProperty("session_key", newkey); + + const updated_keys = await prisma.session_keys.findMany({ + where: { + login_user_id: existing_keys[0].login_user_id + } + }); + + let updated_found = false; + updated_keys.forEach((record) => { + if (record.session_key === existing_keys[0].session_key) { + // should never get executed because old key should be overwritten + expect(false).toBeTruthy(); + } + if (record.session_key === updated.session_key) { + updated_found = true; + } + }); + if (!updated_found) { + // should never get executed if the updated succeeded! + expect(false).toBeTruthy() + } +}); + +it("should delete all session keys of the user with given key", async () => { + const login_users = await prisma.login_user.findMany() + + await prisma.session_keys.create({ + data : { + login_user_id: login_users[1].login_user_id, + session_key: "key_user1" + } + }); + + const deleted = await removeAllKeysForUser("newkey"); + expect(deleted).toHaveProperty("count", 3); + + const remaining_keys = await prisma.session_keys.findMany(); + expect(remaining_keys.length).toEqual(1); + expect(remaining_keys[0]).toHaveProperty("login_user_id", login_users[1].login_user_id); + + await removeAllKeysForUser("key_user1"); +}); + From 172cafb62e613e97256610dd50fed5985d3ee3c5 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 14:54:39 +0200 Subject: [PATCH 105/827] feat: emails get sent --- backend/.gitignore | 2 + backend/config.json | 2 +- backend/email.ts | 36 --- backend/package-lock.json | 455 +++++++++++++++++++++++++++++++++++++- backend/package.json | 1 + backend/routes/reset.ts | 42 +++- 6 files changed, 495 insertions(+), 43 deletions(-) delete mode 100644 backend/email.ts diff --git a/backend/.gitignore b/backend/.gitignore index 11ddd8db..98e0d419 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,5 @@ node_modules # Keep environment variables out of version control .env +# Keep email/google oauth secrets out of VC +email.json diff --git a/backend/config.json b/backend/config.json index 933872f1..24ab1cff 100644 --- a/backend/config.json +++ b/backend/config.json @@ -48,7 +48,7 @@ }, "email": { - "from": "'OSOC2 Test Account' ", + "from": "'OSOC2 Account Recovery' ", "header": "OSOC2 Recovery Code" } } diff --git a/backend/email.ts b/backend/email.ts deleted file mode 100644 index 20101c8b..00000000 --- a/backend/email.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as nodemailer from 'nodemailer'; - -import * as config from './config.json'; -import {Email} from './types'; - -// stateful function -const {mailer} = new class { - acct: nodemailer.TestAccount|null = null; - transp: nodemailer.Transporter|null = null; - - mailer = async (mail: Email) => { - if (this.acct == null) { - this.acct = await nodemailer.createTestAccount(); - } - if (this.transp == null) { - this.transp = nodemailer.createTransport({ - host : "smtp.ethereal.email", - port : 587, - secure : false, // true for 465, false for other ports - auth : { - user : this.acct.user, // generated ethereal user - pass : this.acct.pass, // generated ethereal password - } - }); - } - - return this.transp.sendMail({ - from : config.email.from, - to : mail.to, - subject : mail.subject, - html : mail.html - }); - }; -} - -export default mailer; diff --git a/backend/package-lock.json b/backend/package-lock.json index f3f001c0..604da6f1 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,6 +19,7 @@ "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", + "googleapis": "^100.0.0", "nodemailer": "^6.7.3", "passport": "^0.5.2", "prisma": "3.11", @@ -1554,6 +1555,17 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1769,6 +1781,14 @@ "node": ">=8" } }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1866,6 +1886,33 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1983,6 +2030,11 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2558,6 +2610,14 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3018,6 +3078,14 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3113,6 +3181,11 @@ "node": ">= 0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3146,6 +3219,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "node_modules/fast-text-encoding": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -3296,6 +3374,33 @@ "dev": true, "peer": true }, + "node_modules/gaxios": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.2.tgz", + "integrity": "sha512-T+ap6GM6UZ0c4E6yb1y/hy2UB6hTrqhglp3XfmU9qbLCGRYhLVV5aRPpC4EmoG8N8zOnkYCgoBz+ScvGAARY6Q==", + "dependencies": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3406,6 +3511,67 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-auth-library": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", + "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-p12-pem": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.3.tgz", + "integrity": "sha512-MC0jISvzymxePDVembypNefkAQp+DRP7dBE+zNUPaIjEspIlYg0++OrsNr248V9tPbz6iqtZ7rX1hxWA5B8qBQ==", + "dependencies": { + "node-forge": "^1.0.0" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/googleapis": { + "version": "100.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-100.0.0.tgz", + "integrity": "sha512-RToFQGY54B756IDbjdyjb1vWFmn03bYpXHB2lIf0eq2UBYsIbYOLZ0kqSomfJnpclEukwEmMF7Jn6Wsev871ew==", + "dependencies": { + "google-auth-library": "^7.0.2", + "googleapis-common": "^5.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/googleapis-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.1.0.tgz", + "integrity": "sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.14.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -3433,6 +3599,19 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "node_modules/gtoken": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", + "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", + "dependencies": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4664,6 +4843,14 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4709,6 +4896,25 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -4992,6 +5198,52 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6522,6 +6774,11 @@ "node": ">=4" } }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -6534,7 +6791,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "bin": { "uuid": "dist/bin/uuid" } @@ -7990,6 +8246,14 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -8146,6 +8410,11 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -8222,6 +8491,16 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -8311,6 +8590,11 @@ "node-int64": "^0.4.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -8751,6 +9035,14 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -9077,6 +9369,11 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9153,6 +9450,11 @@ "vary": "~1.1.2" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9183,6 +9485,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fast-text-encoding": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -9302,6 +9609,27 @@ "dev": true, "peer": true }, + "gaxios": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.2.tgz", + "integrity": "sha512-T+ap6GM6UZ0c4E6yb1y/hy2UB6hTrqhglp3XfmU9qbLCGRYhLVV5aRPpC4EmoG8N8zOnkYCgoBz+ScvGAARY6Q==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.1" + } + }, + "gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9376,6 +9704,52 @@ "slash": "^3.0.0" } }, + "google-auth-library": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", + "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.3.tgz", + "integrity": "sha512-MC0jISvzymxePDVembypNefkAQp+DRP7dBE+zNUPaIjEspIlYg0++OrsNr248V9tPbz6iqtZ7rX1hxWA5B8qBQ==", + "requires": { + "node-forge": "^1.0.0" + } + }, + "googleapis": { + "version": "100.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-100.0.0.tgz", + "integrity": "sha512-RToFQGY54B756IDbjdyjb1vWFmn03bYpXHB2lIf0eq2UBYsIbYOLZ0kqSomfJnpclEukwEmMF7Jn6Wsev871ew==", + "requires": { + "google-auth-library": "^7.0.2", + "googleapis-common": "^5.0.2" + } + }, + "googleapis-common": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-5.1.0.tgz", + "integrity": "sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==", + "requires": { + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.14.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^8.0.0" + } + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -9400,6 +9774,16 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, + "gtoken": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", + "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.1.3", + "jws": "^4.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -10324,6 +10708,14 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -10363,6 +10755,25 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -10576,6 +10987,40 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11656,6 +12101,11 @@ "prepend-http": "^2.0.0" } }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -11664,8 +12114,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", diff --git a/backend/package.json b/backend/package.json index 48890b0d..6b4d60cf 100644 --- a/backend/package.json +++ b/backend/package.json @@ -26,6 +26,7 @@ "body-parser": "^1.19.2", "cors": "^2.8.5", "express": "^4.17.3", + "googleapis": "^100.0.0", "nodemailer": "^6.7.3", "passport": "^0.5.2", "prisma": "3.11", diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index a4e50128..3ff9de2d 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -1,16 +1,52 @@ import express from 'express'; +import * as gapi from 'googleapis'; import * as nodemailer from 'nodemailer'; import * as config from '../config.json'; -import mailer from '../email'; +import * as google from '../email.json'; import * as ormLU from '../orm_functions/login_user'; import * as ormPR from '../orm_functions/password_reset'; import * as ormP from '../orm_functions/person'; import * as ormSK from '../orm_functions/session_key'; import * as rq from '../request'; -import {Responses} from '../types'; +import {Email, Responses} from '../types'; import * as util from '../utility'; +export async function sendMail(mail: Email) { + const oauthclient = new gapi.Auth.OAuth2Client( + google['google-client-id'], google['google-client-secret']); + oauthclient.setCredentials({refresh_token : google['google-refresh-token']}); + const accesstoken = + await oauthclient.getAccessToken().then(token => token.token!); + const transp = nodemailer.createTransport({ + service : 'gmail', + auth : { + type : 'OAUTH2', + user : 'osoc2.be@gmail.com', + clientId : google['google-client-id'], + clientSecret : google['google-client-secret'], + refreshToken : google['google-refresh-token'], + accessToken : accesstoken + } + }); + + return transp + .sendMail({ + from : config.email.from, + to : mail.to, + subject : mail.subject, + html : mail.html + }) + .then(res => { + transp.close(); + return Promise.resolve(res); + }) + .catch(e => { + console.log('Email error: ' + JSON.stringify(e)); + return Promise.reject(e); + }); +} + async function requestReset(req: express.Request): Promise { return rq.parseRequestResetRequest(req).then( (parsed) => @@ -24,7 +60,7 @@ async function requestReset(req: express.Request): Promise { .createOrUpdateReset(person.login_user.login_user_id, util.generateKey(), date) .then(async (code) => { - return mailer({ + return sendMail({ to : parsed.email, subject : config.email.header, html : '

' + code.reset_id + '

' From 50328d2d4b166e12040a813c26a45f0d99222b80 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 15:01:03 +0200 Subject: [PATCH 106/827] feat: updated deployment guide --- backend/github.json | 6 ------ docs/deploymentGuide.md | 13 ++++++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 backend/github.json diff --git a/backend/github.json b/backend/github.json deleted file mode 100644 index dfd8b142..00000000 --- a/backend/github.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "client_id": "36264ada9edd7f1ef96a", - "secret": "6f946ad9b524cf185e385dcf7c5189da0bc8404e", - "auth_callback_url": "http://127.0.0.1:4096" -} - diff --git a/docs/deploymentGuide.md b/docs/deploymentGuide.md index c2229179..fa306fdc 100644 --- a/docs/deploymentGuide.md +++ b/docs/deploymentGuide.md @@ -67,6 +67,18 @@ This secret can be generated in the github OAuth application by pressing `Genera If you want to run the application locally make another OAuth application and use as homepageUrl `http://localhost:3000`. Repeat the same steps once more to change the `GITHUB_ID` and `GITHUB_SECRET` but now in the `/frontend/.env.development`. +### Account recovery emails +To send a "password forgotten" email, we use GMail. However setting this up isn't exactly easy. Please follow along these [steps on medium.com](https://alexb72.medium.com/how-to-send-emails-using-a-nodemailer-gmail-and-oauth2-fe19d66451f9) until you have acquired all 4 codes (client ID, client secret, refresh token and access token). Once you have them, create a file called `email.json` in the `/backend/` folder with this structure: +```json +{ + "google-client-id": "CLIENT-ID", + "google-client-secret": "CLIENT-SECRET", + "google-refresh-token": "REFRESH-TOKEN", + "google-access-token": "ACCESS-TOKEN" +} +``` +After that, everything should work out-of-the-box. + ## How to deploy ### Preparation @@ -97,4 +109,3 @@ Listing all the images can be done with `docker images`, then you can delete the - `docker rmi "imageId"` Now you can deploy the new version as described in [Deployment](#deployment) - From 83a27921f3ed6d02c952e2a4431ac5472d6999e1 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 15:07:28 +0200 Subject: [PATCH 107/827] fix: typo :( --- backend/config.json | 2 +- backend/routes/reset.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/config.json b/backend/config.json index e2971e89..54421b44 100644 --- a/backend/config.json +++ b/backend/config.json @@ -35,7 +35,7 @@ "reason": "Something went wrong while trying to execute your request." }, - "inavlidEmailReset": { + "invalidEmailReset": { "http": 400, "reason": "No such email is present. Please check your email." }, diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 3ff9de2d..c669980a 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -52,7 +52,7 @@ async function requestReset(req: express.Request): Promise { (parsed) => ormP.getPasswordPersonByEmail(parsed.email).then(async (person) => { if (person == null || person.login_user == null) { - return Promise.reject(config.apiErrors.inavlidEmailReset); + return Promise.reject(config.apiErrors.invalidEmailReset); } const date: Date = new Date(Date.now()); date.setHours(date.getHours() + 24); From 71dccf15285dca0ee3035b4753d2984b7eb0b6ce Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 15:16:16 +0200 Subject: [PATCH 108/827] fix: failed test (regarding filling in values for error messages) --- backend/tests/utility.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/tests/utility.test.ts b/backend/tests/utility.test.ts index 238dcdfb..dfae37a3 100644 --- a/backend/tests/utility.test.ts +++ b/backend/tests/utility.test.ts @@ -62,7 +62,7 @@ test("utility.errors.cook* work as expected", () => { url : url, err : { http : config.apiErrors.nonExistent.http, - reason : config.apiErrors.nonExistent.reason.replace(/$url/, url) + reason : config.apiErrors.nonExistent.reason.replace(/~url/, url) } })); nonexistExpect.forEach( @@ -80,8 +80,8 @@ test("utility.errors.cook* work as expected", () => { const req: express.Request = getMockReq(); req.method = v.verb; req.url = v.url; - const msg = config.apiErrors.invalidVerb.reason.replace(/$url/, v.url) - .replace(/$verb/, v.verb); + const msg = config.apiErrors.invalidVerb.reason.replace(/~url/, v.url) + .replace(/~verb/, v.verb); return { err : {http : config.apiErrors.invalidVerb.http, reason : msg}, req : req @@ -98,7 +98,7 @@ test("utility.errors.cook* work as expected", () => { mime : mime, err : { http : config.apiErrors.nonJSONRequest.http, - reason : config.apiErrors.nonJSONRequest.reason.replace(/$mime/, mime) + reason : config.apiErrors.nonJSONRequest.reason.replace(/~mime/, mime) } })); mimeExpect.forEach( From e840eee9aedd47115a5787855e4449e492236802 Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Sat, 2 Apr 2022 15:50:25 +0200 Subject: [PATCH 109/827] chore: remove annotations in pr exept when test fails --- .github/workflows/backendCI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/backendCI.yml b/.github/workflows/backendCI.yml index dd02f570..15103e76 100644 --- a/.github/workflows/backendCI.yml +++ b/.github/workflows/backendCI.yml @@ -54,3 +54,4 @@ jobs: with: working-directory: backend skip-step: install + annotations: failed-tests From f2356f718e961adccaf7bea86bd53b2d6261e048 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 16:07:44 +0200 Subject: [PATCH 110/827] fix: github register works if you have no nickname set --- backend/email.json | 6 ++++++ backend/routes/github.ts | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 backend/email.json diff --git a/backend/email.json b/backend/email.json new file mode 100644 index 00000000..f9dbc50d --- /dev/null +++ b/backend/email.json @@ -0,0 +1,6 @@ +{ + "google-client-id": "319097806156-aomhaft2e9a7dafcv25vf5ke5h2ul1cn.apps.googleusercontent.com", + "google-client-secret": "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", + "google-refresh-token": "1//04S5I49MyVFXPCgYIARAAGAQSNwF-L9IrPWv0-zLXexrdSIkP4_RoACTm2-V69IuobLU7LYfkMtqnpIGhrWy94f1sooATcbqV950", + "google-access-token": "ya29.A0ARrdaM_d3Su7Feglzgj-RNyFFazH5hAv_q7sT7wktFrfWYRO4NW5DiDLLuxEjLz8SvDcXIunosbmT74UJcBTwb-009iPhscBkGsDyE3J2DaTwNRyqGmFO30VX2whPKC_fd-nhPZdLGU25QHVVfEbaOluqzSE" +} diff --git a/backend/routes/github.ts b/backend/routes/github.ts index a3d0d81d..a7d43010 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -91,9 +91,11 @@ async function ghExchangeAccessToken(req: express.Request, } function parseGHLogin(data: Anything): Promise { - if ('login' in data && 'name' in data) { - return Promise.resolve( - {login : data.login as string, name : data.name as string}); + if ('login' in data && 'name' in data && 'login' in data) { + return Promise.resolve({ + login : data.login as string, + name : data.name == null ? (data.login as string) : (data.name as string) + }); } return Promise.reject(); } From 57a7cbb67340ea1e1302f7fa7a7ff468853ec32d Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 16:14:25 +0200 Subject: [PATCH 111/827] fix: typo --- backend/email.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 backend/email.json diff --git a/backend/email.json b/backend/email.json deleted file mode 100644 index f9dbc50d..00000000 --- a/backend/email.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "google-client-id": "319097806156-aomhaft2e9a7dafcv25vf5ke5h2ul1cn.apps.googleusercontent.com", - "google-client-secret": "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", - "google-refresh-token": "1//04S5I49MyVFXPCgYIARAAGAQSNwF-L9IrPWv0-zLXexrdSIkP4_RoACTm2-V69IuobLU7LYfkMtqnpIGhrWy94f1sooATcbqV950", - "google-access-token": "ya29.A0ARrdaM_d3Su7Feglzgj-RNyFFazH5hAv_q7sT7wktFrfWYRO4NW5DiDLLuxEjLz8SvDcXIunosbmT74UJcBTwb-009iPhscBkGsDyE3J2DaTwNRyqGmFO30VX2whPKC_fd-nhPZdLGU25QHVVfEbaOluqzSE" -} From f07fb05d7d300b727484c468b6dd4e9274edb1ef Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 16:27:31 +0200 Subject: [PATCH 112/827] fix: move frontend URL to gh config file, as it is dependent on the state (dev/prod) --- backend/routes/github.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/routes/github.ts b/backend/routes/github.ts index a7d43010..d3fe83ad 100644 --- a/backend/routes/github.ts +++ b/backend/routes/github.ts @@ -78,13 +78,13 @@ async function ghExchangeAccessToken(req: express.Request, })) .then(ares => parseGHLogin(ares.data)) .then(login => ghSignupOrLogin(login)) - .then(result => util.redirect(res, config.global.frontend + "/login/" + + .then(result => util.redirect(res, github.frontend + "/login/" + result.sessionkey + "?is_admin=" + result.is_admin + "&is_coach=" + result.is_coach)) .catch(err => { console.log('GITHUB ERROR ' + err); - util.redirect(res, config.global.frontend + "/login?loginError=" + + util.redirect(res, github.frontend + "/login?loginError=" + config.apiErrors.github.other.reason); return Promise.resolve(); }); From 87012bc1f05b55cea3e75e08064f7199f61fbf37 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 16:28:51 +0200 Subject: [PATCH 113/827] feat: updated deployment guide --- docs/deploymentGuide.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/deploymentGuide.md b/docs/deploymentGuide.md index 2d5c6565..2d72276b 100644 --- a/docs/deploymentGuide.md +++ b/docs/deploymentGuide.md @@ -65,7 +65,8 @@ In the `/backend/` folder, edit the `github.json` configuration file. There are { "client_id": "YOUR_CLIENT_ID", "secret": "YOUR_CLIENT_SECRET", - "auth_callback_url": "YOUR_AUTH_CALLBACK" + "auth_callback_url": "YOUR_AUTH_CALLBACK", + "frontend": "YOUR_SERVER_URL" } ``` @@ -73,6 +74,7 @@ You should replace the values with these (see the screenshot below): - `YOUR_CLIENT_ID` should be the value given as `Client ID` (the first red box in the screenshot). - `YOUR_CLIENT_SECRET` should be the value given below `Client secrets` (the second red box in the screenshot). You can copy this value by clicking the button right next to it. - `YOUR_AUTH_CALLBACK` should be the value you filled in for `Authorization callback URL` (from the previous step). + - `YOUR_SERVER_URL` is not a value from the secrets, but it should hold the URL of the server on which the frontend runs. ![How to get the values](./gh-oauth-get-values.png) From ae8ca0524c605e733b659e8564df4fdb85229119 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 18:28:43 +0200 Subject: [PATCH 114/827] feat: sessionkey now lives in header --- backend/config.json | 3 ++- backend/request.ts | 63 +++++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/backend/config.json b/backend/config.json index 54421b44..10ccab4b 100644 --- a/backend/config.json +++ b/backend/config.json @@ -58,7 +58,8 @@ "homes": [ "", "/api-osoc" ], - "preferred": "/api-osoc" + "preferred": "/api-osoc", + "authScheme": "auth/osoc2" }, "email": { diff --git a/backend/request.ts b/backend/request.ts index ccf98dc4..11fe5637 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -1,6 +1,7 @@ import {account_status_enum} from '@prisma/client'; import express from 'express'; +import * as config from './config.json'; import {Anything, InternalTypes, Requests} from './types'; import {errors} from './utility'; @@ -59,28 +60,52 @@ function anyHasFields(obj: Anything, fields: string[]): boolean { /** * Checks if a request has the required fields. If the request is a Key request - * or and ID request, the `req.body.sessionkey` field is also checked for - * existence. If the request is an ID request, the `req.params.id` field is also - * checked for existence. + * or and ID request, the `Authorization` header is also checked for existence + * and semantics; it has to start with the correct value (as defined in the + * `config.json`). If the request is an ID request, the `req.params.id` field + * is also checked for existence. * @param req The request to check. * @param fields The fields that should be present. * @param reqType The type of request. * @returns A Promise which will resolve to nothing if all of the fields are * present, or reject with an Argument Error if any of the fields is not * present. If the request is expected to be a key or ID request, but it doesn't - * hold a `req.body.sessionkey`, a promise rejecting with an Unauthenticated + * hold a `getSessionKey(req)`, a promise rejecting with an Unauthenticated * Error is returned instead. */ function hasFields(req: express.Request, fields: string[], reqType: RequestType): Promise { - if ((reqType == types.key || reqType == types.id) && - (!("sessionkey" in req.body) || req.body.sessionkey == undefined)) - return Promise.reject(errors.cookUnauthenticated()); + if (reqType == types.key || reqType == types.id) { + const authHeader = req.get('authorization'); + if (authHeader == undefined || + !authHeader.startsWith(config.global.authScheme)) { + return Promise.reject(errors.cookUnauthenticated()); + } + } + // if ((reqType == types.key || reqType == types.id) && + // (!("sessionkey" in req.body) || req.body.sessionkey == undefined)) + // return Promise.reject(errors.cookUnauthenticated()); if (reqType == types.id && !("id" in req.params)) return rejector(); return anyHasFields(req.body, fields) ? Promise.resolve() : rejector(); } +/** + * Extracts the session key from the request headers. + * @param req The request to extract the session key from. + * @throws Error if there is no session key. + * @returns The extracted session key. + * @see hasFields. + */ +function getSessionKey(req: express.Request): string { + const authHeader = req.get('authorization'); + if (authHeader == undefined || + !authHeader.startsWith(config.global.authScheme)) { + throw Error('No session key - you should check for the session key first.'); + } + return authHeader.replace(config.global.authScheme + " ", ""); +} + /** * Checks whether the request holds one or more of the fields. * @param req The request to check. @@ -111,7 +136,7 @@ function maybe(obj: Anything, key: string): T|undefined { async function parseKeyRequest(req: express.Request): Promise { return hasFields(req, [], types.key).then(() => Promise.resolve({ - sessionkey : req.body.sessionkey + sessionkey : getSessionKey(req) })); } @@ -124,7 +149,7 @@ async function parseKeyRequest(req: express.Request): async function parseKeyIdRequest(req: express.Request): Promise { return hasFields(req, [], types.id).then(() => Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id) })); } @@ -140,7 +165,7 @@ async function parseUpdateLoginUser(req: express.Request): return hasFields(req, [ "isAdmin", "isCoach", "accountStatus" ], types.id) .then(() => { return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), isAdmin : maybe(req.body, "isAdmin") as boolean, isCoach : maybe(req.body, "isCoach") as boolean, @@ -182,7 +207,7 @@ export async function parseUpdateStudentRequest(req: express.Request): return rejector(); return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), emailOrGithub : maybe(req.body, "emailOrGithub"), firstName : maybe(req.body, "firstName"), @@ -212,7 +237,7 @@ export async function parseSuggestStudentRequest(req: express.Request): return rejector(); return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), suggestion : sug as InternalTypes.Suggestion, reason : maybe(req.body, "reason"), @@ -237,7 +262,7 @@ export async function parseFinalizeDecisionRequest(req: express.Request): } return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), reply : maybe(req.body, "reply") }); @@ -273,7 +298,7 @@ export async function parseNewProjectRequest(req: express.Request): return hasFields(req, [ "name", "partner", "start", "end", "positions" ], types.key) .then(() => Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), name : req.body.name, partner : req.body.partner, start : req.body.start, @@ -297,7 +322,7 @@ export async function parseUpdateProjectRequest(req: express.Request): return rejector(); return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), name : maybe(req.body, "name"), partner : maybe(req.body, "partner"), @@ -318,7 +343,7 @@ export async function parseDraftStudentRequest(req: express.Request): Promise { return hasFields(req, [ "studentId", "roles" ], types.id) .then(() => Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), studentId : req.body.studentId, roles : req.body.roles @@ -339,7 +364,7 @@ export async function parseSetFollowupStudentRequest(req: express.Request): return rejector(); return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), type : type }); @@ -356,7 +381,7 @@ export async function parseNewTemplateRequest(req: express.Request): Promise { return hasFields(req, [ "name", "content" ], types.key) .then(() => Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), name : req.body.name, subject : maybe(req.body, "subject"), desc : maybe(req.body, "desc"), @@ -378,7 +403,7 @@ export async function parseUpdateTemplateRequest(req: express.Request): return rejector(); return Promise.resolve({ - sessionkey : req.body.sessionkey, + sessionkey : getSessionKey(req), id : Number(req.params.id), name : maybe(req.body, "name"), desc : maybe(req.body, "desc"), From 74c10c54f64972400ce9324901ac0d9616b0c319 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 19:08:12 +0200 Subject: [PATCH 115/827] fix: updated tests to match with changed header --- backend/request.ts | 4 +- backend/tests/request.test.ts | 169 ++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 81 deletions(-) diff --git a/backend/request.ts b/backend/request.ts index 11fe5637..b6e6d9a1 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -76,7 +76,7 @@ function anyHasFields(obj: Anything, fields: string[]): boolean { function hasFields(req: express.Request, fields: string[], reqType: RequestType): Promise { if (reqType == types.key || reqType == types.id) { - const authHeader = req.get('authorization'); + const authHeader = req.headers.authorization; if (authHeader == undefined || !authHeader.startsWith(config.global.authScheme)) { return Promise.reject(errors.cookUnauthenticated()); @@ -98,7 +98,7 @@ function hasFields(req: express.Request, fields: string[], * @see hasFields. */ function getSessionKey(req: express.Request): string { - const authHeader = req.get('authorization'); + const authHeader = req.headers.authorization; if (authHeader == undefined || !authHeader.startsWith(config.global.authScheme)) { throw Error('No session key - you should check for the session key first.'); diff --git a/backend/tests/request.test.ts b/backend/tests/request.test.ts index 0f6d3308..aef99751 100644 --- a/backend/tests/request.test.ts +++ b/backend/tests/request.test.ts @@ -1,16 +1,24 @@ import {getMockReq} from '@jest-mock/express'; import express from 'express'; +import * as config from '../config.json'; import * as Rq from '../request' import * as T from '../types'; import {errors} from '../utility'; +function setSessionKey(req: express.Request, key: string): void { + req.headers.authorization = config.global.authScheme + " " + key; +} + test("Can parse Key-only requests", () => { const valid: express.Request = getMockReq(); const invalid: express.Request = getMockReq(); const wrongprop: express.Request = getMockReq(); - valid.body.sessionkey = "hello I am a key"; + // valid.body.sessionkey = "hello I am a key"; + // valid.headers.authorization = config.global.authScheme + " hello I am a + // key"; + setSessionKey(valid, "hello I am a key"); wrongprop.body.key = "hello I am a key as well"; const calls = [ @@ -40,9 +48,11 @@ test("Can parse Key-ID requests", () => { const onlyKey: express.Request = getMockReq(); const onlyid: express.Request = getMockReq(); - valid.body.sessionkey = res.sessionkey; + // valid.body.sessionkey = res.sessionkey; + setSessionKey(valid, res.sessionkey); valid.params.id = res.id.toString(); - onlyKey.body.sessionkey = res.sessionkey; + // onlyKey.body.sessionkey = res.sessionkey; + setSessionKey(onlyKey, res.sessionkey); onlyid.params.id = res.id.toString(); const calls = [ @@ -72,31 +82,19 @@ test("Can parse Key-ID requests", () => { test("Can parse update login user requests", () => { const id = 1234; - const valid1: T.Anything = { - isAdmin : false, - isCoach : false, - accountStatus : 'PENDING', - sessionkey : 'abc' - }; + const key = 'abc'; + const valid1: T + .Anything = {isAdmin : false, isCoach : false, accountStatus : 'PENDING'}; const valid2: T.Anything = { isAdmin : false, isCoach : false, accountStatus : 'PENDING', - pass : 'mypass', - sessionkey : 'abc' + pass : 'mypass' }; - const invalid1: T.Anything = { - isCoach : false, - accountStatus : 'PENDING', - sessionkey : 'abc' - }; - const invalid2: T.Anything = { - isCoach : false, - accountStatus : 'PENDING', - pass : 'mypass', - sessionkey : 'abc' - }; + const invalid1: T.Anything = {isCoach : false, accountStatus : 'PENDING'}; + const invalid2: T + .Anything = {isCoach : false, accountStatus : 'PENDING', pass : 'mypass'}; const invalid_sk: T.Anything = { isAdmin : false, isCoach : false, @@ -108,7 +106,9 @@ test("Can parse update login user requests", () => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); x.id = id; + x.sessionkey = key; if (!("pass" in x)) x.pass = undefined; return expect(Rq.parseUpdateCoachRequest(r)).resolves.toStrictEqual(x); @@ -118,7 +118,9 @@ test("Can parse update login user requests", () => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); x.id = id; + x.sessionkey = key; return expect(Rq.parseUpdateCoachRequest(r)) .rejects.toBe(errors.cookArgumentError()) }); @@ -126,16 +128,19 @@ test("Can parse update login user requests", () => { const s_ = [ valid1, valid2 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); if (!("pass" in x)) x.pass = undefined; r.params.id = id.toString(); x.id = id; + x.sessionkey = key; return expect(Rq.parseUpdateAdminRequest(r)).resolves.toStrictEqual(x); }); const f_ = [ invalid1, invalid2 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); return expect(Rq.parseUpdateAdminRequest(r)) .rejects.toBe(errors.cookArgumentError()) }); @@ -172,8 +177,8 @@ test("Can parse login request", () => { }); test("Can parse update student request", () => { + const sessionkey = "abcdef"; const dataV: T.Anything = { - sessionkey : "abcdef", emailOrGithub : "ab@c.de", alumni : false, firstName : "ab", @@ -191,7 +196,6 @@ test("Can parse update student request", () => { }; const failure1: T.Anything = { - sessionkey : "abcdef", emailOrGithub : "ab@c.de", firstName : "ab", // no last name gender : "Apache Attack Helicopter", @@ -206,7 +210,6 @@ test("Can parse update student request", () => { }; const failure2: T.Anything = { - sessionkey : "abcdef", emailOrGithub : "ab@c.de", firstName : "ab", lastName : "c", @@ -220,7 +223,7 @@ test("Can parse update student request", () => { } }; - const failure3: T.Anything = {sessionkey : "abcdef"}; + const failure3: T.Anything = {}; const id = 60011223369; @@ -231,22 +234,29 @@ test("Can parse update student request", () => { valid.body = {...dataV}; valid.params.id = id.toString(); + setSessionKey(valid, sessionkey); ival1.body = {...failure1}; ival1.params.id = id.toString(); + setSessionKey(ival1, sessionkey); ival2.body = {...failure2}; ival2.params.id = id.toString(); + setSessionKey(ival2, sessionkey); ival3.body = {...failure3}; ival3.params.id = id.toString(); + setSessionKey(ival3, sessionkey); dataV.id = id; + dataV.sessionkey = sessionkey; failure1.id = id; failure1.lastName = undefined; failure1.alumni = undefined; failure1.nickname = undefined; + failure1.sessionkey = sessionkey; failure2.id = id; (failure2.education as T.Anything).duration = undefined; failure2.alumni = undefined; failure2.nickname = undefined; + failure2.sessionkey = sessionkey; const error = errors.cookArgumentError(); @@ -263,23 +273,17 @@ test("Can parse update student request", () => { test("Can parse suggest student request", () => { const key = "my-session-key"; const id = 9845; - const ys: T.Anything = {suggestion : "YES", sessionkey : key, senderId : 0}; - const mb: T.Anything = {suggestion : "MAYBE", sessionkey : key, senderId : 0}; - const no: T.Anything = {suggestion : "NO", sessionkey : key, senderId : 0}; + const ys: T.Anything = {suggestion : "YES", senderId : 0}; + const mb: T.Anything = {suggestion : "MAYBE", senderId : 0}; + const no: T.Anything = {suggestion : "NO", senderId : 0}; const nr: T.Anything = { suggestion : "NO", reason : "I just don't like you", - sessionkey : key, senderId : 0 }; - const i1: - T.Anything = {suggestion : "TOMORROW", sessionkey : key, senderId : 0}; - const i2: T.Anything = { - suggestion : "no", - sessionkey : key, - senderId : 0 - }; // no caps - const i3: T.Anything = {sessionkey : key, senderId : 0}; + const i1: T.Anything = {suggestion : "TOMORROW", senderId : 0}; + const i2: T.Anything = {suggestion : "no", senderId : 0}; // no caps + const i3: T.Anything = {senderId : 0}; const okays = [ ys, mb, no, nr ].map(x => { const copy: T.Anything = {...x}; @@ -287,11 +291,13 @@ test("Can parse suggest student request", () => { const req: express.Request = getMockReq(); req.params.id = id.toString(); req.body = x; + setSessionKey(req, key); ["reason"].forEach(x => { if (!(x in req.body)) { copy[x] = undefined } }); + copy.sessionkey = key; return expect(Rq.parseSuggestStudentRequest(req)) .resolves.toStrictEqual(copy); }); @@ -300,6 +306,7 @@ test("Can parse suggest student request", () => { const req: express.Request = getMockReq(); req.params.id = id.toString(); req.body = {...x}; + setSessionKey(req, key); return expect(Rq.parseSuggestStudentRequest(req)) .rejects.toBe(errors.cookArgumentError()); }); @@ -310,19 +317,21 @@ test("Can parse suggest student request", () => { test("Can parse final decision request", () => { const key = "key"; const id = 6969420420; - const data: T.Anything = {sessionkey : key}; - const dat2: T.Anything = {reply : "YES", sessionkey : key}; - const dat3: T.Anything = {reply : "NO", sessionkey : key}; - const dat4: T.Anything = {reply : "MAYBE", sessionkey : key}; - const dat5: T.Anything = {reply : "something", sessionkey : key}; - const dat6: T.Anything = {reply : "maybe", sessionkey : key}; + const data: T.Anything = {}; + const dat2: T.Anything = {reply : "YES"}; + const dat3: T.Anything = {reply : "NO"}; + const dat4: T.Anything = {reply : "MAYBE"}; + const dat5: T.Anything = {reply : "something"}; + const dat6: T.Anything = {reply : "maybe"}; const dat7: T.Anything = {reply : "YES"}; const p = [ data, dat2, dat3, dat4 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); x.id = id; + x.sessionkey = key; if (!("reply" in x)) x.reply = undefined; @@ -333,6 +342,7 @@ test("Can parse final decision request", () => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); x.id = id; return expect(Rq.parseFinalizeDecisionRequest(r)) .rejects.toBe(errors.cookArgumentError()); @@ -387,14 +397,13 @@ test("Can parse coach access request", () => { test("Can parse new project request", () => { const key = "abcdefghijklmnopqrstuvwxyz"; const d1: T.Anything = { - sessionkey : key, name : "Experiment One", partner : "Simic Combine", start : Date.now(), end : Date.now(), positions : 69 }; - const d2: T.Anything = {sessionkey : key}; + const d2: T.Anything = {}; const d3: T.Anything = { name : "Experiment One", partner : "Simic Combine", @@ -408,9 +417,13 @@ test("Can parse new project request", () => { const req3: express.Request = getMockReq(); req1.body = {...d1}; + setSessionKey(req1, key); req2.body = {...d2}; + setSessionKey(req2, key); req3.body = {...d3}; + d1.sessionkey = key; + const p1: Promise = expect(Rq.parseNewProjectRequest(req1)).resolves.toStrictEqual(d1); const p2: Promise = expect(Rq.parseNewProjectRequest(req2)) @@ -425,16 +438,14 @@ test("Can parse update project request", () => { const key = "abcdefghijklmnopqrstuvwxyz"; const id = 6845684; const d1: T.Anything = { - sessionkey : key, name : "Experiment One", partner : "Simic Combine", start : Date.now(), end : Date.now(), positions : 69 }; - const d2: T.Anything = {sessionkey : key}; + const d2: T.Anything = {}; const d3: T.Anything = { - sessionkey : key, name : "Experiment One", partner : "Simic Combine", start : Date.now(), @@ -456,17 +467,23 @@ test("Can parse update project request", () => { req1.body = {...d1}; req1.params.id = id.toString(); + setSessionKey(req1, key); req2.body = {...d2}; req2.params.id = id.toString(); + setSessionKey(req2, key); req3.body = {...d3}; req3.params.id = id.toString(); + setSessionKey(req3, key); req4.body = {...d4}; req4.params.id = id.toString(); req5.body = {...d1}; + setSessionKey(req5, key); d1.id = id; + d1.sessionkey = key; d2.id = id; d3.id = id; + d3.sessionkey = key; d3.end = undefined; d4.id = id; @@ -489,11 +506,10 @@ test("Can parse draft student request", () => { const id = 89846; const d1: T.Anything = { - sessionkey : key, studentId : "im-a-student", roles : [ "the", "one", "that", "does", "nothing" ] }; - const d2: T.Anything = {sessionkey : key, studentId : "im-a-student"}; + const d2: T.Anything = {studentId : "im-a-student"}; const d3: T.Anything = { studentId : "im-a-student", roles : [ "the", "one", "that", "does", "nothing" ] @@ -505,15 +521,19 @@ test("Can parse draft student request", () => { const r4: express.Request = getMockReq(); r1.body = {...d1}; + setSessionKey(r1, key); r2.body = {...d2}; + setSessionKey(r2, key); r3.body = {...d3}; r4.body = {...d1}; + setSessionKey(r4, key); r1.params.id = id.toString(); r2.params.id = id.toString(); r3.params.id = id.toString(); d1.id = id; + d1.sessionkey = key; d2.id = id; d3.id = id; @@ -533,18 +553,20 @@ test("Can parse mark as followed up request", () => { const key = "my-key-arrived-but"; const id = 78945312; - const ht: T.Anything = {sessionkey : key, type : "hold-tight"}; - const cf: T.Anything = {sessionkey : key, type : "confirmed"}; - const cd: T.Anything = {sessionkey : key, type : "cancelled"}; - const i1: T.Anything = {sessionkey : key, type : "invalid"}; + const ht: T.Anything = {type : "hold-tight"}; + const cf: T.Anything = {type : "confirmed"}; + const cd: T.Anything = {type : "cancelled"}; + const i1: T.Anything = {type : "invalid"}; const i2: T.Anything = {type : "hold-tight"}; - const i3: T.Anything = {sessionkey : key}; + const i3: T.Anything = {}; const okays = [ ht, cf, cd ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); r.params.id = id.toString(); x.id = id; + x.sessionkey = key; return expect(Rq.parseSetFollowupStudentRequest(r)) .resolves.toStrictEqual(x); }); @@ -552,6 +574,7 @@ test("Can parse mark as followed up request", () => { const fails1 = [ i1, i3 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); r.params.id = id.toString(); x.id = id; return expect(Rq.parseSetFollowupStudentRequest(r)) @@ -561,6 +584,7 @@ test("Can parse mark as followed up request", () => { const fails2 = [ ht ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); return expect(Rq.parseSetFollowupStudentRequest(r)) .rejects.toBe(errors.cookArgumentError()); }); @@ -580,27 +604,20 @@ test("Can parse mark as followed up request", () => { test("Can parse new template request", () => { const key = "yet-another-session-key"; - const ok1: T.Anything = { - name : "my-template", - content : "hello-there", - sessionkey : key - }; + const ok1: T.Anything = {name : "my-template", content : "hello-there"}; const ok2: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?", }; const ok3: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, cc : "cc@gmail.com" }; const ok4: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; @@ -608,20 +625,17 @@ test("Can parse new template request", () => { name : "my-template", content : "hello-there", subject : "I like C++", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; const f1: T.Anything = { content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; const f2: T.Anything = { name : "my-template", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; @@ -635,10 +649,12 @@ test("Can parse new template request", () => { const okays = [ ok1, ok2, ok3, ok4, ok5 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); ["desc", "cc", "subject"].forEach(v => { if (!(v in x)) x[v] = undefined; }); + x.sessionkey = key; return expect(Rq.parseNewTemplateRequest(r)).resolves.toStrictEqual(x); }); @@ -646,6 +662,7 @@ test("Can parse new template request", () => { const fails1 = [ f1, f2 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); return expect(Rq.parseNewTemplateRequest(r)) .rejects.toBe(errors.cookArgumentError()); }); @@ -664,39 +681,30 @@ test("Can parse update template request", () => { const key = "yet-another-session-key"; const id = 987465327465; - const ok1: T.Anything = { - name : "my-template", - content : "hello-there", - sessionkey : key - }; + const ok1: T.Anything = {name : "my-template", content : "hello-there"}; const ok2: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?" }; const ok3: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, cc : "cc@gmail.com" }; const ok4: T.Anything = { name : "my-template", content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; const ok5: T.Anything = { content : "hello-there", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; const ok6: T.Anything = { name : "my-template", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; @@ -704,19 +712,20 @@ test("Can parse update template request", () => { name : "my-template", content : "hello-there", subject : "I like C++", - sessionkey : key, desc : "a description did you know that orcas have culture?", cc : "cc@gmail.com" }; - const f1: T.Anything = {sessionkey : key}; + const f1: T.Anything = {}; const f2: T.Anything = {name : "my-template", content : "hello-there"}; const okays = [ ok1, ok2, ok3, ok4, ok5, ok6, ok7 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); x.id = id; + x.sessionkey = key; ["name", "content", "subject", "desc", "cc"].forEach(v => { if (!(v in x)) x[v] = undefined; @@ -729,6 +738,7 @@ test("Can parse update template request", () => { const r: express.Request = getMockReq(); r.body = {...x}; r.params.id = id.toString(); + setSessionKey(r, key); return expect(Rq.parseUpdateTemplateRequest(r)) .rejects.toBe(errors.cookArgumentError()); }); @@ -736,6 +746,7 @@ test("Can parse update template request", () => { const fails2 = [ ok1 ].map(x => { const r: express.Request = getMockReq(); r.body = {...x}; + setSessionKey(r, key); return expect(Rq.parseUpdateTemplateRequest(r)) .rejects.toBe(errors.cookArgumentError()); }); From 5b1267280a5395740899022815553c040daf6689 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 19:33:13 +0200 Subject: [PATCH 116/827] fix: bugfix in utility, updated curly --- backend/request.ts | 18 +----------------- backend/tools/curly.sh | 15 ++++++++++++--- backend/utility.ts | 20 +++++++++++++++++++- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/backend/request.ts b/backend/request.ts index b6e6d9a1..71ea2c77 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -3,7 +3,7 @@ import express from 'express'; import * as config from './config.json'; import {Anything, InternalTypes, Requests} from './types'; -import {errors} from './utility'; +import {errors, getSessionKey} from './utility'; /** * We use 3 types of requests: those requiring no special values, those @@ -90,22 +90,6 @@ function hasFields(req: express.Request, fields: string[], return anyHasFields(req.body, fields) ? Promise.resolve() : rejector(); } -/** - * Extracts the session key from the request headers. - * @param req The request to extract the session key from. - * @throws Error if there is no session key. - * @returns The extracted session key. - * @see hasFields. - */ -function getSessionKey(req: express.Request): string { - const authHeader = req.headers.authorization; - if (authHeader == undefined || - !authHeader.startsWith(config.global.authScheme)) { - throw Error('No session key - you should check for the session key first.'); - } - return authHeader.replace(config.global.authScheme + " ", ""); -} - /** * Checks whether the request holds one or more of the fields. * @param req The request to check. diff --git a/backend/tools/curly.sh b/backend/tools/curly.sh index 7da45433..ba811086 100755 --- a/backend/tools/curly.sh +++ b/backend/tools/curly.sh @@ -22,6 +22,15 @@ function main() { printf 'HTTP Verb? ' read verb + printf 'Session key (leave blank to pass no session key)? ' + read skey + skey=`printf "$skey" | xargs` + if [ -z ${skey//[:blank:]} ]; then + skey='' + else + skey="-H \"Authorization: auth/osoc2 $skey\"" + fi + echo 'You can now pass arguments. Use CTRL-D after you entered the last key-value pair.' args='' printf 'Key? ' @@ -38,7 +47,7 @@ function main() { echo '' args="'{ $args }'" - echo "Curl command: \`curl -X \"$verb\" \"http://localhost:4096$api$ep\" -i -d $args -H \"Content-Type: application/json\"\`" + echo "Curl command: \`curl -X \"$verb\" \"http://localhost:4096$api$ep\" -i -d $args $skey -H \"Content-Type: application/json\"\`" printf 'Send this curl command (yes/y/no/n/maybe)? ' read ans while [ $ans != 'yes' ] && [ $ans != 'no' ] && [ $ans != 'maybe' ] && [ $ans != 'y' ] && [ $ans != 'n' ]; do @@ -47,12 +56,12 @@ function main() { done if [ $ans = 'y' ] || [ $ans = 'yes' ]; then - /bin/sh -c "curl -X \"$verb\" \"http://localhost:4096$api$ep\" -i -d $args -H \"Content-Type: application/json\"" + /bin/sh -c "curl -X \"$verb\" \"http://localhost:4096$api$ep\" -i -d $args $skey -H \"Content-Type: application/json\"" elif [ $ans = 'maybe' ]; then printf 'Output file? ' read f echo "#!/bin/sh" >f - echo curl -X "$verb" "http://localhost:4096$api$ep" -i -d $args -H "Content-Type: application/json" >>f + echo curl -X "$verb" "http://localhost:4096$api$ep" -i -d $args $skey -H "Content-Type: application/json" >>f fi } diff --git a/backend/utility.ts b/backend/utility.ts index df1e0931..c01a20fc 100644 --- a/backend/utility.ts +++ b/backend/utility.ts @@ -53,6 +53,24 @@ export const errors: Errors = { cookServerError() { return config.apiErrors.serverError;} } +/** + * Extracts the session key from the request headers. + * @param req The request to extract the session key from. + * @throws Error if there is no session key. + * @returns The extracted session key. + * @see hasFields. + */ +export function getSessionKey(req: express.Request): + string { + const authHeader = req.headers.authorization; + if (authHeader == undefined || + !authHeader.startsWith(config.global.authScheme)) { + throw Error( + 'No session key - you should check for the session key first.'); + } + return authHeader.replace(config.global.authScheme + " ", ""); + } + /** * Promise-based debugging function. Logs the data, then passes it through * (using `Promise.resolve`). @@ -189,7 +207,7 @@ export async function respOrError( req: express.Request, res: express.Response, prom: Promise>): Promise { return respOrErrorNoReinject( - res, prom.then((res) => refreshAndInjectKey(req.body.sessionkey, res))); + res, prom.then((res) => refreshAndInjectKey(getSessionKey(req), res))); } /** From 64562cf3e281ef07e7dc9dc87ed3cbe636faa971 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 19:41:48 +0200 Subject: [PATCH 117/827] fix: utility test is correct again --- backend/package.json | 2 +- backend/tests/utility.test.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/package.json b/backend/package.json index 8330b9a5..e2648bdb 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,7 +10,7 @@ "docker:down": "docker-compose down", "integrationTests": "npm run dockerIntegration:up && npm run migrate:postgres && jest -i", "test": "jest", - "coverage": "jest --coverage", + "coverage": "npm run dockerIntegration:up && npm run migrate:postgres && jest -i --coverage", "codecov": "jest --ci --coverage --outputFile=coverage.json --json" }, "author": "", diff --git a/backend/tests/utility.test.ts b/backend/tests/utility.test.ts index dfae37a3..89721258 100644 --- a/backend/tests/utility.test.ts +++ b/backend/tests/utility.test.ts @@ -249,11 +249,15 @@ test("utility.respOrErrorNoReinject sends generic errors as server errors", }); }); -// test respOrError has to wait -> ORM connection has to be mocked +function setSessionKey(req: express.Request, key: string): void { + req.headers.authorization = config.global.authScheme + " " + key; +} + test("utility.respOrError sends responses with updated keys", async () => { const {res, statSpy, sendSpy} = obtainResponse(); const req = getMockReq(); - req.body.sessionkey = "key"; + // req.body.sessionkey = "key"; + setSessionKey(req, "key"); session_keyMock.changeSessionKey.mockReset(); session_keyMock.changeSessionKey.mockImplementation((_, nw) => { From f941fc5b3cb54be50cd02b5a118a88b032dadc40 Mon Sep 17 00:00:00 2001 From: jay-tux Date: Sat, 2 Apr 2022 19:52:35 +0200 Subject: [PATCH 118/827] feat: bump up test coverage a bit --- backend/tests/utility.test.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/backend/tests/utility.test.ts b/backend/tests/utility.test.ts index 89721258..a44dca13 100644 --- a/backend/tests/utility.test.ts +++ b/backend/tests/utility.test.ts @@ -368,6 +368,20 @@ test("utility.isAdmin should succeed on valid keys, fail on invalid keys" + expect(login_userMock.searchAllAdminLoginUsers).toHaveBeenCalledTimes(2); }); +test("utility.isAdmin can catch errors from the DB", async () => { + session_keyMock.checkSessionKey.mockReset(); + session_keyMock.checkSessionKey.mockImplementation( + () => Promise.resolve({login_user_id : 1})); + + login_userMock.searchAllAdminLoginUsers.mockReset(); + login_userMock.searchAllAdminLoginUsers.mockImplementation( + () => Promise.reject({})); + + expect(util.isAdmin({ + sessionkey : "key" + })).rejects.toStrictEqual(util.errors.cookInsufficientRights()); +}); + test("utility.refreshKey removes a key and replaces it", async () => { session_keyMock.changeSessionKey.mockReset(); session_keyMock.changeSessionKey.mockImplementation((_, nw) => { @@ -418,3 +432,17 @@ test("utility.refreshAndInjectKey refreshes a key and injects it", async () => { expect(session_keyMock.changeSessionKey).toHaveBeenCalledTimes(1); expect(session_keyMock.changeSessionKey).toHaveBeenCalledWith('ab', 'abcd'); }); + +test("utility.getSessionKey fetches session key or crashes", () => { + const r = getMockReq(); + const err = 'No session key - you should check for the session key first.'; + setSessionKey(r, 'some_key'); + expect(util.getSessionKey(r)).toBe("some_key"); + + const f1 = getMockReq(); + f1.headers.authorization = "some_key"; + expect(() => util.getSessionKey(f1)).toThrow(err); + + const f2 = getMockReq(); + expect(() => util.getSessionKey(f2)).toThrow(err); +}); From 2d2ea3df4094cbc5a80dfffc9586b40af554c8b3 Mon Sep 17 00:00:00 2001 From: Huan Date: Sat, 2 Apr 2022 21:19:11 +0200 Subject: [PATCH 119/827] feat: making the user component --- frontend/components/User/User.module.css | 37 +++++++++++++ frontend/components/User/User.tsx | 51 ++++++++++++++++-- frontend/public/images/admin_icon.png | Bin 0 -> 1174 bytes frontend/public/images/admin_icon_color.png | Bin 0 -> 1706 bytes frontend/public/images/coach_icon.png | Bin 0 -> 1053 bytes frontend/public/images/coach_icon_color.png | Bin 0 -> 1639 bytes frontend/public/images/forbidden_icon.png | Bin 0 -> 882 bytes .../public/images/forbidden_icon_color.png | Bin 0 -> 1360 bytes 8 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 frontend/public/images/admin_icon.png create mode 100644 frontend/public/images/admin_icon_color.png create mode 100644 frontend/public/images/coach_icon.png create mode 100644 frontend/public/images/coach_icon_color.png create mode 100644 frontend/public/images/forbidden_icon.png create mode 100644 frontend/public/images/forbidden_icon_color.png diff --git a/frontend/components/User/User.module.css b/frontend/components/User/User.module.css index e69de29b..a15b10be 100644 --- a/frontend/components/User/User.module.css +++ b/frontend/components/User/User.module.css @@ -0,0 +1,37 @@ +.column { + float: left; + width: 33.33%; +} + +.row:after { + content: ""; + display: table; + clear: both; +} + +@media screen and (max-width: 600px) { + .column { + width: 100%; + } +} + +.button { + padding: 5px 5px; + text-align: left; + display: inline-block; + font-size: 10px; + background-color: transparent; + +} + +.buttonImage { + border-radius: 4px; + width: 2px; + height: 15px; + display: none; +} + + +.displayNone { + display: none !important; +} \ No newline at end of file diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 512f4bf2..89474a0c 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -1,4 +1,13 @@ import styles from "./User.module.css"; +import AdminIcon from "../../public/images/admin_icon.png"; +import AdminIconColor from "../../public/images/admin_icon_color.png"; +import CoachIcon from "../../public/images/coach_icon.png"; +import CoachIconColor from "../../public/images/coach_icon_color.png"; +import ForbiddenIcon from "../../public/images/forbidden_icon.png"; +import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png"; +import Image, {StaticImageData} from "next/image"; +import React from "react"; + export const User: React.FC = () => { //boilerplate for the admin/coaches route (pass admin/coach as string) @@ -26,9 +35,43 @@ export const User: React.FC = () => { }); console.log(response) } + //TODO dit moet nog afgewerkt worden + function button(image1: StaticImageData, image2: StaticImageData, usage: string) { + return () + } + //TODO dit sta hier voor tslint + console.log(getAlluser("admin")) + return ( +
+
+

name [Status pending]

+
+
+

email

+
+
+ {button(AdminIcon, AdminIconColor, "admin")} + {button(CoachIcon, CoachIconColor, "coach")} + {button(ForbiddenIcon, ForbiddenIconColor, "admin")} - getAlluser("admin") - return (
-

Users

-
) +
+
) } diff --git a/frontend/public/images/admin_icon.png b/frontend/public/images/admin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ceccc09b7ba3a754d8f70e0dae43ac7c0ee09691 GIT binary patch literal 1174 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 AiU0rr literal 0 HcmV?d00001 diff --git a/frontend/public/images/coach_icon.png b/frontend/public/images/coach_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..54b66326beecc0b5ef5b720a523b7985cdbbdefb GIT binary patch literal 1053 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 IM6N<$f@>?9cmMzZ literal 0 HcmV?d00001 diff --git a/frontend/public/images/forbidden_icon_color.png b/frontend/public/images/forbidden_icon_color.png new file mode 100644 index 0000000000000000000000000000000000000000..3e25ad3c0c1fc299212adefb38cddd8fa39d9dda GIT binary patch literal 1360 zcmV-W1+V&vP)8n#g)fB3Ah&* zAMjyMY6LSnvYD(zMlsRAd@wUH3O;OXiMGY9t-ZbH=kHu;1HI>#QfT*0AU*$n_jAv= z=lp)>hfslTc62m8+0yvD1TjEBZ2;H_2z~_ct9AlNNTNFe@Gc@n1yOYGmDns)Aly#p zTZf<9-|X4*0U-?LVaiob!m3Bmj29s6i_EW-JFYCiKM(ip_X#@dHSk^7712bwpj!ea zk97|bi12o2tPCa6IqT!#)rC(>b#euaheCeYkR9;|d>^VPljMbcS7N8#+O>e|aF-bF z7?cu1f(Q4ru(S-96 zI>5e{eh>m*1a~dnm-51%O#Rn7qnm8;%NmX5aUuwFvCXZYC21Nn-ZzWwIRIB*?Yijm z7;kH>@QeY;a8o2U1{G+nzvq-B7~zMu>e_RX#vh%TAKF+air`MW!SNf$yhNRq!r3+~ zYnBg5a%)%Ld%Uk}aARFz2DzaPg|VpHt!#7BADKU4|Ey59eKf8kx-av(5=k@l%zRKU2#4R*m44{60~CHebNJ0S5e|xF^rtN42ipgdPyI0{RYXLK#ibv}D9*nqr@M>K<%ADLeG0;&kt6qz3hOvgs~+gWn9 zDK+@&FX?)!aE?8c z6Og^g;#MAFx8RfllhFiwnrLx-jwmQNunaoPaDWLlf-ArP-6*c}2(XQ760wj??}H4A7}u)lSj;(W4qLM`FcDBW>Ka7!TKa&ZGcyi-NOP$RhbK*-NF zLmG}HA*7ttjPYk0G6PTpI3*)v9@o`NA!fyo>Eutkjp9RNs1|TG2-^3RO|l491DEHb z;^pWs_qjY#+(9jK@<{hkwc*zKdQRzVvp4(J=1~7u`VR&CM1O0>D<7Gt7Myx{KqJc0 zl;aZR<(!X-MeT#Sb$Tw&zHnS)!(T19Uqc7?5gJoEIF%a72g(Ic^1#INx^J->aO#A3 z+}H3W``->nBQvPHO}yn8bkEE14qFzSVX!g{Q{|*S);yV$ zO?Ae5u3)TmPJR=-8aq*3k6NP)nagCq@(O$fwvdX1RTh=ARQJ4Oq*NXIP?SJV8z^3I zTgr{%tVM+LGH;ZfJjyHC-}4Di`!G~d9MO+K+8cITi7gYDfAx32;{kDA7ufDdsPfp> znLuP=qI9=1w4QlTm-|831@ZEa+%?k|+t>3irBoJRUJ8^@e8dsZ>%9G&w=?$t)E$i` z*2H-0++4i;VHGO=+My6fLcMIDGY8Pl+0 Date: Sun, 3 Apr 2022 15:04:16 +0200 Subject: [PATCH 120/827] fix: student page loads 2 times --- frontend/pages/students.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frontend/pages/students.tsx b/frontend/pages/students.tsx index 43f81fa4..b93bcf22 100644 --- a/frontend/pages/students.tsx +++ b/frontend/pages/students.tsx @@ -11,20 +11,17 @@ const Students: NextPage = () => { useEffect(() => { const fetchData = async () => { - //console.log(sessionKey); const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`, { method: 'GET', - body: JSON.stringify({sessionkey: sessionKey}), //TODO Autherize the user headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', + 'Authorization': `auth/osoc2 ${sessionKey}` } }); - console.log(response) + console.log(response); } fetchData(); - }); + }, []); return ( <> From 9430503389f070e0f923c9c5b4d03bf6c6cb4edc Mon Sep 17 00:00:00 2001 From: Huan Date: Sun, 3 Apr 2022 15:54:37 +0200 Subject: [PATCH 121/827] feat: divided the components into their respective place --- frontend/components/User/User.module.css | 21 ------- frontend/components/User/User.tsx | 57 ++----------------- .../UserButton/UserButton.module.css | 24 ++++++++ frontend/components/UserButton/UserButton.tsx | 43 ++++++++++++++ frontend/components/Users/Users.tsx | 41 +++++++++++++ frontend/pages/users.tsx | 6 +- 6 files changed, 115 insertions(+), 77 deletions(-) create mode 100644 frontend/components/UserButton/UserButton.module.css create mode 100644 frontend/components/UserButton/UserButton.tsx create mode 100644 frontend/components/Users/Users.tsx diff --git a/frontend/components/User/User.module.css b/frontend/components/User/User.module.css index a15b10be..9a250a30 100644 --- a/frontend/components/User/User.module.css +++ b/frontend/components/User/User.module.css @@ -14,24 +14,3 @@ width: 100%; } } - -.button { - padding: 5px 5px; - text-align: left; - display: inline-block; - font-size: 10px; - background-color: transparent; - -} - -.buttonImage { - border-radius: 4px; - width: 2px; - height: 15px; - display: none; -} - - -.displayNone { - display: none !important; -} \ No newline at end of file diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 89474a0c..8405f42e 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -5,60 +5,11 @@ import CoachIcon from "../../public/images/coach_icon.png"; import CoachIconColor from "../../public/images/coach_icon_color.png"; import ForbiddenIcon from "../../public/images/forbidden_icon.png"; import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png"; -import Image, {StaticImageData} from "next/image"; +import {UserButton} from "../UserButton/UserButton"; import React from "react"; export const User: React.FC = () => { - //boilerplate for the admin/coaches route (pass admin/coach as string) - const getAlluser = async (route: string) => { - console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'sessionKey': 'TODO' - } - }) - .then(response => response.json()).then(json => { - if (!json.success) { - - //TODO logica van niet gelukt - return {success: false}; - } else return json; - }) - .catch(err => { - console.log(err) - //TODO logica van niet gelukt - return {success: false}; - }); - console.log(response) - } - //TODO dit moet nog afgewerkt worden - function button(image1: StaticImageData, image2: StaticImageData, usage: string) { - return () - } - //TODO dit sta hier voor tslint - console.log(getAlluser("admin")) return (
@@ -68,9 +19,9 @@ export const User: React.FC = () => {

email

- {button(AdminIcon, AdminIconColor, "admin")} - {button(CoachIcon, CoachIconColor, "coach")} - {button(ForbiddenIcon, ForbiddenIconColor, "admin")} + {UserButton(AdminIcon, AdminIconColor, "admin")} + {UserButton(CoachIcon, CoachIconColor, "coach")} + {UserButton(ForbiddenIcon, ForbiddenIconColor, "admin")}
) diff --git a/frontend/components/UserButton/UserButton.module.css b/frontend/components/UserButton/UserButton.module.css new file mode 100644 index 00000000..98ce7016 --- /dev/null +++ b/frontend/components/UserButton/UserButton.module.css @@ -0,0 +1,24 @@ +.button { + padding: 5px 5px; + text-align: left; + display: inline-block; + font-size: 10px; + background-color: transparent; + +} + +.buttonImage { + border-radius: 4px; + width: 2px; + height: 15px; + display: none; +} + + +.displayNone { + display: none !important; +} + +.button:hover { + background-color: dimgrey; +} \ No newline at end of file diff --git a/frontend/components/UserButton/UserButton.tsx b/frontend/components/UserButton/UserButton.tsx new file mode 100644 index 00000000..1b700939 --- /dev/null +++ b/frontend/components/UserButton/UserButton.tsx @@ -0,0 +1,43 @@ +import React, { useState} from "react"; +import Image, {StaticImageData} from "next/image"; +import styles from "../UserButton/UserButton.module.css"; + + +export const UserButton: (image1: StaticImageData, image2: StaticImageData, usage: string) => JSX.Element = (image1: StaticImageData, image2: StaticImageData, usage: string) => { + + + //TODO logica voor elke button implementeren + const [a, setA] = useState(0); + + + let image = {"black + if (a % 2 === 1) { + image = {"colored + } + return () +} + + + diff --git a/frontend/components/Users/Users.tsx b/frontend/components/Users/Users.tsx new file mode 100644 index 00000000..48cfba36 --- /dev/null +++ b/frontend/components/Users/Users.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import {User} from "../User/User"; + + +export const UsersComponent: React.FC = () => { + + //boilerplate for the admin/coaches route (pass admin/coach as string) + const getAlluser = async (route: string) => { + console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'sessionKey': 'TODO' + } + }) + .then(response => response.json()).then(json => { + if (!json.success) { + + //TODO logica van niet gelukt + return {success: false}; + } else return json; + }) + .catch(err => { + console.log(err) + //TODO logica van niet gelukt + return {success: false}; + }); + console.log(response) + } + + //TODO dit sta hier voor tslint + console.log(getAlluser("admin")) + return ( +
+ + + +
) +} diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 96324fb5..952c73f6 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -1,13 +1,13 @@ import {NextPage} from "next"; import {Header} from "../components/Header/Header"; -import {User} from "../components/User/User"; +import {UsersComponent} from "../components/Users/Users"; const Users: NextPage = () => { return ( <>
- - + + ) } From 940b856be46a0179e701539b5ef4f0ba9fb90aa3 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sun, 3 Apr 2022 17:29:06 +0200 Subject: [PATCH 122/827] feat: refactor and load users from the database --- frontend/components/User/User.tsx | 14 +++-- frontend/components/UserButton/UserButton.tsx | 18 +++--- frontend/components/Users/Users.tsx | 41 ------------ frontend/next.config.js | 2 +- frontend/pages/users.tsx | 63 +++++++++++++++++-- 5 files changed, 75 insertions(+), 63 deletions(-) delete mode 100644 frontend/components/Users/Users.tsx diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 8405f42e..28f4857d 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -9,20 +9,26 @@ import {UserButton} from "../UserButton/UserButton"; import React from "react"; -export const User: React.FC = () => { +export const User: React.FC<{ name: string, email: string, is_coach: boolean, is_admin: boolean }> = ({ + name, + email, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + is_coach, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + is_admin + }) => { return (
-

name [Status pending]

+

{name}

-

email

+

{email}

{UserButton(AdminIcon, AdminIconColor, "admin")} {UserButton(CoachIcon, CoachIconColor, "coach")} {UserButton(ForbiddenIcon, ForbiddenIconColor, "admin")} -
) } diff --git a/frontend/components/UserButton/UserButton.tsx b/frontend/components/UserButton/UserButton.tsx index 1b700939..7b85bc0d 100644 --- a/frontend/components/UserButton/UserButton.tsx +++ b/frontend/components/UserButton/UserButton.tsx @@ -1,9 +1,9 @@ -import React, { useState} from "react"; +import React, {useState} from "react"; import Image, {StaticImageData} from "next/image"; import styles from "../UserButton/UserButton.module.css"; -export const UserButton: (image1: StaticImageData, image2: StaticImageData, usage: string) => JSX.Element = (image1: StaticImageData, image2: StaticImageData, usage: string) => { +export const UserButton: (image_bw: StaticImageData, image_color: StaticImageData, usage: string) => JSX.Element = (image1: StaticImageData, image2: StaticImageData, usage: string) => { //TODO logica voor elke button implementeren @@ -30,14 +30,10 @@ export const UserButton: (image1: StaticImageData, image2: StaticImageData, usag setA(a + 1); console.log(a); }}> - {usage} - - {image} - - - +
{usage} +
+ {image} +
+
) } - - - diff --git a/frontend/components/Users/Users.tsx b/frontend/components/Users/Users.tsx deleted file mode 100644 index 48cfba36..00000000 --- a/frontend/components/Users/Users.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import {User} from "../User/User"; - - -export const UsersComponent: React.FC = () => { - - //boilerplate for the admin/coaches route (pass admin/coach as string) - const getAlluser = async (route: string) => { - console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'sessionKey': 'TODO' - } - }) - .then(response => response.json()).then(json => { - if (!json.success) { - - //TODO logica van niet gelukt - return {success: false}; - } else return json; - }) - .catch(err => { - console.log(err) - //TODO logica van niet gelukt - return {success: false}; - }); - console.log(response) - } - - //TODO dit sta hier voor tslint - console.log(getAlluser("admin")) - return ( -
- - - -
) -} diff --git a/frontend/next.config.js b/frontend/next.config.js index e61ca03c..53e2dc06 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true + reactStrictMode: false } module.exports = nextConfig diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 952c73f6..707e4882 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -1,14 +1,65 @@ import {NextPage} from "next"; import {Header} from "../components/Header/Header"; -import {UsersComponent} from "../components/Users/Users"; +import React, {useContext, useEffect, useState} from "react"; +import SessionContext from "../contexts/sessionProvider"; +import {User} from "../components/User/User"; +/** + * The `manage users` page, only accessible for admins + * @constructor + */ const Users: NextPage = () => { + + const {sessionKey, setSessionKey} = useContext(SessionContext) + const [users, setUsers] = useState>() + + + // Load all users upon page load + useEffect(() => { + // boilerplate for the admin/coaches route (pass admin/coach as string) + const getAllUsers = async (route: string) => { + console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") + return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `auth/osoc2 ${sessionKey}` + } + }) + .then(response => response.json()).then(json => { + if (!json.success) { + + //TODO logica van niet gelukt + return {success: false}; + } else return json; + }) + .catch(err => { + console.log(err) + //TODO logica van niet gelukt + return {success: false}; + }) + } + + getAllUsers("admin").then(response => { + console.log(response) + if (setSessionKey) { + setSessionKey(response.sessionkey) + } + setUsers(response.data) + }) + }, []) + + return ( - <> -
- - - +
+
+ {users !== undefined ? users.map((value, index) => { + return + }) : null} +
+
) } From c95894f09444b53a42ae5d00cf41deeca02bc0a9 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sun, 3 Apr 2022 17:37:34 +0200 Subject: [PATCH 123/827] feat: show the users on screen --- frontend/pages/users.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 707e4882..8025dd46 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -48,17 +48,22 @@ const Users: NextPage = () => { } setUsers(response.data) }) + // We need to disable this warning. We of course do not want do reload the page when the data is changed + // because that is exactly what this function does. + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return (
-
- {users !== undefined ? users.map((value, index) => { - return - }) : null} -
+
+
+ {users !== undefined ? users.map((value, index) => { + return + }) : null} +
+
) } From 31d871cf1251eb07bfd310b14f55a3d190c2f0c4 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sun, 3 Apr 2022 19:09:15 +0200 Subject: [PATCH 124/827] feat: refactor and styling --- frontend/components/User/User.module.css | 47 +++++++++--- frontend/components/User/User.tsx | 75 +++++++++++++------ .../UserButton/UserButton.module.css | 24 ------ frontend/components/UserButton/UserButton.tsx | 39 ---------- frontend/pages/users.tsx | 21 +++--- frontend/styles/users.module.css | 5 ++ 6 files changed, 102 insertions(+), 109 deletions(-) delete mode 100644 frontend/components/UserButton/UserButton.module.css delete mode 100644 frontend/components/UserButton/UserButton.tsx create mode 100644 frontend/styles/users.module.css diff --git a/frontend/components/User/User.module.css b/frontend/components/User/User.module.css index 9a250a30..46a4f579 100644 --- a/frontend/components/User/User.module.css +++ b/frontend/components/User/User.module.css @@ -1,16 +1,41 @@ -.column { - float: left; - width: 33.33%; +.row { + display: grid; + grid-gap: 2rem; + grid-template-columns: 1.5fr 1.5fr 0.5fr; + align-items: center; + padding: 0.5rem; + width: 100%; + border-bottom: solid var(--neutral-200) 0.15rem; } -.row:after { - content: ""; - display: table; - clear: both; +.row * { + margin-block: 0; } -@media screen and (max-width: 600px) { - .column { - width: 100%; - } +.buttons { + display: flex; + justify-content: space-around; + align-items: center; +} + +.buttonContainer { + transition: 0.2s; +} + +.buttonContainer p { + font-size: 0.75rem; + text-align: center; + margin-top: 0; +} + + + +.buttonContainer:hover { + scale: 1.1; + cursor: pointer; +} + +.button { + margin-block: 0.2rem; + margin-inline: 0.5rem; } diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index 28f4857d..b672c710 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -1,34 +1,61 @@ import styles from "./User.module.css"; -import AdminIcon from "../../public/images/admin_icon.png"; import AdminIconColor from "../../public/images/admin_icon_color.png"; +import AdminIcon from "../../public/images/admin_icon.png" +import CoachIconColor from "../../public/images/coach_icon_color.png" import CoachIcon from "../../public/images/coach_icon.png"; -import CoachIconColor from "../../public/images/coach_icon_color.png"; -import ForbiddenIcon from "../../public/images/forbidden_icon.png"; -import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png"; -import {UserButton} from "../UserButton/UserButton"; -import React from "react"; +import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png" +import ForbiddenIcon from "../../public/images/forbidden_icon.png" +import React, {useState} from "react"; +import Image from "next/image"; -export const User: React.FC<{ name: string, email: string, is_coach: boolean, is_admin: boolean }> = ({ - name, - email, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - is_coach, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - is_admin - }) => { +export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: boolean, userIsCoach: boolean, userStatus: string }> = ({ + userName, + userEmail, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + userIsAdmin, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + userIsCoach, + userStatus + }) => { + + const [name] = useState(userName) + const [email] = useState(userEmail) + const [isAdmin] = useState(userIsAdmin) + const [isCoach] = useState(userIsCoach) + const [status] = useState(userStatus) + + // TODO -- Create a function for every button + return (
-
-

{name}

-
-
-

{email}

-
-
- {UserButton(AdminIcon, AdminIconColor, "admin")} - {UserButton(CoachIcon, CoachIconColor, "coach")} - {UserButton(ForbiddenIcon, ForbiddenIconColor, "admin")} +

{name}

+

{email}

+
+
+
+ {"Admin"}/ +
+

Admin

+
+
+
+ {"Coach"}/ +
+

Coach

+
+
+
+ {"Disabled"}/ +
+

Disabled

+
) } diff --git a/frontend/components/UserButton/UserButton.module.css b/frontend/components/UserButton/UserButton.module.css deleted file mode 100644 index 98ce7016..00000000 --- a/frontend/components/UserButton/UserButton.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.button { - padding: 5px 5px; - text-align: left; - display: inline-block; - font-size: 10px; - background-color: transparent; - -} - -.buttonImage { - border-radius: 4px; - width: 2px; - height: 15px; - display: none; -} - - -.displayNone { - display: none !important; -} - -.button:hover { - background-color: dimgrey; -} \ No newline at end of file diff --git a/frontend/components/UserButton/UserButton.tsx b/frontend/components/UserButton/UserButton.tsx deleted file mode 100644 index 7b85bc0d..00000000 --- a/frontend/components/UserButton/UserButton.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, {useState} from "react"; -import Image, {StaticImageData} from "next/image"; -import styles from "../UserButton/UserButton.module.css"; - - -export const UserButton: (image_bw: StaticImageData, image_color: StaticImageData, usage: string) => JSX.Element = (image1: StaticImageData, image2: StaticImageData, usage: string) => { - - - //TODO logica voor elke button implementeren - const [a, setA] = useState(0); - - - let image = {"black - if (a % 2 === 1) { - image = {"colored - } - return () -} diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 8025dd46..038a7a94 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -3,6 +3,7 @@ import {Header} from "../components/Header/Header"; import React, {useContext, useEffect, useState} from "react"; import SessionContext from "../contexts/sessionProvider"; import {User} from "../components/User/User"; +import styles from "../styles/users.module.css" /** * The `manage users` page, only accessible for admins @@ -54,18 +55,16 @@ const Users: NextPage = () => { }, []) - return ( -
-
-
- {users !== undefined ? users.map((value, index) => { - return - }) : null} -
- + return (<> +
+
+ {users !== undefined ? users.map((value, index) => { + return + }) : null}
- ) + ) } export default Users; diff --git a/frontend/styles/users.module.css b/frontend/styles/users.module.css new file mode 100644 index 00000000..4f3ebf67 --- /dev/null +++ b/frontend/styles/users.module.css @@ -0,0 +1,5 @@ +.body { + padding-block: clamp(1rem, 2vw, 2.5rem); + width: clamp(18rem, 80vw, 100rem); + margin-inline: auto; +} From 83885496b7d0094839b860cd00a9d8c45d576207 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sun, 3 Apr 2022 19:49:45 +0200 Subject: [PATCH 125/827] feat: pending button and button skeleton code --- frontend/components/User/User.module.css | 2 +- frontend/components/User/User.tsx | 69 ++++++++++++++++---- frontend/public/images/green_check_mark.png | Bin 0 -> 1912 bytes 3 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 frontend/public/images/green_check_mark.png diff --git a/frontend/components/User/User.module.css b/frontend/components/User/User.module.css index 46a4f579..007e463b 100644 --- a/frontend/components/User/User.module.css +++ b/frontend/components/User/User.module.css @@ -1,7 +1,7 @@ .row { display: grid; grid-gap: 2rem; - grid-template-columns: 1.5fr 1.5fr 0.5fr; + grid-template-columns: 1.5fr 1.5fr 0.75fr; align-items: center; padding: 0.5rem; width: 100%; diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index b672c710..bfe67bbb 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -5,7 +5,8 @@ import CoachIconColor from "../../public/images/coach_icon_color.png" import CoachIcon from "../../public/images/coach_icon.png"; import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png" import ForbiddenIcon from "../../public/images/forbidden_icon.png" -import React, {useState} from "react"; +import GreenCheckMark from "../../public/images/green_check_mark.png" +import React, {SyntheticEvent, useState} from "react"; import Image from "next/image"; @@ -21,18 +22,48 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: const [name] = useState(userName) const [email] = useState(userEmail) - const [isAdmin] = useState(userIsAdmin) - const [isCoach] = useState(userIsCoach) - const [status] = useState(userStatus) + const [isAdmin, setIsAdmin] = useState(userIsAdmin) + const [isCoach, setIsCoach] = useState(userIsCoach) + const [status, setStatus] = useState(userStatus) - // TODO -- Create a function for every button + const toggleIsAdmin = async (e: SyntheticEvent) => { + e.preventDefault() + setIsAdmin(!isAdmin) + // TODO -- Send the isAdmin value to the backend + // -- If error revert to old value + } + + const toggleIsCoach = async (e: SyntheticEvent) => { + e.preventDefault() + setIsCoach(!isCoach) + // TODO -- Send the isCoach value to the backend + // -- If error revert to old value + } + + const toggleStatus = async (e: SyntheticEvent) => { + e.preventDefault() + if (status == 'ACTIVATED') { + setStatus('DISABLED') + } else { + setStatus('ACTIVATED') + } + // TODO -- Send the status value to the backend + // -- If error revert to old value + } + + const activateUser = async (e: SyntheticEvent) => { + e.preventDefault() + setStatus('ACTIVATED') + // TODO -- Send the status value to the backend + // -- If error revert to old value + } return (

{name}

{email}

-
+

Admin

-
+

Coach

-
-
- {"Disabled"}/ + {status === 'PENDING' ? +
+
+ {"Disabled"}/ +
+

Pending

-

Disabled

-
+ : +
+
+ {"Disabled"}/ +
+

Disabled

+
}
) } diff --git a/frontend/public/images/green_check_mark.png b/frontend/public/images/green_check_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..2de51a7c2084ef082a7e5bc040bceb64e8b00e4d GIT binary patch literal 1912 zcmV-;2Z#8HP)RrusyFN|8@#(&|8e#+ zb7#))oZmV3e(yQ=cYo&qWXO;qLxv0)zB>>|GY$R*+fF%e?pY4A2tvs)MTKw}%%gA; zfW6-`rTR_y7D$_rneD!x4oUc+M&K452p!E&MnDu90^;%gXZ!UEN7&m8f#Y^d)3lZIQOlP2A!5`%Mq{ZWEPH zi))TiXV=-HwkcSiXvmo{kV0dMMrqd|k{Joyd+ortpWzoa z_ZI)MH@YDyAuhGaA11^JUy8t!!#q5{JB68uXHmN+_kS*T-_&-`1DY)TnZ<=~5w3AO zhv)1HJlayYyeH8ZGMHq3`|e8hYY`Ew1pXRGgNO*{=m05GeU1GDs^Z&h2rV;!|GKHT z>VqqdLBS-a6x(W}ET&Q2ea5jB(ty*y*V!lKz7a1%gk{V}tbd|KpV#EPvWN(#W@J~- z{+vy{7w-i5EB8FWXDTW#h1OJA*yXvtgX3Nu;rr3`GGa#-*OnhHS$V{5ax7Cbvax@7 zjwZ`C!zLFm{yW}ByxF``@pi!^3pzY&Ri?bbAXbEpz@GEif8EDI1SEkeA>U*CYNM0igx>kXPT%&?#wzb>s_B=c$D@CQG9iP7gs(m7 zto%d7o~nB5gOa>2m}{R2X%#}jVw4rUygf{5YqWhD$UQRxjkWQ9Qvyx3eVoM9i>%;` z6=A`lDOEcnc7@-&J4EOPE>>_Tq+7uxri6q;dY@4ak;_-jL$b1E9Aa~HT|Y>h=C^z9 zGHh3;uO5Na#C28mkUsZQ$FjplMGqybj55>oAeh9I5ULGz8)XIYs%DV@Mz`Nvuw-yr zNcx2W=pT6q@kRrRyzO?iY4Wr#UtJhsLv$ap>FS!P4=oOqx<~qWx;%D0>K`pxm2OC< z5PNRcF{3Qf^6cLfmfsxj^UurAp9OSKHCPs3bkcIv{k>9(eYg#99_DFQyAdm^swgs?*^5GvP zN^Q<+Y-Cy4ksm#J*TdOl& zUa?)C94U(rQ14}-XCMho3;9>^%6II5|2J&L=_BR-##5e|1?DC`shLI$QrKVwC?Sa%D^}EU)=4> zKOKfmL4@DNJ`C1&Ix6cR1yVi^5;LJ y=mF`=b(haHKP7{$2jY%#GGxe*Awz}?Y2*u>{>OxbP>=`!0000 Date: Sun, 3 Apr 2022 20:29:40 +0200 Subject: [PATCH 126/827] feat: add cookies to save the user state --- frontend/contexts/sessionProvider.tsx | 50 ++++++++++++++++++++++++--- frontend/next.config.js | 2 +- frontend/pages/login/index.tsx | 15 ++++---- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/frontend/contexts/sessionProvider.tsx b/frontend/contexts/sessionProvider.tsx index ac5e694d..c807bf82 100644 --- a/frontend/contexts/sessionProvider.tsx +++ b/frontend/contexts/sessionProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useState } from 'react'; +import React, {createContext, useEffect, useState} from 'react'; /** * Interface for the context, stores the user session application wide @@ -23,11 +23,53 @@ const defaultState = { const SessionContext = createContext(defaultState); +/** + * The sesssion provider is responsible for storing the user session both at runtime and in cookies + * The session prodiver also reads all cookies upon reload + * @param children + * @constructor + */ export const SessionProvider: React.FC = ({ children }) => { - const [sessionKey, setSessionKey] = useState(""); - const [isCoach, setIsCoach] = useState(false); - const [isAdmin, setIsAdmin] = useState(false); + /** + * + */ + useEffect(() => { + const allCookies = document.cookie.split(';').reduce((res, cookie) => { + const [key, value] = cookie.trim().split('=') + try { + return Object.assign(res, { [key]: JSON.parse(value) }) + } catch (e) { + return Object.assign(res, {[key]: value}) + } + }, {}); + const cookies = allCookies as {sessionKey: string, isAdmin: boolean, isCoach: boolean} + setSessionKeyState(cookies.sessionKey) + setIsAdminState(cookies.isAdmin) + setIsCoachState(cookies.isCoach) + }, []) + + const [sessionKey, setSessionKeyState] = useState(""); + const [isCoach, setIsCoachState] = useState(false); + const [isAdmin, setIsAdminState] = useState(false); + + const setSessionKey = (sessionKey: string) => { + setSessionKeyState(sessionKey) + // Set a new cookie for the session key with max age of 1 day + document.cookie = `sessionKey=${sessionKey};samesite=strict;max-age=86400` + } + + const setIsCoach = (isCoach: boolean) => { + setIsCoachState(isCoach) + // Set a new cookie for the coach state with max age of 1 day + document.cookie = `isCoach=${isCoach};samesite=strict;max-age=86400` + } + + const setIsAdmin = (isAdmin: boolean) => { + setIsAdminState(isAdmin) + // Set a new cookie for the admin state with max age of 1 day + document.cookie = `isAdmin=${isAdmin};samesite=strict;max-age=86400` + } return ( diff --git a/frontend/next.config.js b/frontend/next.config.js index e61ca03c..53e2dc06 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true + reactStrictMode: false } module.exports = nextConfig diff --git a/frontend/pages/login/index.tsx b/frontend/pages/login/index.tsx index 01a7a4aa..44ecc7a6 100644 --- a/frontend/pages/login/index.tsx +++ b/frontend/pages/login/index.tsx @@ -13,18 +13,23 @@ import SessionContext from "../../contexts/sessionProvider"; const Index: NextPage = () => { const router = useRouter() + const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) // Sets an error message when the `loginError` query paramater is present useEffect(() => { + // The user is already logged in, redirect the user + if (sessionKey != "") { + router.push("/students").then() + return + } + const { loginError } = router.query if (loginError != undefined) { if (typeof loginError === 'string') { setLoginBackendError(loginError) } } - }, [router.query]) - - const {sessionKey, setSessionKey, setIsAdmin, setIsCoach} = useContext(SessionContext) + }, [router, router.query, sessionKey]) // Index field values with corresponding error messages const [loginEmail, setLoginEmail] = useState(""); @@ -112,7 +117,7 @@ const Index: NextPage = () => { if (setIsCoach) { setIsCoach(response.is_coach) } - router.push("/").then() + router.push("/students").then() } } } @@ -211,7 +216,6 @@ const Index: NextPage = () => { const githubLogin = async (e: SyntheticEvent) => { e.preventDefault(); window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/github` - // TODO -- How are we supposed to send the data to the backend? } /** @@ -254,7 +258,6 @@ const Index: NextPage = () => { return (
-

{sessionKey}

Welcome to OSOC Selections!

Please login, or register to proceed

From e1c5c3c797d673c105c5f1c3b50ba8d422019576 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Sun, 3 Apr 2022 20:47:44 +0200 Subject: [PATCH 127/827] feat: use localStorage instead of cookies --- frontend/contexts/sessionProvider.tsx | 30 ++++++++++----------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/frontend/contexts/sessionProvider.tsx b/frontend/contexts/sessionProvider.tsx index c807bf82..bdb423b4 100644 --- a/frontend/contexts/sessionProvider.tsx +++ b/frontend/contexts/sessionProvider.tsx @@ -32,21 +32,13 @@ const SessionContext = createContext(defaultState); export const SessionProvider: React.FC = ({ children }) => { /** - * + * Everytime the page is reloaded we need to get the session from local storage */ useEffect(() => { - const allCookies = document.cookie.split(';').reduce((res, cookie) => { - const [key, value] = cookie.trim().split('=') - try { - return Object.assign(res, { [key]: JSON.parse(value) }) - } catch (e) { - return Object.assign(res, {[key]: value}) - } - }, {}); - const cookies = allCookies as {sessionKey: string, isAdmin: boolean, isCoach: boolean} - setSessionKeyState(cookies.sessionKey) - setIsAdminState(cookies.isAdmin) - setIsCoachState(cookies.isCoach) + const sessionKey = localStorage.getItem('sessionKey') + setSessionKeyState(sessionKey !== null ? sessionKey : "") + setIsAdminState(localStorage.getItem('isAdmin') === 'true') + setIsCoachState(localStorage.getItem('isCoach') === 'true') }, []) const [sessionKey, setSessionKeyState] = useState(""); @@ -55,20 +47,20 @@ export const SessionProvider: React.FC = ({ children }) => { const setSessionKey = (sessionKey: string) => { setSessionKeyState(sessionKey) - // Set a new cookie for the session key with max age of 1 day - document.cookie = `sessionKey=${sessionKey};samesite=strict;max-age=86400` + // Update localStorage + localStorage.setItem('sessionKey', sessionKey) } const setIsCoach = (isCoach: boolean) => { setIsCoachState(isCoach) - // Set a new cookie for the coach state with max age of 1 day - document.cookie = `isCoach=${isCoach};samesite=strict;max-age=86400` + // Update localStorage + localStorage.setItem('isCoach', String(isCoach)) } const setIsAdmin = (isAdmin: boolean) => { setIsAdminState(isAdmin) - // Set a new cookie for the admin state with max age of 1 day - document.cookie = `isAdmin=${isAdmin};samesite=strict;max-age=86400` + // Update localStorage + localStorage.setItem('isAdmin', String(isAdmin)) } return ( From 04969071393dc89730a5194079f28880683d4e18 Mon Sep 17 00:00:00 2001 From: Huan Date: Sun, 3 Apr 2022 23:15:49 +0200 Subject: [PATCH 128/827] feat: boilerplate for communication backend usersrole --- frontend/components/User/User.tsx | 76 ++++++++++++++++++++++++------- frontend/pages/users.tsx | 17 ++++++- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index bfe67bbb..f681af4d 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -6,8 +6,9 @@ import CoachIcon from "../../public/images/coach_icon.png"; import ForbiddenIconColor from "../../public/images/forbidden_icon_color.png" import ForbiddenIcon from "../../public/images/forbidden_icon.png" import GreenCheckMark from "../../public/images/green_check_mark.png" -import React, {SyntheticEvent, useState} from "react"; +import React, {SyntheticEvent, useContext, useState} from "react"; import Image from "next/image"; +import SessionContext from "../../contexts/sessionProvider"; export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: boolean, userIsCoach: boolean, userStatus: string }> = ({ @@ -25,35 +26,78 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: const [isAdmin, setIsAdmin] = useState(userIsAdmin) const [isCoach, setIsCoach] = useState(userIsCoach) const [status, setStatus] = useState(userStatus) + const {sessionKey, setSessionKey} = useContext(SessionContext) + + + const reverseRole = (changed_val: string) => { + if (changed_val === "admin") { + setIsAdmin(!isAdmin); + } else if (changed_val === "coach") { + setIsCoach(!isCoach); + + } else if (changed_val === "activated") { + if (status === "ACTIVATED") { + setStatus('DISABLED'); + } else { + setStatus('ACTIVATED'); + } + } + } + + const setUserRole = async (route: string, user_id: string, changed_val: string) => { + console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + user_id) + return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + user_id, { + method: 'POST', + body: JSON.stringify({coach: isCoach, admin: isAdmin}), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `auth/osoc2 ${sessionKey}` + } + }) + .then(response => response.json()).then(json => { + if (!json.success) { + reverseRole(changed_val); + return {success: false}; + } else { + if (setSessionKey) { + setSessionKey(json.sessionKey) + } + return json; + } + }) + .catch(err => { + console.log(err); + reverseRole(changed_val); + return {success: false}; + }) + } const toggleIsAdmin = async (e: SyntheticEvent) => { - e.preventDefault() - setIsAdmin(!isAdmin) - // TODO -- Send the isAdmin value to the backend - // -- If error revert to old value + e.preventDefault(); + setIsAdmin(!isAdmin); + await setUserRole("admin", "TODO", "admin"); } const toggleIsCoach = async (e: SyntheticEvent) => { - e.preventDefault() - setIsCoach(!isCoach) - // TODO -- Send the isCoach value to the backend - // -- If error revert to old value + e.preventDefault(); + setIsCoach(!isCoach); + await setUserRole("coach", "TODO", "coach"); } const toggleStatus = async (e: SyntheticEvent) => { - e.preventDefault() + e.preventDefault(); if (status == 'ACTIVATED') { - setStatus('DISABLED') + setStatus('DISABLED'); } else { - setStatus('ACTIVATED') + setStatus('ACTIVATED'); } - // TODO -- Send the status value to the backend - // -- If error revert to old value + await setUserRole("coach", "TODO", "activated"); } const activateUser = async (e: SyntheticEvent) => { - e.preventDefault() - setStatus('ACTIVATED') + e.preventDefault(); + setStatus('ACTIVATED'); // TODO -- Send the status value to the backend // -- If error revert to old value } diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 038a7a94..55ec9d37 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -13,7 +13,8 @@ const Users: NextPage = () => { const {sessionKey, setSessionKey} = useContext(SessionContext) const [users, setUsers] = useState>() - + const userSet = new Set<{ person_data: { id: number, name: string }, coach: boolean, admin: boolean, activated: string }>() + const test: Array<{ person_data: { id: number, name: string }, coach: boolean, admin: boolean, activated: string }> = []; // Load all users upon page load useEffect(() => { @@ -47,8 +48,20 @@ const Users: NextPage = () => { if (setSessionKey) { setSessionKey(response.sessionkey) } - setUsers(response.data) + test.concat(response.data); + test.forEach(userSet.add, userSet); + }).then(() => { + getAllUsers("coach").then(response => { + console.log(response) + if (setSessionKey) { + setSessionKey(response.sessionkey) + } + test.concat(response.data); + test.forEach(userSet.add, userSet); + }).then(() => setUsers(Array.from(userSet))); }) + //TODO The code above doesn't work now because the sessionkey doesn't update for some reason. Await doesn't work. + // We need to disable this warning. We of course do not want do reload the page when the data is changed // because that is exactly what this function does. // eslint-disable-next-line react-hooks/exhaustive-deps From a6e6ccaa7b1953bfd72e6ae11af9f985d9ad1118 Mon Sep 17 00:00:00 2001 From: Huan Date: Sun, 3 Apr 2022 23:58:05 +0200 Subject: [PATCH 129/827] fix: the admin/coach icon is being reverted when the request is not valid. Updated the route --- frontend/components/User/User.tsx | 37 ++++++++++++++++--------------- frontend/pages/users.tsx | 3 ++- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index f681af4d..c0228486 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -11,15 +11,16 @@ import Image from "next/image"; import SessionContext from "../../contexts/sessionProvider"; -export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: boolean, userIsCoach: boolean, userStatus: string }> = ({ - userName, - userEmail, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - userIsAdmin, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - userIsCoach, - userStatus - }) => { +export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: boolean, userIsCoach: boolean, userStatus: string, userId: number }> = ({ + userName, + userEmail, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + userIsAdmin, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + userIsCoach, + userStatus, + userId + }) => { const [name] = useState(userName) const [email] = useState(userEmail) @@ -31,11 +32,11 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: const reverseRole = (changed_val: string) => { if (changed_val === "admin") { - setIsAdmin(!isAdmin); + setIsAdmin(admin => !admin); } else if (changed_val === "coach") { - setIsCoach(!isCoach); - + setIsCoach(isCoach => !isCoach); } else if (changed_val === "activated") { + //TODO This doesn't work because setStatus is an async function(need to find a way to revert it). if (status === "ACTIVATED") { setStatus('DISABLED'); } else { @@ -44,9 +45,9 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: } } - const setUserRole = async (route: string, user_id: string, changed_val: string) => { - console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + user_id) - return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + user_id, { + const setUserRole = async (route: string, changed_val: string) => { + console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString()) + return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString(), { method: 'POST', body: JSON.stringify({coach: isCoach, admin: isAdmin}), headers: { @@ -76,13 +77,13 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: const toggleIsAdmin = async (e: SyntheticEvent) => { e.preventDefault(); setIsAdmin(!isAdmin); - await setUserRole("admin", "TODO", "admin"); + await setUserRole("admin", "admin"); } const toggleIsCoach = async (e: SyntheticEvent) => { e.preventDefault(); setIsCoach(!isCoach); - await setUserRole("coach", "TODO", "coach"); + await setUserRole("coach", "coach"); } const toggleStatus = async (e: SyntheticEvent) => { @@ -92,7 +93,7 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: } else { setStatus('ACTIVATED'); } - await setUserRole("coach", "TODO", "activated"); + await setUserRole("coach", "activated"); } const activateUser = async (e: SyntheticEvent) => { diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 55ec9d37..4209019e 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -74,7 +74,8 @@ const Users: NextPage = () => { {users !== undefined ? users.map((value, index) => { return + userIsCoach={value.coach} userStatus={value.activated} key={index} + userId={value.person_data.id}/> }) : null}
) From b452ca33ad8f6829d59fef6e2ba3823e48460998 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 03:26:43 +0000 Subject: [PATCH 130/827] npm-backend(deps): bump body-parser from 1.19.2 to 1.20.0 in /backend Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.19.2 to 1.20.0. - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.19.2...1.20.0) --- updated-dependencies: - dependency-name: body-parser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 382 +++++++++++++++++++++++++++++++++++--- 1 file changed, 356 insertions(+), 26 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 24ad0291..711b8305 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1932,21 +1932,89 @@ } }, "node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { "node": ">= 0.8" } @@ -2099,6 +2167,18 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3190,6 +3270,40 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3445,6 +3559,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -3659,6 +3786,17 @@ "node": ">=4" } }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -5409,6 +5547,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -5836,12 +5982,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -5849,6 +5995,37 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6173,6 +6350,19 @@ "vscode-textmate": "5.2.0" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -8544,20 +8734,67 @@ "dev": true }, "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } } }, "boxen": { @@ -8673,6 +8910,15 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -9484,6 +9730,36 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + } + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + } } }, "extend": { @@ -9681,6 +9957,16 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -9838,6 +10124,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -11168,6 +11459,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -11462,14 +11758,38 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } } }, "rc": { @@ -11704,6 +12024,16 @@ "vscode-textmate": "5.2.0" } }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", From af276a0625e647d40d645342d24c649d46a3a150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 03:27:04 +0000 Subject: [PATCH 131/827] npm-backend(deps-dev): bump jest-mock-extended in /backend Bumps [jest-mock-extended](https://github.com/marchaos/jest-mock-extended) from 2.0.4 to 2.0.5. - [Release notes](https://github.com/marchaos/jest-mock-extended/releases) - [Commits](https://github.com/marchaos/jest-mock-extended/compare/2.0.4...2.0.5) --- updated-dependencies: - dependency-name: jest-mock-extended dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 24ad0291..3d1c9903 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4519,9 +4519,9 @@ } }, "node_modules/jest-mock-extended": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.4.tgz", - "integrity": "sha512-MgL3B3GjURQFjjPGqbCANydA5BFNPygv0mYp4Tjfxohh9MWwxxX8Eq2p6ncCt/Vt+RAnaLlDaI7gwrDRD7Pt9A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.5.tgz", + "integrity": "sha512-cQjKRqzZ/hUyy3AdmB7Aa+0DB45dt/eEApwfZFZzup9oefGnw2QodW/BLR39aCpPkK0Qb54P5OKYRXJ98kQ8cQ==", "dev": true, "dependencies": { "ts-essentials": "^7.0.3" @@ -10473,9 +10473,9 @@ } }, "jest-mock-extended": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.4.tgz", - "integrity": "sha512-MgL3B3GjURQFjjPGqbCANydA5BFNPygv0mYp4Tjfxohh9MWwxxX8Eq2p6ncCt/Vt+RAnaLlDaI7gwrDRD7Pt9A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.5.tgz", + "integrity": "sha512-cQjKRqzZ/hUyy3AdmB7Aa+0DB45dt/eEApwfZFZzup9oefGnw2QodW/BLR39aCpPkK0Qb54P5OKYRXJ98kQ8cQ==", "dev": true, "requires": { "ts-essentials": "^7.0.3" From c16694ac35ff9ef0252f599b7fc9df737a5c787b Mon Sep 17 00:00:00 2001 From: Bram Devlaminck Date: Mon, 4 Apr 2022 09:40:16 +0200 Subject: [PATCH 132/827] docs: add newest version of domain model --- docs/Domain Model/Domeinmodel_version11.drawio | 1 - .../Domeinmodel_version11.drawio.png | Bin 145246 -> 0 bytes docs/Domain Model/Domeinmodel_version12.drawio | 1 + docs/Domain Model/Domeinmodel_version12.png | Bin 0 -> 152300 bytes docs/Domain Model/README.md | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 docs/Domain Model/Domeinmodel_version11.drawio delete mode 100644 docs/Domain Model/Domeinmodel_version11.drawio.png create mode 100644 docs/Domain Model/Domeinmodel_version12.drawio create mode 100644 docs/Domain Model/Domeinmodel_version12.png diff --git a/docs/Domain Model/Domeinmodel_version11.drawio b/docs/Domain Model/Domeinmodel_version11.drawio deleted file mode 100644 index 503d6319..00000000 --- a/docs/Domain Model/Domeinmodel_version11.drawio +++ /dev/null @@ -1 +0,0 @@ -7V1bc9u2Ev41fnSGIHh99CVp07qtT9007dMZWqIlNhSpQ1Kx3V9/QImgJawkQhRAgAlmMhMRJiEK3wKL/faCC3yzePmhiJbzX/JpnF7Y1vTlAt9e2LZtuQH5r2553bQg5NqbllmRTJu2t4aH5N+4abSa1lUyjcudG6s8T6tkuds4ybMsnlQ7bVFR5M+7tz3l6e63LqNZDBoeJlEKWz8n02q+aQ1s/639xziZzek3Iy/c/GUR0ZubX1LOo2n+vNWE31/gmyLPq82nxctNnNajR8fl88fXz+ndF++Hn/5T/i/6dP3zH7/+ebnp7MMpj7Q/oYizqnfXzx8/PH35eu89Xz7N39/8g//8bJfNI9bXKF0143UfF2WeNb+4eqXDWD4nizTKyNX1U55VD81fELmO0mSWkc8T8nZxQRq+xkWVEASumj9U+ZK0TuZJOr2LXvNV/RvKKpp8oVfX87xI/iXdRmnTJ/lzUTXCZHs7dzzUT5Jmi7QWcUnuuacDg9qmu6ismnsmeZpGyzJ5bF94ERWzJLvOqypf0I7yVTaNp81Vi/T6oiryL63s1M8/JWl6k6d5sR4afOu+D24d0s4JUwNnPUrxy5aQNrD9EOeLuCpeyS3NX0OrgamZg27YTMrnN4FGXnPPfFuYg+bGqJlEs7bv9ut+J5MuymZkcNrvs/Hu9yHH4vs+gtTO10UpEYgsquLrenjLbfEkH7Z+6lvTWmhPEGAEBJjczgovGedqS1DT+Kk6KKblMpok2exufc8a1qbl9+Z31k05efYpXYvIPJlO42wtQlVURRspq+VmmSdZtR4I95r8I8N1Y71zL1zyQjfkGr1dk3/17UV1k2dE2qJkLT4xEeHnuBbjPYJ1dDJ3C9brLl6n4rotRjuAnoqeDdCLF1GSfnMAHlle5tUibT7KgplqbGUwYwDzLKl+XD0anIXi7HOqBWk4OwDnp6Qoq1+jRWygFgo1shw+rLEtCWsXYJ1GBmoZUGNOLS0Nah9ADSBOk7Vp0AwH2rtt7sB/QZCsu6OA/1HLw+0lAkKBoVDgPQKQRo9xep+XSZXkdf/F5l5GMFRtvZDHu1hbklANAKoP1Wpa/0xj++lj+yEUMMYf5jXGsHNYcg4bf4gx/nCgr/EXAhH+fo2/gFuydDH+ECSflmvy6aMBcTSmnQcwnMVkNS2+OQSFbgJPxpnXtJO2B0SQaFsWeUYGpTRQC4UaoZ4KVxzWkJZbzvMs/nW1eDQzWzTcvOadPLghPZclky+ZMeWFY+2pVtcIUnTk0yJLDNJikQ5Uc7EIEnQA4++dtTndZkKctKs01gbBDfcdseBXdRTEyHmbTdMv0cvOjaeyOYMTND7rLUcu59wPpM19yNgeJkY2U54OWZZv5GVrFJum0WoCzsWgnVkjYlAgh3ts12aQ7ou0cpoFQarzkDY/zlnzQTxqrX46vLzsypFoqvPCYCATCtCNs+lVHQ5JrvLleiJtbdvIHxvFS2z3RhPTu6dJtMiz6R/zJGOUNHJow4ckpTvieDqL6U6BYDHPZ3kWpe/fWpmddfySVH/R1yCf/67byXzdXN2+bN12+0ovMjJqf21fbD1VX749tr6iz52mw8t8VUziY4KyuY8MwCw+KlANPPXQHBWnIk6jKvm6fdNR98t9Lf7bvp7Q29lK+Gz83uYnNY+9CR3sCbsdPW1+NOiJCE30unVbM0GPvDITm+hh//ibsQ+4ocvMn807CHUU2ZC9pEKyNcMIvHf1QkX3nlsL2NZc49KOnEsVnfhN8HPzhRet+cC/DaXCzC2C5y1YkCE8ezypTtBkQIcdT0jBva8/R2t9aew6BXYdG3Qd+Jy7fRTKsv1tSN4Zs65zRcDcyOti1tmQupsUcVTF0+vXw75xA3hfwJVbdzYk9f7JH6+Wy5QgU6sAg7p41JVHS9uQpZvGk6Tcp/IN2meizR0wLQ9uSNYtcrK13L/HM4CfCzh3nKQ0wPeEJ5Yfkiw6mN9k0O6Ntqt604Y5ODzD0PbGN+Dco8miaDFkkO5yYqN+KvdERBkDfQAD3WUC4z2PdwlA0tYASIsZA71rMcD8yOtioGNI13VFrhu0+6Kt3DrHkHZbRmX5nBcGbeFoK7fKMSTfkvJqukiMjSYcbPVGOYbMW1Le5NFkbtAWjrZyixxDxq2My7IhWMm3YvQWZmCgFwi9evMcsm/RZJKvaussqlYHk5gM5r0x5zXZ5WFuguZkWmGW4qg5h4Nx0z1qjn4+KWoOXYwhao4aydKj5ixvlwjyXSYAgztqrg0TOtSToKi5gHnjrqA59n6/ma1SY+YcTWPm6LRXEDO3H502gZI+kj89lXHFICQGE0gw6hF3Jw4UMZggazBIIBc4Hi3UU6P00V6CtRCNkepWQzanGqKz2Xpn0WyLVzrBLSFC6TPLeMBujXgVVeAzHYVyorsDZ/d7Qgcdfy/mft86Etv99vQAkxRSuNa7d5DRU6HOsG7qzB5OnUGuVRN1JgwUQeoMDQYJJETvi7iNS8NX5G8NG06+wGqoUi+th/yxIJ9m9acmysXa+9wRfPfgedBR3prbrFBA+/hE1AG6hwThIOpEh9hUiZwJPbOkut5gkgDJ0p92glIBkibsQUVBwNDmrQ0VHikIeB4TA8lVE/fQudr43NDrEvfgQEK13NQMNYEP4uFWHvjQHoUC8f4zT1dkJY+Lj9lTbqAXDb3yKAgXcutEPS7JOCSPSZpUSfztlf2Thqb6MAe6lGxX5l9lH6IJLPZs5u65aCsPc3Ah29Es2yayRQ7kysMbXMik5GU+MdsyCWArj2twIUcTT1cbo9wEsog3u3gDHeQBDqmYFvC7+GtsEsrEg46V79kgqdKCfrsqTNqoJI5F+e4NGt0t7n/HUWEwF455oHr75kFzu8X8IxmNpFpVpqyjcOAxrXTfCbwtC3gYZLU+AfO/pYlKlgO5cr1Oa8DD6j5Xho0Rj7dyfe5xBIiZKPTe+PLqbllR6B4k266qKprMF9/AUXpyXOTOe/TB3SsUglzkDlOSndtFjrA0MdmTVmrIuY7loJ1b43GRe5CbM6XcZKOu3FPu7SnlRkbRIC0aaeWOcQ+yctXr0tjmwpFW7zTfE/1iNu7i8OV1k8vakfmQcwPwmsQd4Yk7dIPUmbhDg0p5E3esd1ZI3eA0cYfmy50bfo+ZhBtk9T6Zgc0xRbYr6WgGHDJWUIiPvxp4wNtJS1WXvuNDllSX9B0qGBql7zBctURYIJOpR/qOOFDGlr7jjzkb9ZyThNTWRKCB8Z1KLeTUaaJrIoR9U00Rdnd78liKTJi68k9VV8wDbmObSa2K4OubSerrlkk6nCLSNY9UHCSnInKeaQRJzO9Di1hKtYgt3DQSrkaEmT2gpwNqRNgSARlabdZtT9m6fd4qAalQTVZdYQM67HhCvvG+yP+J9yTpGDeyggLzCNMCAZ2UNE3aFy4jASQtjRu5cz0IuaHXxY0cQMbLnOwtA2nlrmOaTWQyt6Rjrdx5HOw5N4Jo1mzPETIG7DPBVu8/Dval4RK4byMTyy8Bb+Vp1wFkoeJsatCWgrbyjOsAcmTLJtjCpG2Ix1t50nWw77TWclIkS5OIKcUEU551HUB+C8Bs4r9648ubiCUNXki3/Z6nUFcbrk1FVUPk8FrmgSwBCQ3XdvpCEIyPawsN1zYM0sq5thBybUajC4OXl16TFdAdQnat8Z8Zva4qFTO0WLXOu9zLS8UMIS9n9HrXchCefGS7er0O+bjlZj0wzhXxcKtX7pCQK8i6b7AWj7VyR1oIyThDvkqDW70rLTRUnEx8VadihhyZtrrHG5+SUqlJ1krIe4Ya5ebkxxuzLKDdP+DY6epK1CFpLmvihB2npLEP4GAn00VO4gqyIJ2pSwQ0nf/aZK5c2sxROfJyV5AF6Uc9wqjFoTJoGDWyOFg+3bXJCLNXQt7slXAobYJYFeDS+gDnaxPXZ1YIUWmQiK0P4KLjh0PDJxgFJEufQLpVF33SLgHaKBR7OHUC2U091IlAUAbWJ5BHXNdzi6fGr6BNiUeEeUkphGxpkgIpSONZ6F4XXG70dXEtIAsSkKbMo3TclfsYkAWJSONkkIS2ci8DskwFQJkAc/sVZPHOCI24BuBpW6tO871d2rSx34OQ3eI5vc13xHZlM11JLj+BEGT5tLGW6eBoYy1f2nj3EYn2MrUG9LOXxcEiBpVwOEzGXMVuvKVZW6tGowJErK/MZsMC++sA0JUoChdwE44VHH83tvLq6U8crdUqbmLqW/2uXTQ00mKDlf1ESNcKeAJhEVSL1d6ZN5e2PxxIkB4kRhexuUyFJ00qPCHMG51Ol18JNimkFg2J3L3MeNzga0MiI0gmltWKDKkJUJcBuHr2GEE+cfmWoWRAlwC6ehLZhhxjkj3lxcKc4C0HcvXh6siGbF9zxuv1q5nmMjBXXv0JUY5qG/Nmd29Oc5aGu/I6UMg25/tKRZi38pM0J6ENSS8AsLZs9HjPVGmVaCcbjRs8ZLPRHlMNH1ns0V28ZLTPcNGuL4eK9tlzy+ymUNPBF2Me8Buvvlxa2YbspTa0Mp39Y6vOj2zINmpCCYsb0oFHFDJzN5tdPWl8rF/rarpIjovtnkE9yOO2qp5FBurmU4ceDPEhNA5Lc33eIj0N97WnhDOLEPVlDec3sTkSi79J3a42V6ilPnU5MM1n6tSEfTU7shxWg0pKOwXubKsj7ZTdc3g7WwFZqh1Snvqo9mCkqh1DSlEX1S5sSAceUUjY/fbw2w0YVOOWHcAtC4qGtaFwymqBImpjGq/sKXQP5sdeG68shnzeaxwVBmvhWKt3yGIOas9wt70BVl0RFGFILMF9kjHvRJt3lLLvtO7QUNTtnkDi3idiw0BititRJh6II8bu0VcDDyBnCBsPQ6ZRGxuPrgDjs/Eg2aiJjSduSAce0W+A6xthlTmXt8rcKJUBYruyzi4zJ5zkxhpzcFgdB7cf0SFzDx1teTxxsIwt99CBTGBzisCncs85nYYQHKDaD5unEdrqa/04hhDswSE4IyQEHUgIrshSYOJ4JaCtnhJ0ICVoTo2QB7j6/AyHgyM0HHBvgNUX+XEgPQU32d+Q3X/aLqw7VofqP/lmOTqwzTvVKmf3i224rWCC1mX41jDEx98L73+vU3+HXDrXgcyjNnQBncsa0QXDBSs6kMDUhC4QB8uwlLDzvR48ojj8k26yNTp5hA0A7c8Iex09iVI+bNl3Cx3P7QAPDHPuiAtJTn3UiX4HjwynTlzIdOqiTkZ68ghlL7ZG9KedqtAPX+r1nh1jwyErqRhvBbx1AtjTgARKDOQWDYvcuT6082xELLILeUVTMV467ur5ZBfSi+VeLWDAPhds9VyyC6lGothnq2hmqjvJQFyDUj8uZMvS+Gts5rcEtNUX+XEhBZeU90X8FBdFTHbgm7lusBePvfpCPy4kC5Pymvzw/VadwfxszHlL/8jD3IPkFQDaOIf7m2UWbyCXPIQhCwYQ1tYFMOKzBlxeF8BgZw3YbNqx5fT1AWBQKYrtStRZA+DkACblR5eTAzxIjWrjBaBLwHfpBfAgA6mJF0AcLMN6Abwx1wscr0YJeRVKW4a7U6O80IUKtXVsaKUraoqK9jsjG4lyPMOuROWlslUFHa8jLxWUIbR3jrfZ1Tlvjw+x/CkoPbhrEm5XVOGvtHJ86dFIkw2XTeUNXvBwDEAOrPt4Ywe10X8dM/ax8Vkf0pMnBlL1KeogWE+2TFq3ovRP05P0FZpVHovRkDYTthT2PgEOs2lknAfAiVuhTg3gVFwttJ3NZ1cLtd4FHtoVj0CIeDi72zImUEOitvEhb/QQl2WSZz/Hr7DUv4nxUXCeVxvxqDBP1IfEh4nw6Vx5fH7stYnw8SGTcpeTKfHJJIvKgVx9cI8PqZ43JWAQF464+ggfnyNjxDgEewOsPls0gBYs3KJrY72OJSWIw9YcKMsU5FRaTBd9k0zdkKG6JCWZ+h113tn73WCII1wCCbmJQpx7dD7rQ4kyWc6XmOlCotEayKkEJsbfJw4pQZwnuSzyvNrGoIiW81/yaT0w7/8P \ No newline at end of file diff --git a/docs/Domain Model/Domeinmodel_version11.drawio.png b/docs/Domain Model/Domeinmodel_version11.drawio.png deleted file mode 100644 index 674a05787d5787444e2531a786d717a5c73da82f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145246 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%0ABO zbOwmQ6(Y!{Y$=enN+guqTRdyK7N07~EjpJBjS)+Heb`hN*5KQYPk<0~AM0y-yhTyC zmK#;pkd`mgg+mbuWM=wNHaXe4a|7D3=6sirKFe7q#tW;MM7vAZ?Vf|<)#Xd%$I&?? zw|uNn8@Iw7ZfdA6Rri;UpJ&%z1MGb^rF>5q*8?)I^y(C>}9Pr6~6 zN}B^r{=AENh&h47asxOl=$w&|WjrU&;(k&u23)YtPtg;^Nmh4XExqlHRB{hExj($I z?a0dxWM=(QZuFnwBj7qxz5y-|k?OIwqdhykqDM|4aD{IKPCjgU6Hu4;SvTg^moMk^ znd|#oFUe==Kd*n$vJ~GVcikE3bY(XsY3zE3jnUn~bAckHwyBqPU(xDYDx5h=75V+# z^C~@nEWEx=Bvf!Mq7-3Yqq(K?mUD>5?X`x!NPAWG;<%O|=fauk+B=x07gFrR0FSwr zHBLWm(~MmN(gj3DV)Fbq=jAd>PuFQ6Dwcn_?=PA+Qs12Nz?BWW#Rg;|-9qTSI7zVk z=ZS`8wk@Y!tYV5Qi_`8HZ)u5WXDyWR4{P+DKx$1Imrw%<2^5Q4VlvBDcTEhWXZY}} z!V9d;SlU0;7(sjkDTy~)oz8GR0*DZOrur$x$Ct}Amm&dguO#*D<8(|N5TACJuY41+ zLe0IR^xjC3&LR2>s6$az{uw||g|h*n{A#`$iNxq?6Pvdf92pd|3DJ}KaLR7GyBc(NXa?N1_VdFOGcUjPV8|B@Jt!8jz)nTatU3FE4Sc>U*W)O zF4s&80kjWgGot}yd2gTO@`0#I<24x!j-UT zE!tbXfJL|z7Ok1;Tg*0N^UsUUOAFk2oqK)- zFDP!voh1rlKCq5tsJNrREP2N1<7PcQCoahlOvHFdfO*xVhPYN8!YEMzH>8g(v`ckM zt+c&K)vMtpp7~=j^^O9Nm=b_V#-v~@eBFC0b1--V4Zc0!B_+1{zl*z?2;tDCzfy?@ zNo8K-jO~c^2{#v2cBCzW%uz)i5|)O@=!i-7FvoEcWWoCB`z~3R{uF zTBj75zI<2FT6oQ_2v%H|pF?T^HwMz<7hnFdik1U#toqP5tX@CAms9}UEy;Y{#sUPV zC*3fYr?ELwhH+>orib&FUFIdu1ECO6UA=a+!0Qx%P_#;Ao+l#LlS(p@0G|<#I49$h za`J&D$IL)oOr{IBAk3R^0l-AWiZvCvAf>Fp8jtfLV?DcPXSI>Tv_|ptCzE&hscF`Ah`)^ejAcY6e zN-hHjh`@Dn+7MB2sGJpDsCLnhjQR+tIo@OI*~W21&>imovMS?*uHDMWIRgNkwY-+9 z@Tnd5rQHQ>BHV}dOD19rb$Bbrp%UTO+pacy@1a+p&9<ub#yLt!BE{m^({|DUM_M9c@avN;~#4eGxOF9sbf_^cTb6=0g)m(kfeg4 zf}Rt_q8I0L02=Q0pd?ha6USog7k8cIh59)0 zdmIhFMx%aS<6GWkrtdYZMAH)B2(>GsvNWnNW0^)EsMdVGCo;9SR%i6x#)C%1)Tj^_ zjFTSooI?zrxo;CoSK)Mptw993jaJTT)sN4Tm}-MUqN}sZVjrd7&>18cPNVh8e*^$o z2yV#5#d0d(45cc_e|2Z;z7ckLTyZwoN$VIOPA8R?_g)C8D2P6>8$(V)5lI5UG9QGA zKP{sTB@}uu(}{lO5Q$Juox=XQP>o_0^&g2byM~Z=eRUfe;Upg<#G~!ImmVga`Vs2h zOV8oU|5tDy5Sk1cfKkPLdcbsK*Z`ACbWu+vbBG=!7|qVMJcr0gZ-00zOTR*@~Lb$B>fxv=IQM#+swPvP^w!sSdAV{yu z>r(ja*CCmHsjSvD-Q#uSZIi~OW2tQa6vFx{iv8U93Pv>m@u4Il%of0WJ=AX07l1Uv zrLQu4UQm0#NHudmt2cDTbs(@}#AzFsul)n>0SUkBdYK2$E-agi$g^XOA8WF|0>odG zzJJ8siK34k+>L}v(|qP9Hq@P-hPr>-)xFAvmnlSM=NIKE!)9mkDqf1#{H7>nAal6Y zEguLt<~7RTkCqH0IZiJNdi4GmJ27ZqB~fk*h1U49mAg(mp9131SL(SxzMr04CkdbM zZT?9z`zjY}Xc}QTpt(HXuk^Lb(ihxTUYy6@S-ErTj;L1+%VnN(LQBXa7)&xs4Sa)(SH-`~l7Xm{tp)OSq=6EYa~xA-*+ zaL30hmfV^Y^6Q(>$~zV0O?-j-7B=44tLrFk zB&9>TajxtTP9@zW*6D2Xi{7e!z*f*%Dn06BJ1PWj6o#6s^}mbQVtWa^Vq6=~(@+yeW3^M!0KVY%Fe;{=XseqPD4NH|{ z!_(uEh~JJPL}H+|yIAXIP8*fMOm0}fyAnQ)^Lr69SFO`9 zt&=Orrl~T%c0||S1Y7V8NBL(;xh3|C=Lc?vToc7UY0Mo!F#=@RGc_xUhklCQyP%r& zL%Ey^+dYIdEpGu^-Rk9ag_hv1R9||N)06X}UuozGYEAjwk3UMtfJqU=DTA2k^NF12 z`j1~7DUKshdH4$fQr{`+^$k}wADliiZ+|!az%U@jfTGe%kK=A0U5=yA{>BJ0ssKLt zu6mK~f4AmvJnlfi4F$cK5Q}iy-P!yJ)TacG_-*q>=AtQ&Fa?!-Qrs6?c-SVYPAopQ z)%Mo;-)7{S6p=cr)uIUcbFJU*Cbu*K79Ow#pqA(Y0UKiTIhyq7spZp+Pk+_|pz8NYIw9 z|NIH|_xJgj`;6fl?OMP6LWaM;$m(-|U{&~D0@S9<;wz?NygwD3rlYa1*ory1{`yEH zl2S=l)g6vfu*8wQmE21X%~d9+6%`f!h>8yAK;1v|PmA2!QtOv3?9=xuUA4H6r|=Yc zuqKF~<+0Q8y$V-lZCC@9GZf4veSZD9nN7qbQOG^sew*0p)p#u~#%w?@WIc5neL_Qw ze&Ls|-j?jGX9(5-fPHiV8`B)=FF^j(Mu};!kv*$tCZ5&u1&ZgSr{E&Nn=6ySmWdt$ zK7w{$2Dt@a+8(TSP0nLe!vu2d?7%03x zR2^zpiA6HvgXJN;TTd*r7?>e&TgWF9ZgFP3j0mVw!3(sdAR<*Ly>iov&gzPlYG#mY z^&P2Y-+0AF`6&<$2ogHXR1tBFb-)nafu2LlY7~}yR{&B`rffLQxPD){G0oiU*6S&% zQ}_&b0g5P_PqA}o9*bi#zV6F|;1Xx9XqQyD27-LcW8>wWy(vfj^CWjkQe zFk|C(!O*r$X2bN$h41r29>H_^C)!^vcZhE8EUsZ4f-gE$Q;g8-=y?V?_MMA;p$r zyoPN9to*uD;rj@P>F$-tOYC_d<8M#+7u6FUl`ZReof@_I$D}JGRN|S}qOyg#=okHq%r6r8d_pa|)fc z@-UCD^~~hV^;VecWwwIU`z&H?DAJVUsF7op{+fD0y^}jzHn_WU-|R+`jeBK8t@nV& zD?gW4_#&6f=4;$~O=Iak2~yNXv~MTQD|7GNo+^uS!ITcE42)*r8Lny^sG&d&wi_yR ziHKi29&~s|G>_9fT~(NR94fiXPpquFswn zbkSJHQU}A& z8nlo##Y3Ur<|j~tHoklibru$uIIhH>5Qcyx>RKJ>Zpz zq`sp_bN~R41wz?=Dg3 zE-#$(qU`03mKq-7PRKE{IF&0lrXaC2?Gam4mTut`A?B3=zJ0zCxpwcqeNR63LUBGe zH~o$TNPB^HS7&-&W42z|X!H-Gs@4M3K=!;57d?CWJndy++u-wb24mKnh=^ThrA5Vh z=v=>O9w}gpGSo;Ip6{j`Q%<(ymHAI@GoRu4<~uh}2yeorR~sBRYUQ2-k!~4LyXb{{ z2d_Cd%=t+?8tAWc0wR2)*m?+LdRF#9*}74kzK$2l)jp)pbCp+F9f@1lySuI{As!ok!+l^_lmFA)4L7Gj-ci>cz$BnLu$Oqccoh} zg{}aSGIPQ)<(6$lv{`*=YYH+K3sGRj4y_kj}eY?6Qa*G zZ~c;dVV1S#hLf}ryVKPj%!DY?oZ<@PTP?RZ2cB{BXG@idT?wu;0^B<39F5!qouB$4 zu*=s56MHNgNvDfpqCN!>)HTSrzM9m3n$0X?XdMKOy}5DkR^r^iP<|2^ zu%!U%`>DEFPMZ&|XQ{Gp_tos!0oBFouM95uBJ+579jhhq8@JU5`-ax~@Ag)B?R@kP zHIZLKZ#UczR-wCf%6uHmMQz``vuSF9`j|WYqeQxoUjPf#Q#UEZ4+d-tLAr!cpN(%` zFD>`a=W@V)=W;e{v?-`Q9pCp0G`fPMm*!q`qEy`5(Phk@#3W(^-!QW&CF%8mNm;JX zLoIe|oQsWgAcf01w*=&V+ zm09Jo{yRXGB>}=vDgJmEKDb102?Rjm<-HZQL3Q%miq{l|6|jPDFpbr%rTOD}uS=uQ z3@KV9yIw0PP%z*FxmJIUxP7mlQSCADP9xpl(ukyF$N^vEV&_-JeJ2tYFcL6NGh%dI zSSKZNn90Uac^O>0%T32v!&Jje$H~ia?lm;m1>L*_5`~!CO1OWO9e9hj1agkz(B{yU zQR8Zm*^!#~%j}?;0cWOoGHjUO%y7yQRb^^6q$*xQ6j79dndum+w{fCYuvKm5&In8{ z#q`EEZj~-4*B#J8C4wnDrkV`X-XHmACGQcCgYIi=0bf+2UdY`yj)c@oATTA0^;$OU z492KS)QYKx3@<5+ZrF#fY!t^`YEJCfH7KSl5lF{tB$Y~hJUr4r=8FU|?!L9oTub!Z zM~hNFNq4gNT$N{J(^Fl|qao=2N_il;{!!zsb>$?YG{P$EpZ+h9))@nMi?`oZqNu^P z8bAY@*p5YkP-#XU;_6*Lgk9a`Q-SZ97t_34a*J!m9ZyFv#kb6=xxIY8^|pa+1NF$y zNw4k08OSg_t2hsWt>Qv8l?M4f752v@$vKS)`4R6#gg-!UK1E!Y}(d?7<=)?2?y;sn^Z|4``a&S)cXsU z@3U2ZD57}#UrXX}tmJ!7jXOqV3<*)w;(%$rTtex^5{xX?j6tq)7x%4rmbv6LZE1bG zw&-I`eNl@_BVRi!D%-@g57L`COeIP#h}mUkAmR(haPqj{`RXvy?tAl9^?pQ-`h?5Y zkqwr*NX{7>9OZmP?&5XQ%5TRA6_wvwf)ooPS&a8|FvVY?dC~1r)h=s#Kv+kBz;*T6 zKC3(<7qH1o>us<*QYNt6_S7xWVYK+L>kQ{{qRkzVItJP0J`?Fhhm81Y9@_`Jg0Y{3 ztfwVn0jQ1T#}UWuFT)N$prKh5T>O08xa1iiYJIF~16!?+MZ2^w6Mc0sE$_6xd%)zh zRv)<$#{yplFvx~>@01LRA{~$+&@uimOVn_qrch5>GB;RsAy5&nD4sjcd9HH2A%s)K*;kF0S)n@QdoUSJZmZq2p zN(WUxS0?`eW&Z&6^7mAT3~2v7lS3BkfEjoMmny(dc!-A2n>T|o#)?^1epcZl687nH z=M`we>}VB?S@e$+-_rMopley%qUX6>1mYaI9#bB$tT#J&eQm@5mUZk3wadA$4mUeg zHrI0xqNjVWZrwj(2WDiF^yp=0>%FwD^3NGYCM+Otjivg7YRj0$7SBIk7)23^`f>XH zCh7bNE2;cQJEU{jmLu26QwPIveZA>xD+CPF&Q>UJ&ALx|$&>{g=UZ zMx(coj@JR3=Di0pjhHrXApNj=wLOn=-nMDyJi6XK)7-Ic+AGUJt=s8ZEYB+`=^n5E zIY0{9t|ERHR5x7=^!bwGW~dx0CEj&f6f7A%Q&rSsF(8kXpPuD47P`@f@))$@i#``9 zd+{ZrcucGH4735_8fy}Dcwwa~E|trksH%wgxO7(MG$Y@i=ar;K@-9C@h<+{%-OnWg zxBA-8tJQcr#LHmDBmRz#mxUeZ?L&2=%!IFhwaDn zG~SqR^*RC~^`eL;NVKzUL1$(s8VyGi1L2=*&5&eQSy!d5>;M*k@riRyg-x40UXIkPpph(K*a+yX2&(O}+ zW(#IEz|HIVBe|?OziFM!xYf8v99eb7%NLsYLrPHX{fh9lqM^!m7#?u2@g~l6_CVA3 zo6Q|AmY`kfYPx!Nd4aMiXGm?o`YfRNgF$`?K8A(+MPX+l_@&#)P`#*2i$>I+fNRL~ z1r+~V%_f-;S7vtApxx1;P=@}xnE9xF#mwtN(s2L4mxvO6E?*~f+b+sX4J`}=&0ztU zME9*7X5sKNtq*G_8RI86uRjJ7V5n}3ZuNKoC-mj^^~tN~w*18WNqI#Mkp-H%{%Pwl zL{$*=(;bC)+LZzW|22VV; zK)QF*+YjEp7-FkBz0FDW$s1sCfCMZluLjbbC;JhAdB^{$F<_)x*^p;?%+ScisaMAIsEPgRG1d6oPc?=o<} zSC2;o>}BklzxN6j)~lA)G$0QL##)ImRav(FFMlu-bLuC0XcQ00)=wK}G&AJCIO6BzXg9)LSpqUG%I=d)eR`yDyg=&tW_)(3#~%e#CL#WNdEg zMunX)vLklZr(}*}h;4|Kmkae+&RTgu?nHN*F0tgKO01UoYoREGmxuCI_yIoeIdh7k0RiVW}i^@SJWM%L^WX8hS7 z5VeGwEV;&lzZ$j>7&Z>T!^*tar{E3K>a?n|of#!_&c2ixC;~KT;+bhky$G~~FuM3A zDKr{U%KvLn)Xu4YGE@k+&lO8Ck$&zllxSCV9T%hP05YQ7bOBC^Iu@r`yQRq2Db59M zZZc~3l3I>1YDD28j3pa$c@fMg3z*U8!N_v{DegJT+3%#D!FhB|e@prZ4Gmy;I2Jsp zA?BB}D+#|W?OR`}mF&jq&wJsSR4K^@&>yQ5dS)ZG)l;MNT`A70EYELZ*(-S~b`DZx z3@(ksbW8f5>`Q)hgJk@vlrff{#!ka>m*kPTA6;vyB6a1g<0%`^(p21@o&_pwW=lFt3L9;Z7i??rg}Un+v2L2yP&Ovpn_JvxjK;;z z7T1(Tjdi3B$qIBQ4Ul!$*_Or6ZCxcUiix+zCc39=@-$up3ThCkTL!9Bu1X5=h->y! z7ZX|g6hL*ct}2DgBweMu+>x@I;cqs1>x6lc7pV>+MsKfe9hL7$ohOB%8|0Na>~=n# z8RJ4ajXR2Dio_CUq4lzGv1w11MM^r?pt5U%6}7TD@aW&OC_E=>ytkuF^X}vL2j(7E)m~3B z&IonId&?qy=A@g!o4z{zdi@WsGUH+;fE0lX@|wuVR0L9)t?sL3%y{hL=l1?!802y! ziq#NEA8j|S>-K#$?v)>7w!*-k7dx0YCJg36FrqBHdF)m&?>b@zUrb1dT;MSw#59-I z-XvJu57=ybPHj=7e2@?PIGzD_gqPLc6Nhe^rb+p+Q$*FXhD5I3YR{(f`JY|dQziWA@-E~F;1m047Q zo#P?3TPEpq8RA-A23`cquqpFk9<#t!=hK8ULn+p~guwZaPnP*il~T&;?B^dVnyUO_ zIf84RjB2kgu*3;jnLj`YMU*?xwm$#g&L0JmJvI(eI93qRzfUZtMfS zpPPN?rsN4aztp9-G;17D%x7-Q>h}77{Fwl$k0e@vh=Dzov8l!%;03bpAKsQ}ppyBc zW~~g27;FDMhu76MO`?vN*d8c-%&a4hY|*+%5UMObHYFMbqSN=eh3_D?m z3eHC@UYKUi%+@MWw&49BG#+@H7W#4TbcD(i1v-A%zi#X2g6%Usfo66d{l!+M-;(bA zoQ!ukFYdi|y~iHSPY?i7gT&Lo2^-*!|Ju!emzd+AG2ePk#?&<%{ za_sxrjCBNN<7b;Pkg+&)bu$Pp|CO2FNhgtzaPHcI6uV1cC*hfe5cK)iJ49&{e@}<`y?UUvERaTL z?mWAawdUQe{^1UP8E2b)Csu@nRn$sXr!O42JyRas`uq3)HYnADq*S6`{2q#$;m;?t zVP6XL-`CM;<`kWS3G&ED=%bXAN^vxzR_Qh*>!6`RAy|KZo7wGid0B@#H~*tl=b#?o z#&+5jwmt{C!YHxdYP)guR^170%$(Is>*F;>ZHS@(Pio$KmT#|dvElcCegpsTrY426 zVy(lT1=O91gi9V5wc1wTP*4Q{#MQUcN9hpnNXN0lQ&;c3Gi+zPa_vaT0!nI$9O&n{ zHu*j>%S*me^X>H-p!(W&*J9Lmy7l=M$FeI)zC5JRm(`91uX2^$mjf^9y?&fQrlibi z=Ub}5anw^r&Ll=Nxq_sZ=dS6j_WRcKdSh(rFhrrDHSc@9j~AH9eBdF1+_UBEmZcb4 z_hgUz@Z46fiM^q$%qa?dZ^W7!O=Fi~tEo3b&p#ydBE1_Y9-dviLkPAZQ*&CzS_4Z-_-27T-maQv9YHr%eWZ`*Q>e}x2iEf!2vy}OL{8m|McIg_W z!Ot^~yztslG^aAM|g_fL*9m5AK4Hlmm?>YYbGU_La=!+sjo zZ|*vBa-yA!s$NuzNn2ai&s7ZXU$CE_l5NF~K_w84pD=X{> z!?O@hoDtx8iFerHF`|K_{cy5k`w{L03J+(2jSdIT zZlD<#DhN#Tuuf-m(jxMW+X{@dPDYVwCu^Ymp;0w=WuFdsY|I1I^n@3jl~T_%v)T}v zzs-os&o_Y6&eftL)uUYGvw)-F8;RxxNgq2-JV~~bCsP-@%wL8dw&t)<&y5gR+~aau z+0K>>UD(Kvli)U4^S%|?gxkJbW;?7(W!J1rOfCg{aQ_RpLck{}d(*M+xx+ScD9P&{ zm4R1UHbWWQd$hJVomMW8+3&8b2~tKf_o*zs?Ar~MQ|C*+p8GlX8TIJXY`It0vntB5 zQI$?MH2jW+bHqC*uri=)7EhP+oMT6~?;H$qMj@$8B`v?O2rXY>(A>3C+nNi9n&i0M z6R1G~K#kXO#gE(0h5F*}IgvNj97FXFzRk-T+6)f;(n1Ngy=TOm({6{1`^OO{1T?BaW4m-5pL>+_7H#elYvsN zMCuy3{dFfjsIPmuW8$g01ZJaq|5-+#H>NnmpkHo@zf_e^zm@2Z+t{rXU}k5}|0bQg zTsWDE?rxyn$CFi$OU>KNbsQ<5}#N#o;D%nrE)skGl_qZ$#A=Qd=U(iJJ?TgcOM#sqc zO`2}MNLerCf>%z#9Jy>mix*V^`!oY?Q>Vng=!>dG) zMI6n`kwu;L+s&@@!84!5htCQO%3)5uO-3C_A+Cf{1i<`o&oLHXLt@cZ$ zcX48o$I`S_1C{dYR*ROfV??MRd=<=y(ymB2?>t;jG0R_pPLS)@TS%>fu*+cKNe?`H zPJ`2?wW`)na(rDC7pe;J9NMB8LB@}8+~u!QX9tdho$o`u`AHIhlqZqI6T~> z55#9C+T_z#gtTH?Q3h9u;9Re#ak@P>JHtwYV@p1oNL~FY4bCZbeTmsJ z=abYP_zE4vo}bp4@RC|0EVYpR$u;0_rYRG4udI-krep-zz_8^XOIcJuaJnVK+P-)4 z9JIq+s$B#3*CB`>)R#TDmB62f;1tidPH>By2aZ&LNczQDQp1y&Fz?wD8h{lyM{}fa;jpm=zPN5pLN0!DDhd2pQN0t7=^oXP#s20};SA7I z7+`ZXI1+S)fwglt-UY9<;M!t&)NgM`bz%84$9leIf7L%wbZW}Pm-P*m`6!1TBwY{8 z)J%VoU%SPfQ`o|7A^uh550&zla%rLDgpJVBw0Ociqx9m|iO8yVN0W>3+v}E(>~6){ z2V$d?XtGu_;@ZoCy>3hyp>z0(uOPxCP@L)(JdD8Nz6RY z!PEpZ4<(7iS z&V9qPqp&`jQTX(^7c&2x?XqqFgWt+}|F_$~J4qsEt|L{z={_m1H1o7ZOT(yN@N~kM zKx8NuD?tqv_468o!`hFzX}B#)R17xDm%~Ocmeo*|@xrFyJg^0rB^6EJh`OH^pZ4ga zog9}WS0pD7_n|8MYill1CF#X;N}eVtq%$f%^bp_eL&NedIUBhKxWeK0S~@`t$T;Vi zj6o1jOzpapbm)x^+iJQnXS3DRg9OSv&YupSmmb5cXK?{_ji9?S}d%1u|| zQ_3&z1Q(`^N*LvpmwVS1#*9W)Yr_0_68OKR*f(-!b4zmm?+#pCypRw4G-Hw|{hc$2 z(4yt8Bv~bxk?6J?y)IV~>>}tkstxtM6K)P`Zo#)`fIR0OiI)eCe$SuIjhe5{-pTT? zA6T6n+flFnE9~y^&7yneEubT1Gq?P zuZf(JOT*jqGMDw2^!f4!06Uj9q!O3Cs@r0d;J&7TOdOr6@_dvhHz3l=X~;d@cCv3( zr-#F?lg?|As46pYr*tG=){q zOz^93)1>yeN)%~=*IIHtonTm6;+#JE^cO2ChrkjrUXnJl5Pr;Wv>rlT)(s(VNwd7?}z4XArxe>-f! zQ;%!Y37zAXX3WHZH5zlzO0R_+0X;PW(4z!&0q)XrQ?ZDJcztE{#RCmn6r{RuWDH}C z9Af7-1-=Ikq|*6N@iry~U_h<(-B(8<1y=@3aigO8gQZ;pOI;-NR@iAO>atDgX0c@q zgO6;hf1oYcf773R^~3e!c@FyAIoaF?dbKReNcjAr)*P>pO0K@b1zX|7PAp62R%hp& z){7M+I#4F1ivaq~9D9s~V&|;;Hz)fe@!CoO7%*yQ8Q*6#;!}4&XsGVMX%OFZ!Mx%WXoTkcrA9F=T!l{wHtM>~dE%0_J;Q z1@wD|bvxSyF@Cf81arqd8N1Lixiz^>#XY;rhT8&cZjkR?VbMc@ZZbGR=WIV4Pw|Mu zXkb&6g8on1iBSw`W6ZNkD26wkdU5aHrP}7d0z8six$8hb2ayC_h(D^a>YkJLg9;^m z)wuM*_qLhkB-?qT7mG}$rp{`YYP4(l_^jtAjy9g+D&C=5ybF2{7-QRxyZc7j*4Vkm zSXp$$XuF^;lyq?``wh5OG9DYhpquewJ9ghNQLlB6&mP`7Mw>&ke;)ok?8CWGnVxTR zw#X2)cAwHx@RX}st&WQ3PN2vx%#}u8S4~HA$5b$`hd;Y!;=D!q$*2<+fhco z@dbwrR2n!Y;!P#_di>sho}=P5q8$028&>%YJzw<3pNN3}hdNUu;^#csy6b@da~1Q- zvaP1#Z6w!pZvyy)YNd7E(#3ZLe})RO2lc`Oi~3ai_8r}U0CpQCLGCX2*Sh&X6&WYW z9ViQ(A|6(Ay13455p(Ol)6Jf?6a!V=2z7Rzb)JN`jGbFxT(2ld(1L7215om2j^?Os zNL25@=e8izGI*%{`{^sydf+9$opBF)jF)ID?D>9pNf>m*V<8`wD@UK&O|KQ} z(B^sEnV$@m>qq~oXnzkXl~jLn^dh1g_>&rl#$=J3s^A|)xpKL?Ngl*G@nmWKoVQ`B zRI2VjDy$88+`0XEqG1b^{x9Dcrr{C{K5kbGUdxY$kA zT=;j5?=+E>xSROEM|_j#b@zp}X>}`mmLBk9Z991We~Rm8IS?I6lhR|{*30I-s2y4- z=e-4u&X55Y0=`j;sjx5ruRhrXi`U%aK$>jE%eahhNjHgIeG{{HAc-sg*OEx`h*yo% zLG~7~T~Nn@m={tO`|kPV&6t}Ww)%$CxwuT2xy{-`!tTN+20;p2qmmI%`e$o!F z9QSl9tX=~ew|%>EC_Wk7z}=cXBj$Vj{%Jae{37<8dG>Z zKTyJ>M93*^=KLEl?BfSD zij$l@J)g7Ng%6$nzrP(IHB73O(^jQ;Klnf7ll_cZPHu=U!sQU+{~a*^RVNg{Ep?B5 ze*T{p{*Yr(rcBZ^q{tVUdi9Ty55M)~kSY*Z;3K8G|M!I-t}*Z}r&K{HyNO%-m4A$U z`0cVvlKn95<-zddMN-^}qH-huTuVkfu#4F%-6;zMSF@X}D{D*I`2jKWaX4(_75Vb^rR}Y*<^?|VkvaKe#|hH}k_1uxMx4}hDY5qK78O+y{p~Ez=Z!zF#m+1Z z&=e?72k&)-=7RFaCH~dxAsH_t&;-VbWhL48!oi_?{lLw=fOHb>SW3LcKc7gukTvC_ z(JtXh^5@k4TGX-{;M&jgfkfwKzK#x+^vsF3JVFI^&cVz3cBCe{MyaY$7k)vwCS)zD zm){qbUMh<+5R%)Jzmhwf>+xv51EA&3gbw(_bXureG+QiM+(Q!^UK%oe`)hzdUS&)Z z1ucJMjB9V`WZcp4i8_t#2|~BQ16$TXV)r4UynD$W59Xv>$l2`q)>PD0bhfx8dKgi5 zX`^`BEV1;B0uHz@C5^4B?qKy$Pw4iTT>`I(pKcl720E)OAm8c|FMwOT^ZpCJcnA=Y zj3*u&!7MMl{l;=|@vLD4Cu2eX?A&CILrCoQej*!Ab9CsJis4cMBwlk{K=!52XopXrviwN6)!V42*8` zwR}PS7}fsXcE=Z=>w(Izv{S48`tOc1;m?@qpR5(bnw88(guOS0>4Zuu&2mqWBCD3m z3hOSFnHM_-I2Vmtoj2fiGE^js9u=*`xs1iMIgECeMJ6`#{)ZqY1A^F6!CCgCERqus z6Q%5s!K#;>=Hlx=uQ@syRg&NhahtH3aI`2dEF6t0jpQOYLyQc>JBsgZ{nTMV4)BE_ zj(VH?#XK&{1C@4ot&xCQiXqDUF|={IxwV9$gUS1?hFpb0T(ZY}KLWnG_H=$Hb2i<> zb43Y5M6t&m2h9#kEgXavo?7B({5+QNhMRB zDH4xluaq!`zX;wXR8R5(F60v{ii8(%0%wmi_c ztnBrP#q6>jf&Z?aj|FZ5nrF)J!{0XN4^_>|4yeDIUg!TqJCb~*NzxM?dCKcmG<&~ zy#Iz@ssK`ZQt^)k??)RBzet$@n?sQ@RsSFF*ZTmFY=u|v-%SI65DB*DviRwLH%$bB zSuF?Q@bS{W%Rb|wN8QjO;>v+^VuK_{tj+zq5|la?cqve;Sp#R%XV*i=|>xZi| zD$PE+B(XRkvg;T2tKqGePx*3jp^}pqcFkkLl?{`tljq*F`-ejQ!xSYs6O4a*2?FBw zcxm1MyGCzOxExYgGdkH=r>3I4t0q-Oi+p%zRPM9D|TssS(yuTN+z}#R+7(K{%LUTfcP4imP>0woG zM`PY?4?9X4@CCI&1I{v@1h0>Cr%@?xV`~h&uG&U9bXW7c=1#P(qCSnB zKh2ZR!`EA?F19}B-4fcu1YrL*23p9Q!nbJS3X@Hlpo_D@$&t=`Y zJ~q?SBSND$zyp}()i$FybE#d5;7kh|7xBCItNlEA8#TE%3hu%x&*VCuEcC@s7%&o0 z3jHBU`2;ysSFh`csw105Y@|UuY{5sp06Gq?YQ{wXHa$)S$Sv9GUuPCsqxU^(TYl4h zdaJMU!=YnMGM;~I;s22K9#BnX-y5hR2#SCW8AXaR3IqX_CN&fR1?jzui1gk|hzN*+ z5DP_mliq6}0YVW_kP=Ghgd)9nNJ#Q7=*(}%nR)-W)?16UBwp^#x%-^6_qX@{_StM3 zKLfac3O|2gjY{Rpe`u`dk`0F%v-a3k=Ks*RMaP=-jVb>_pGq=Q0boBMRFdJ}^~o_n z-BzFck8A#7hyFMvgFnw-Rrp^){Y4uun*X}y#R}LtIvz5kzo@Uj87vOW#+CoNwjl|a z8D>Lr#4jPyKR>GGO$+?_;g=zN_XkX%SS6L~-*En4UNTv?&E{`I`OCF0CjgOnIi8#Q zYi8}Q(v>WZ>X$@F)@+I1`ga>D*$0@|e33=Nt5$E_ogozNy;_}0oLrH`uUrG+Ugm+#%baAK4gzrDssqQF{i0bBaesN4yIee%ut)cjP04P*=xb zu=y?as)kShp69QGbzb5{(mEIg>eOdhld6=iPi*XgSY{Hw#Z?^D-ZIe0HVo;$b2l&^yXuQPK~1G0bUhJbTT{ zXSh}K!UQ&U1$!0?OMK!Fbxq;_rE7k_Iesp-Y=mkgbF}gcuI#Sfx@F&dYpjCev_lh{ zUJHjk7l`M(phhz&3HY}|<8FSxjGBRriP3WcV$Oc6uWg+&SgEHZQ{EA;Wp$RDd(Yfp z6vAbvC598UqYwSx^7!j-s#<_jaA6aK>-X4WiFHymg$Gp3VmZyKGBNDp0+GZCG$F?4h@^;BzbGJ+k186Z9V4gMW%W_ce* z$oR1GNCe!|gV|hE$||cm$5mU5!e1WajX2Km%t*CHR`}t*LY2(A=BcpAM~mC4y@m$_ zHDROdLRSraPF~JI!OqCV+2t(H`vsX!|Humbj2X}XMqW4u2=RarFNx2K23z@?ScrXw zPDr)&&4U4zklAv#VI9DgP&k3b$uy7L&1d=4GHFNKzj1ZiqbUfxNiPH#8<{fAt#qV4 zk)w9?MKQiObyB(}({7D`ikOY?6y@&T&}2QFRb<@%r&;}BLDa{*JaJ^O)a~HlNc%)) zQ%F;qU5~+q(fORGNC`q!qINyrP!KZxgvUpDv;>Fzh&0^BCuuwUesPTU( zf9t@XVg9Uhz+tgsUw`CRhZR5mUt8ca`Nz`#Uv7jy;x$vuksA=n@m4+e^JwNOA}9QV z-=nB#$>gY^^I~C{zvxv~Rdcd8dHHv(X#wA+&uBaeN<0NL+Rk0cELJ6vfQ~2QF1f8c zJu2X!RTQaKop!(91R9i++o#jYS)}pz1?GPR1O$0r8ha`;`b#tH?F>5vP^vdu4HTIU zSR_FjqD}m&K4%hvV-tc$-G}Xplb|HRZt%(|(!zHPMIx*^)>AM%^{u{6Hv-umedmu) z%x3agDo%lth%wB@j|ng^@wa7%hoLD+emJW`&Zw?gm_UQbV%`OW`|=fLukY_D!qw2C zOrq{*9BTKU$$B(y{)(BhoPm|ErQC7+oMbYHq8Y}*;)h>`-?ksATx%D?2IdwZazJz} zOg6xO(gB?ezIl!uQiTt&B!WqE3)e6rdv09tGm-jmNd z^D_%?&=`ahGHoC)RLo|bo3}}G-=B`o&~06O#a;uZ^&MDR z{Ekw#WZaPVVt!h!Crd?7*(uJjlgd___SOM3g0FCkM1W6Oy8|3Rfgy`C0w@OAyJJ4Q z=`ZZ8S8H~a2kqnRDjte&_iEf2-q#;&e+`?Gh<9qfoSmT*moZds6TKneYG9LJo@$ed zMxuqSHJ!>Nh8Zpl|BNBXl2O+{vs0&>fBR$3SkE7ND?!f8)wgmpCDjWL6EDkJ`dLt? z;+=5Ol?PS2vo9WV(Ld<~lMZ?#I5nyvnjd|EgZtEZta*@2C7cWrz^>Yl2=#@JI4df! zIcq3#mur@Z0eu!V@jJ_kA?0KJZv95lIG~4+#;_gS$Qsxfu+uL@mx^Ru-vGA?yVV`1 z{G<#fZ1n{myC#*RmL|su-T^jgrhbKOOm`U>XBbadA|KF7rPsNQI~oDB zx6&|ce)G_3A+T4L!RX?0KgAb2_^4p|JX0!JI`tQO2_lKOi?XcRVgQoso^G4MRVaH2 zc?L5G9_Y<-uZyPdjv!A20t@UM zYge8zSbqz}#GGV$ZRNOi(xc^)HlEOshAR|I^u_|$?`_W}K{Jejx#|UmAB9^g?NrA? zc8xUSx7`?pou8JC5qiof(E_ec!`eKOHmyOuUb6`d^h1zG+pwQ*w#2D5sD^|Q4erGc zx0?oFcN|TNz}gMnvzhTndAVmk=t~PGhI!ksdPhC5;-Cx(++12V(9SaIqK(m>%jPQs z1~$qwznyJL6EU)cEETt;s+FVVaFc4qV@cHT*c0%lJ`CJCdghV6Z!YttHe@=CioNI$ zY5}kXo=4J~#c85vxCw|?K0vz8Pim<#O89#6Kk4-_eD1q5Xpy!(mKj<*Ex@25PS(%n z&Eh?ml9gP&-pnYkB23(y(rCHr^5|=i&(_?zusk=gQDC@1f{2^?YAxR0b+@RIHuQ87 zGiP3?x_aLB7fhvBZ(e9tOl)Y&D8kKP&UCl0o#3k))Mx%wv z!!QTv5QEM#P-1x}vj6>qVg>LQI~C6L!^ zX~5|gsUDvDEw6+=-gA1wkFxb80je^DA#K>x1e7Y*qBmZz?!kXWz>?ev*aqMiay2jM zwRNv$jbD}^9c;dtoi?&O{qnuIm$81Qhk-Fbpt8Hv$19rM#iUtg{5X0sA8uR;s6gqA zP3S}r!;8~%?+P>QsB#Nyy|)h5V`2AdZyHZb@8PWV11%YqNJS5FEoEwdwS;de0RXl+ ze!UA&)(^BFCqZ^zORTlBxViyNohS)jkUc{Soo;avdkbENR#iJ)d~4H!%bd z%fKSq)&4V|!&ac%eL7N`2sxJ18CGW|gWIazr1W}(Zfy-59yCKAF95f5@zs`PBt0i{jd5CU?0Ct4q3gAUqPYq=Q-TyBN9NlS=Jp`Q4ud zt0%)&XaJ)g8Zwk752F=bsWj_yrAe{qOq60v4v@p1T*bEcw`5#VVl=FDL-#jX{ljVK z(*j&Kw;Jay#^dCmTjTH2><#Rz%)2NpDG2+%U4{i}cT_bl+z)CxK__!3w&H&|O?UJo1E|wWctXxAvapf`2mS@fS1cG`QQh3d0j;U!+lg@KXlPLrI0K zAQlU5z-&N4j%)Rl(P9~1x{Ye`O!VT+t8BGl_yRf+>J>BZ9nHjQ2oKeJ&O;2@XE}kd;+)u+bKL*bS|E8)Kk$XM{rEgtr~aXgE?}g7V|<)@HfL z5N2P2G?@2`bbawpbN-6_w7GJIv@o4Z}rlQqQmDK{((o@gK)FgjVe^0AxBOh z>N2!p&0-YK~P8TA8Oj~ z>;7sGa2ODdHea9W_n)Eph#;JNv7iAWwv?%9DApxk_z+Yk$5P$eBBsY~#o1r~=}g=> zecQtB#^emTKs1}zCv&?cNp}k|sWQJW>m~o1Ww&L3CHEVN%4d`_%^YcTS;d_G-(Bp8 zOF$k||I_VNhx{Iqnmr()1kv?+XqAzd{YITxH5R1E)z8JnF4rO z*3bFj;_9M8dgA0xVz=-iWxduOqgJ7ssLrDy_QbucB(fyixuW~w<-9V@D*Rh}n{eJ3aPMewTAG{dw&I($;w8~KQ^QNaK*;aKU1M&`u($poieds|Zn$NDjf zm0Qgl0+?6R*p%ix9>S5 z@9sn+t>~@Zd;_7m6Mc8>XI-GXqaJ(`iNrd>E|vuW^ro#1G5-S*{TXP$2k+PUPefmOj%edufExYVm@OX}6W8TO+yoTI7f zsl16U?rZ?11Pf#n-OZnVDv5W_--s#kpG;j9YUQ8SZlZVwD+(Jf&|mZ0(eYJw;8X$e zoJ67uzIa0Bm+x-dDB6kY4eAR&a`GR*r*g*tEHLc*Wp2F1A(O|aQk4V{DBhQyx^UCe zB>Rq8^C!Q(iQxAK09wjEB#sIIXQ15}w9C=RRL*8F@hu*i1$yBLxi!)2L?D&)=y(q0 zQk@5RR@wpVN&bfb)V!5`-K9CR%mFL04fr+|zG4LbLz=4`j5(neWYIQQ`*DEwM<@ey zUU!b5NY51A_|wKnGE?6!5%gfNxV*gZJHYCDk|qD%U2TL`tJ*K?uA~#XG~YQ(9fNBu zA8TB+*L-;qyOKLBTaGG|Vrgor(zMF_raBUB5=nQ_Vd}}eKuBmS9S4|0h|5p>jrxcj zhf9Qit6FPIi2Qm5hw}V#8RA>TON=0>qXw~D@JJP)SC-ecaur_H20+dGERM^}p5y+v zUFW)zUpyHAnr}8fWKX!LUuJm@;W~f5Ih=_xp);7p?>FzArOUB`c2_C@guX_^6D>5{ zHzOzkKhE6}03>smd;`^YB~Kd}9jfo-m1FeLW&qwb{<=j)C$aaq%sxF*`?Nfhnfv=J2Rw7=Xr& z1;~{vRX8t+V0sUXf`*daU{2-xNu-rqD%i~xqhbY-Nf z9#S>&lCc5k1lRz~*SVKKjZ!x+Lx{Lh6}?#Kmt@k4Zc60q0H zY1Ny0F6{yVa?MeFgxi)~$%jTc81N~0eGk@KCIE2xx&r8(YqAH7s8K^joL-Eg;nTwp zsoOk55`?WD(?bB&(V`*oEnb-K&2M+EKS~f05Zf6rGX-h|Cqj7-@abpyuita_n~v0J z0N#UyvR$j0D;KBiga?lnq&?Lql`|XE zcp6|`Rpl~9J?``#m3M4MP=#8{Tb@vPeO3sTBd)V=Y{gi3tJdftyg(8tK1l3NuGZE@ z=`WtjCu5m?UWNDhL zP`(|Lt-%gZSzZZ^c}}enc8P*zk42ZudKwmT3z-u%Y^%pFjr?QnJ09jBSM)2;1%_6h zD(f5rG7*)Y^J?kAoVlE%k&BTg5gQnmf)=A9BmF@mFDa+Etfe$h!dC@Bfb~3mk-;6S z3UD(NvkQB*cT^40`ww1I*K%17cCLRJH*LMzB&#C?W`~Ftw)OJvZW}u!&*XHGH>LSl z9qJ~uJPVVO%Xl_zwIQOfz(|zeR(DI?9gWsFQ@LC*Y>$350K^m|K(C_wjByZ{_u;u^ zUvtuk8EFRG4sDP=-JozZ^&vw;e@Xd8b{*U12epS;^p&91}fLK#NCdXQq zE(@DIvuWoy1WD}br zI|qy_8UzLb@$;M(!z_aSzTW!QaTv(nFPwC9SmRWg+A~gBt?S$o*6X-;`m56G9Mfh^ z5D}GqnSG5a<^80|)Fu9DrfHcbyq~TYz$(}As#W(bm>;exjKNLY^{b1$@CccFv7T_6 z1}LfSqO_OoIc*3fY@=VHS)g0s&2Q~&&Pjl`V0ro&h|)JdAEU|28EQ@$Jmp+VsBl?#y^x;m{C+Z<&R)ZC+%~h)a$XJ2gF%?-y9m#{2=m;2Fkq~}HVrY)nw)A{*5bZM^NTKq#Te^hBFf2Aky!ZI zPJ_Ghwy4^mik`;0ASH+i;Pb$@0O-)x2LfYT^x=ELJQ6);v!ckDX%;SFo7-wv)d~}5 zE97C!=w6D$pk5+RT~M1R(lTW)6l~enu5rRMhXN+D?I+g1j4^Ovg4BJ?ytDRTm<=X& zHxKBkt9HDfg?5H+b;Fft^vV#SHIPF}p3&?QFP=R7=ngKkp+#sggVHQOm2kRU}PX7}mOBii+fV`y)7#Tct|k$Mr0p zw_?5lM5fwKl1`!Ihocftv({)N3f8fi8+F-^R65~g(62P1Yf-bVN)F7~3s%Q{-;~78 zR@niHTi5CaN%>j*vnarO7m1CnYst%-kuIP8=%zNXIW!}qQ4Bvzq-&*Az#ZZCw~YQl zW2o=X;7b%g0}eT*l3K)ZeSrIUdRi=JbFt#)uJ{pGzkNraw-@z$X!)Rf>~fAa;BWSM zyBrm5_{*LKyaDrfosUoa#Gf3V09lJ;Px0HSpYFjg$u~*2vtM8+rF>fTt#il8b0Q__ z!&0B#e^lo#A4X83;%NB{UkR@5Qt*xgR?`1j|l%8)N$-fn*XaXW}-4d$+v zJBc))1d_g)dTdbClKOevtAY!<3~ zkeV!K?4|riNf~gbf9wr_;PbTrF2DQFUHW5<`NsqKPoujEa=W);7&~(+kD)lSDzEwc z@~(#;H`Je3VK+8Px;TH8^p7!SSslt_hP2FveFLQ|LjU4lha$4|pTk4>8X7|{Fk9|c^rKZ@J9s|U|k z{&I`fEg*#vQZIb<(7}@|A){RJR-GG2?U5FqNj8hFG+TY+$v$L)qQ-Lr`}?!03N1C< z*vH_bsD*u5k{!a9(R~S_5w!F5!!Pfa13T8ks+nXXt8#R~nf0v#y%M3iopv~|IgBxK z$!WP`G2#~Qigb17p1V^Livwxzgi0XrXs#uXFSVxGIK`kb$PSg;!iI_4QsduQfU0w3 z2jseQIv+QWj5>$L__xqbDsF>v!|`I^cyKW>vAv0+#W7?Ml*vhQ&%B#=BCt`JDj#D> zUr@OovPK+c2|ON6ON9+536|Lpi`XKkDkh53=!_n-)b?0pN|;7Q<-W{yr~!E|t<}I@ zg%k>5Jv0sv%dT9iZcBv-s26)SDhW}sCqkZhF1*WZ96+0;EvwvH`s}#O)F0T{0y(!J zl*N+oa%~Sdq=ZIAZ0+gCgSPKQm?4`WK3);FJk&bQbc-@6u#M#6HQ*Ym)Oc@sDz30O zctspeo0$OB|GJr$u=a=c__@qk3lI0q_H>HN(ey>$=vr+cn$DZYMl2DZH_}lt8{xkO zn@I!BThg8^ztX{mtTG+8WBb!kxPx#V;%TC z0vxP>f#SYqsug3n?5{Yw{D%8nYC1}Tp&=a*R6M_r$;;HFFJ$ix@c?b<*}XgNw((rB z?$6F|Sgd^iWO01>Wbh&n%WqfsZO;zn_1*MMCCd-3>kPYc$q&EXzqXENk=OI;yJWf$ zVKqS%WuanpsC63fi(Xd5)&D|Fko2Pl!X17lEZ#@ia@B`qymUUoV*6bhH}?G|=}ecE znNrV{nrXfw5a8HGgTnAv;bXbG9iUglOFIJ&Fh?mAb>r9B_=`jNJrdsEIt}70Udnyn z5zls*^XQ38u_#QufgjjShDZm8Go|9Aja?o()(h5%-mbynQ{w2-lz#&7$$FKbaH+QH=wu}qjZ6>)q%?v>y36dU3PT@(_d+of@a z6}fp83YE)<;YRNMk@MF5Jh%ia+?4rpCk#qj-c51((5xmkIBYxMmgb0M+6`X4>B}He z(0|8Uf0jl>p3Nz)u>Eao{s9x(c+55JjQ&}L(4#k z>lXY9{T_y{;)|7~jb+(A4PgNeQ~}JW+^wFr>Qx92)^&dzqD1s^E^CKlAHA~Iv8<5( z>au|0j+IM!NTS_MLi?8t?uR_1ZrF_^I7C!g%Y_p>a=sLFOdY%ko8FAx!Ot7qpl1hR>K4ECFzj zsdU@4xzncG;bXUOszv?AzTELcyJcOqo_>Q)X)OyUzJ`S4t<%!>XBj{(2l!wT7p@4{ zUu*|2+AU!68EJ(V#&2(S#-ywgQ)k1bHtpkY+CiKr9C8bR7 z_kp(9WGf*!XH&n3uWsVTQ7Y|MQ1#8V`T})2S}*qS#4&dNHw0Ec5i9w zzuc1%Ji{fR!j*AJe7gTKO0H>ehD*@El7;8HxAMrewyu53tk)FKd^(U5_2^XoaGCK_ zmP1<{Q9PwQ>XqI(l37}xk6z*?H7G{_hUC30A}AXO#q8b*Vf;cv>SKGqgZ)jQfDZZ z;unAecf^QWOdBqRLRgC`$-D`KPyJX>C|<1Mu(o+aH(9O*_GM~Ovi~RkC?@mc18Qdd z7dYRplKhS+kB zh8S>k6(G{~u@LTGZ2uoB$X@~AIU87&E&UhG`pconceHK)s960~Fq*M|#1zuZTYkOp zQ|6L8z?782pD6wq?;qd$6Lq%*KKn&F+5XaS(a{~ioSa_DqH$0?c@doAv(7E_56J=8Q0{+YD!G(Z$c^M= z>rT7_vWt=Z$IEia12ZIiPwc1+ph%a@rEGgwp{>j!KeFQ6kp%v>h9lSjEP3@R^AYbI zSh<UUK$N)$q*eE6DtVZKnq(6T~RpDShij0!H(&Hd&xAcoQgHTVSu-Eno0x zpS$y8<|%H}#l3rhh>w3nbK%d2+}z$cEGmuQYvDEkDv*yVi2WD14)63?o)i9YXXMSg z`0_}(w($R2?@Itkd*;v-^$(%{Py~|chm=ZwefR$=ns^G79SHhxRowsKg8aFT|M&=O z$R~E`=n}Jk%+pWL$_H3Hg9gPnevJ1Y!%(#%bK|n>3_?ypU9HPc?{3{I6=Qgi4Lx{& zqm%di=n%n^bXtgGqz*1mI)!jE^`0umE>Trmxm(x?s{{8u?AAonoe9CRF`VY^0>@X8HtoQ-Tq?oR5I`a+O zXyP(E;~_r}xYtj)iu?sf?hS|Kes$mO?bMGP=x-f^-f&Ma0_w3+sK3PubA3w`5Y5cg zyNv&4MLBr+4;iQ)14d0fe*Kxt=?{LmYp&LPmA@zS>xx`0(}CUI>tK9AT^Kst?0euh z@N}8Jk;HS)%_&L0q=vc%jvWAt;kVe(h;%hPz0ISPl=WatZMhP6~zacsY391RQo>!JVo1NtuZ(xIHqa zD?8uPCMpYeB ze273ONbv8bTTqCchZPvf<1KUc48f`^>aiTt0wnNdFsgdl6QKB>)@Ai`*ysd@5-E18 zy`aHB`y2xlb8GeTM_6;918QIJyw7%1e?tR{neES-CC(pC);Yd0=GlJdFXFA9fvy=+$OkZso=I+Qx zMOW^Lt`?#$%~}Z$3-)x{2#X#QJ<`V<8i4YMxfO&gX==?43N5U@bEa~>*74iJ+57Cc zb;{MiUF_v^xTOlM%BhT88Ek+%%+F_02oFC~eSN9SJhMm*|ICy$3a4eM6#{v8EDe@6 z2$}d@%23|Ffgo)&B)ufz`*~7^y}4zQjNoT@X?=3YoqFR615fPCF(VvC1mnHGNloMn z8HY2AP6alm4y&~IUZf9GOjPY?_=si+EL9e{Zd>J->-a@tIGCNeHuLG?BM8Uhh(h9L zGBN>;WTZ0QEFE5w?p(4Fw-M}&n8Y@C-osd$8{CL&GwajsYlyDW1Un)D0Iu$ihmZnU{mq9|* zae_T}15$M0Mh(oPKEc;ubVlRFhi$tM*b=R_+ZH?Vw-2f&Zoo`7Q8v;^3AjKX`x^GG zv1gq{KD*-5)etXO#nH*ae@=OB{;@IWS^QBK>+X1SEk z4ty{jmT-H>?=a?Y!kYjDRu-kP1pLcHMh6z6PX{#3B}{OfnygQ?uF#9YhEdS=VyvJE zXEm{Pa{`7X>~IDm9gsY42%qM*p_A_*4ek9#6?KHmJ((}*#voj{w&r|6OD5Veq{VNp zi{&>?$M_kC2<@#5J2arY9WERgl5X*Ds-Vn!7}*Hv->WirXc*$XZ0D-Hg6Ax_Nwyy2 ziNi0K5fwe1?mA7#u44)KMrLuLaKCVonWCBo6|gUZ_zcQ*tF~r2QPlRz&Zv3l+IO~q zw3evvv*qtklpI-rqR(Xecde~ot2}4tiK#QeEid(hDi8wJBflT)EWR1frnw5=VG@uZQw%wJORqQaglGD@7UK6ww|P5oJPYlsghzAtk8>ct*pZ?JoY`A&Q`oq2+RSIfj5KtCj>ceJk#327N7 zLku}{y+WQdMT|y@(nHPG<}i=!AmZcL`1t%i#7{oe@O{`unsev| z@~-5%<_RRHM2Fb4>2%QKFvJZXwGpd6Cc)e_MdD!K2nOYH&M)5{brMLgnw+MLv|(Z|S|c8dw8}Tj$ha;AhSCcu&S78V`vDhZtGA zE+vm(j7}4JCz(XeDcxz9=!M+BSR(0*BSs^Wp_C@xtJg^bGWfoks1_-7tO9NEG)LGur zrMoHa2}7p!5Wt@{lDN%pIUu=uVK`7lGg;hsm94=g?d3dVx^QA@7aU%)k;A<;W~X0C z>Y!|Bj&rzLfi=fhhK^iU%_kSLH1CFCgO|4$LfW-$&oNieV%9dOI|D~|aXMR@y$4X< zws~T=70EBE2PR#fCcWTFZ-=xiH(EOuK_rQ@yXreMj@R=au73XmKF#5B3~edD?8sYo z=H5=N#Xz=j34zMni+=ls%uu|Y+*WB6+^%>dr@m*Lrt;vR3xYH@ZT+6qD4coqGT~r) zwpQYx%279osj*8(KKS*8+SfZ%kaB&q_O&Jpvj%7x99#9G1A|ZcI2uyzN)VkrJ-@_F zJePrm5YMm^m23_^y>mDN8#`l*B=AhM`Uj+*L-OQ#dk0xCqrl(dgm|yIHRw&eOJQsH zYGHI$#AEQDuaJ}UJrYV5rDU8d)8crLT`(Gf&B%VR=w-Lqo6R`nHg~b3b}q*;d}Edc z6}*8vyDC5!nRElhEp?m}X5$tT{GeSEqkWg&dRz6OkAmWuJ7VME*P$Q=T6B&_1lL9I zEO~mCbTUP`5m2^|4&l0N?Dwe3cu?MnF?*@-j8?6>A8?!ubI{_8oPH(EyUS*>;uZdr z*Yl`b88ti;@dqM0t?wA;qi;*!6JVLXO-u{mjMIP{?uxl24ZedX~ES-uI^Wlx;qF zPt(eRT39%6QP55e4|&KKVito=W9F=s8^)xpK7`1i;tI})`)t3#K*8(5bCpaY?ypwx zhK5B-4s^5|aOD~m-+u9hM*msUh^^#WsJaYs+Ya1bscop`ph6`wH&r9S98v8iZ|1G3*V&! zrWIYSbUUlZ&G%%?!D})DKfpv-_dd4qz^ljRp03DLm+p;B;zzE(J#idL*EhUs z{ULn8MrK;WT*X|ioz13Z*|`-lZ9Ae5t2%pCh{4c-qjpN#0ezczcF21FX;YfX)VU&_ z7l?MCtmzpiTf{R3J?M5mB#Jg6ZcA;ZX zLLQmJNUP7Sv73Y)d3qB@AU>&|dwUTL6{4-Zc13HEP*^eqs;AIav2Yxn?w3{B#Mf=; zZ%M2VuWm~e7msONqIVTqTEKb*y|wT13g|%3jm>b;Lvmr@th{Q-=5J*C9OG4HCcokC z_m;!PSm?*R3DcHdpE5i)FH(kD1G#qJt^QZh@jmKvHH4ctdK&h<1`n#s#!kk`1?v_E zK@0Q1QVMa6m^AA}ACPhrjHdc8P8s4^k)`bx)LtDKe7gw>n5h43jqHj16=GS zG34I7Wrbem;-#fwsUcPP_23KN3z4mNXdC5gWtpI)_KZ=cI|nL>Hv!z2be|5cR|?sZ zvn0vjPO{7{7D~WwZbM~UWW|YHqo&hZ`f|gOeZiHLSCb5U#q7^z`I8mgUdnXUDbNYm z3fF1ooNt$XMEbk})eYuqjNFI~EoP=*gxl-u8pY&&B7FwS?BY33T!cqPOsA#q2Mq}# zz-nGdfKM3r2*2hOrERFYBUo#+78j}gNr1sw+|&%+6_xs~47pglyZKff;q`QNatXDy zSp^&)-1i{a>4V&au@Zfeer{hwBIQyxYI2`%p{BpE%Qnc;t3hWi;$zpXgV#-+4mnfA zY_dWFJs7=s?oy%MP&g^aN)WALA6VpC0}%VZH!0CTT9vFSPx+I-ymN~fVL+s`0P=*;e@ zn)7wFbGw7=jkP3^)Tc&z)WmP$^&W;SnB@|+%C(KfwCpo0i0N2M4O0A+Hl|Y%eF8Um zTu5;!6FbLuc=SQh5h8Uz?&yUz>&+n>Qx%LAj4 zsA{Zn(9=+&a3LL-Nz-896f9YK>+*-5`cD4t22G8b_S%NGxJQlqLdHUZz3&A?Lb>ey z#*nS|o1nqKL1OD9HKrTzwuJ78c8zOyzruPJnv7;a7Vpt)imVU0v+aa>wNK zHH4dsKL)EAUjhWEmP}NDaG>NhD$g?8OrC^>J>HgJQoOUDX63e*+tSFLRt2uYPuXWB zXlIk0rS8oJx-?jrf$4G!YOO8d%E0@fZi zH2p`3tB}mIYa=Cxx$8&T>wNY?Ms6DXh+QBb8>(&BZTDd>MRm;V24gz6ZTYA+TJ!X5 zyr=z~hZ@PUU=Z+2FBw`o+W!Akb7Gr!qh}04sKm#m3>zZD@wd>mB=eA}2OOoEx2S{G#=n)Gz!KV_n(>NPx03S2>#^E?*Rl;8~q z>LwiYjp`CIpZ3UErKPmC0n1CaM=C@@`J?rW$Tpf!-3^UhBcJ7Dc8C zn;3l$iz9833K&;U^JTQZnsKok%rRf146WK8DvcgnErXRP&$=}{Gq9`ZsxDKCvcI}g zNmPdD_B)u88gYU4qXg07RBen|I(DilNE@8yku;WKhp(8}&rG5Vu)lI~B)VtoxO8Ik z#M?Al3}s8l8OSaev=ET_aPP?o13+g*N$)Nr<)O;Ushe-+`N|Bp{JQaChlxKOh~=2bA^&8@NEOLomVL!tjiiP(1!W{fL#sfxd{X@^c~n0C(GujYk%XZ0Z3^;4uy z^twZ z84`WHvU(~$XN4GS#AoerccxmZ3FMG2-MDg*h%I)%Z}1r0+gc?zqm|yAVr1fHRN7%` zZIzHq%Uea$`w)eMu95~t={z}eO2b$_o#uFOfm-W$y6)TL=M)0JU;phknYF};feXdE zA>4BH1B_Rwqq5Lv1Fo}jT$ejXbBWASa)RvogLl`v@2~8Nx$GLw(uyKWmsggSHaC1e z@&P4^UY*d6_yn)rkwNl~n^WHgi_fgx)m^a2OQZShcM!y`VU$<;hN5`c_YUI;R9^I3TW+h(%;6;TqxOFqXA2trw6n=jugsl>C~2vCj4fD>Onjh?NdH@}_>USqU_TDn4`4 zSva@C?*uWG5DsU})h=*04(=*(Q=Fxz9`mL)t&!;ocXerLo*vtO!q|VEbhT+Vf;&zq z28R@IEnrv`!7>dOIencx}=X@*nVBudUYn>-SeiRrhN5m`ThOIN?ur}n(60j zN%tkmrInNQzYsq7Tx0FZT3_j~q0Zwvcnfxzb4bv_H=4Se@cFK_@SSw1Ic^wN=x)b; z%t|N0yaq?J)@VutvMuHMhOOY zS#g|^(*`?09+hi2f8RV$P69+PdM2hbXy{Q zciKy4S~tnAvyw$BChH^_XY|$Pw-;$j5MBYm>ynX%SnQ5D|*T3i~g~-&c3Q+Bo zx&rjcX8=#~8PNQ^1Fd&(y>y^iKwQ3Q9y+8cOun&qRZEyglN=_V=ITs$wg$?gMQ`qYj$RFbaPA7Q@HxL7)BfNF0Df zj-PWpr&@%eka>9c`>WIbO`!~NACyJDZq1<^;~g7LJsYp{pV{mL!xT1qX@B1JMcBVD zS0|1ab|%2HCQD=X}VV`KO%q6&R30EOjzKn3%i07^Q+ z59}&}K>7XM3+&2U&w3D4hxh&R1GG)kY%?8vcMI{XUB7M3c|-jA;5`A*7Wn|F!glbg zH+cFn@zm|T)|he(Q0;E1ou_N6GsSaQ%UjLkFN$Q#jK`t*rQh=y7sDPz;-G;n%k5%nP zQ)7e?d5QEf0t|KK)&>7hjp^fP_C7^(M$$G>bz~gOjoqYYiah;;J{>Y>C zZFpWX`O&UU6?tCCh4Yf69!g_r>A}J5*{mf;P0smqT`rp5Bd!sE#Qdn{1gQ2W3iN@^ zc%LatxuFVN3CpOi97IaGnhE$Ya_{T7m1Awzbf3_)`v_KfNH&^B4%pzP@l_Upk zay_99kJK|MP?pggddDRJ5`vR&^pQ5S98_aXcX=&EOQK+%k+*E;yVEm(Kt;yG4>zqM z!(#5QI2pwEq-F(_+?OQqDzeJxzv*yzpz`QlB|V%p*s0EYqSGSH!vnPFw5XK+oUF~j zcA2}V=Yp3? z`})y+%+?sJX6W18opEbpm9Q9Z$_Aln42&e6RZ7Y7PvO1mDx-wN?Jga_&L!v4E2GTiV(_ zw@ef=4Vl>fr>#t{)j5=;K321mzt|yrr{bCkf!AdFq&$y4v%%jlu)cwB(vJDq-?)sAd_3suJD!t}Aw+lc4)M=wWFfgKE89gl?jIg%@8 z+WHpu0ebL)5{86GrU||%DH>AWgt7eoK@}u5{B%}`S!mRxDJ|7*{w!5DX+&zMJnMUe zD`?~Dg-^zv*C=XYm3`b27`8a+A~%7rQ5BHvIMNP0RQJstw`}BDBn?(htaJ_#BRVDQ zF8OOx?QNqpTOY7H)Gp90*bES;hr)|yH>bX>uP*-H(O>V!jJ@@+R(JqKn-lrF$=lI0 z7nx{oTuBOu=wXSWePez#Y&xLWRV1|0@vQI?^Xzy)@pACJ;qvS^&rL`+Z$?!l6|ecx zlIX3;ziz0o0+Pe-E4dODh#=DJJrMKWHn@V9K}x@kTdhV=B_hgixzCL`z4Yo>1u&*H z?^~UK_6&(ShRTjG53W0N(WOL1?cf@`_27;Wi%^-~q1Jbn9+!y_x%c?%>t^YW5Ze<@ zC+)G%r*E`2cizK9Tly-yRs+RM=WCvDc|6&FKKictt{T3Fqacee`z6MN9CAG0e@MzM zW%Zgp@>sV?`7vo`yWNw-4gs^Up06)sU zVB^52gEx{F#gJlJu|L-w`OUMD4kZUeXr|wrTP$yPt=;|{{QhefDjEIu5&7L46GpqasUZ~t^o_BTN)Grr3R21Qjn1D8sa;H z`n=Ec#_zw@x7Jx_fgI1-aql~>ecktN^fYa5xa%1_|K_DfHQ@)esXt8}7)4$bQNC4& zOG7;}vDe-ME{nUE(lTn+fdybn6YKbDE)1gEFIglw4~T-{ptQlb*eTG=h|d-`m4zug zUjU-hRXJhVu~;x=88mkK=%DXde@R`Tm*qUUoynUondQ7U?6`e^h1#ifU;Py$B7nx+ z*O-IJ=9U{ND`6I%Q{=L8WiEI}o`jV|2r5CD7Z>$m!zf>&t*$TJR$Om|Eywwj7bu`M z&O&tMdrrTIF-{rjaUz*{Ff#&xLcJ zk>wQiz3Gb&vxbyK8%kB}Nj0BA3>1WSa%>8^0g@I4^pqQU+Q)TMq*u6H4!cc!u()2= z{7`Y^MCh&tMvD4E3I(7aIvI3dNVCY3xAvY7^R_6k&y)93U0b&_bc> zC$}BBO-hr`jIBj1d~rSWG)Q0RMD2$)g|3pK-WBf-s)^`z=|deUaEphVprL!u32bKZS|2Wf;$B=tIJrs45}h+FI#dZa$v`kWFT@7N^F-HY{@I0I>`=tvL(b+TV-Hm z!Ap+!9%iu%Uv5cLEMKL{kDm2|Zq&w{wj}91!2BWac1Unu13}x^n_-4TCv)A>%;np` zPdr|Z9-HwqYFV^A$JPK@2NUS?DTZPjpg7?ot`y&iS9i)Ib`-)2RX+?*-dbS~QyzYN zBh+H|CcH#z=96@X^9ubl*EnnokePmt*cS3w5^hMdFZi59Y7w_Qw7#d>G$ukSlwv``N@R4BQ!BcHvB~;E;nDTur1ycs( z_rpNm7b`-z+Ln#v$@=+kRvI+>GEe9(9MMi*Y&?&*DCRfnrZYtT-c>OT$B zGo5ps+)_BM0i(LAR`_30ggPRs#RhhtD$c2h6UZ=%PJB-KGC7=MG=BQbvZ}_x#!%Pb z%OPH}*$&nc>5`W%mCCw_h1vd{B|2L|58<{xYyBYLFb6;4*(ak^@D&N=pb>trq>S~F zulISLUcw2OCqrAR+KTPUwh4~kg;@e_@QClciw?$F2QH2)r4Kt#`Lqs9BIazMMTm8< z4eINKLSP^q-AceV1PP-RN%QsV@uCgDHvB>eaYiOQr_l9?%WWR)$;=RRVmH=Gv?lA4 z5*%g#GO;6_5{o$eJbDW`o|x0$JjemM<*!7cbVBXWduD}YC-jX(nxQ=BDQSav$i)6t zohDduh`kcaq9K$N45}*Y?3{}a^NI@XzNRA1NT>lL4>5^4BNH0!J!@|{l|$BpkJt$B zL|7hiBbkOeNCt}4`vqj0mdVb+TDfEs(zN2^t)h zqmd9|`!Kg7ZF6)-?;OlTTolTHm@i}=9%2R*+=3#DgVe&^C~ER5}-wA1rV{A8!>3iI<`bIJPovf(+%1m3OoR-`^f9H#Yf7&L!2-r(nEztHsV+2jg=T z(iAIQ%)KTN;Fyr2;+Z7xXCb5`{67Bh<{`pc3m)^52UMJs>R&F&3A5zmuqsDoF}TRH zba(D}u}|U~8bgv}UnR(H#E9q@D3O9hI_=_GQ7e$%e8)!;m-Tg8gL=DP<~%Nlr3IxF zu0`YR3^1G&tiR$t>+qw9q{7eTWhwMCr$OK46L6+6VFm&5l)GI5AAHKyVPH=Lwv)I->%3K-kHCZ1Vx^u*w=m)#7F@C z9SCojg%;{QGV2{Yt55g5OSe(+p6Bm=#CD`nl(0iL=8;FaJQ=MhAM3EHt$jPgO2W34 z;C}l@q6Y4ubV|$1MKx8@%_f`%B^o&Ib``vl$t6Z#+8(@$FCRmzP#^qZi3s~*-Id^AQSy*7E;yAP0%$pdR z3-tupCLTAOq$9+~sDiJBg1T?T0mq(}Jt@Nw=V&|VubZTi}Fc_15L&#K1@ zwm;qgLJp%v7=&uz5GDLb)YTF0imzV0*c|J8UfER$+RLjQhge&r?N#XN@b-{DC1+j4x4$iz!|c7s{l$FRn2{_6k`#aC4N?kv3SU=I#Bk_zi! zGX3a};+)gxJrz`(a@_9pYVPy$9dqRCBCZEcwk`QeLJtH7E?E@5Kpc`75;CQqffer+ z{U;0$k8J@;{Me#uoSm=LEpc{~0d+T0y{CLTRsp)7D{J8C-bfEnOVCN&31#`_Ue053 znbqq%A4vq;dJ5uduax@j+3x_!-swOg0+EBHcOc45%v8R9Hu5k|2NUJ~x*(Tu{K%AU z9(t7iiI|{PRH+iq**}rF%DzcJm?{R~i~Obsmw_r3HVjBsS)bB0S>N(BF^A+dN!L7y zjA6cLXua23H&79wI|Lh-_uWnHOE?PD7vDNqWO_mMM_jJWhYR7?{GK+{i6bRF7(_Cwa+vL{8OL-mW%4> zPq!ey!)B6y3!4YePjGnNi5s_~w4$x9G*2HrEESgt(7tW{d)*(MyjbfSa!g5KuwC~5 zv~IdAFhCgrfDI*yy~zQzFwQFo?_oM$0?nO&N$y`F{O5<51gz#SF`+kJv)-4a|2!HQ zu-6plMhwqChc>mRIF4jsw9VcBs%;QyRkYtd z+Lr}+AM?V+vOC`6e}9(=s6QS$oT1p)x%PkrKwByE3%70^*x-+d1+cD%%0(Tn50O*l zKUvIYQp$P(wTg4T#d;qD4g+xOM+kVqxtt06y9KfaIf8tiI4zj`qkz@B_3b-NS+t0_ zJgsw7^$j3d>ev*B!ToT6fxx)*rkcG3FF6z}kqm79mZ5WKv*~R-bQNDZUg&!ma@0Oa@1Jaat&x~I5DRu%VX+q1n zu}-1j2esy{p{=uXPIw=o1HwzwaLFG&ek~;Cv;;)Vl%T( z_CW8MY(ta2>6-)AfH$kO1uJ*?+2OAT^||bWrRRS#{F(sHE8uG9a=+Pt?#QXo{iufa zjB*JG$SJ{fZP%frzwAVuFM*NX&@Osj1bG{d4L4O34H`AU?}@eZab%`jV1-MBcK0z_ zA3z5oOwLfYNIIqGxxtDrIrMjl06)g4Mi%3y=>9hE>TO4&k_sfpG<6iS^k$VRb1duu zQpK*IZl8P-6@O;w&RdM`)X6NDQlK@8{3SANC!+CD2g;dSm848W+EKQH9PuxAV>{fR zB+*`jshpO$7;^Usz*H6wE?aKL<8D|mCwvbyc(|m1#7?YAQ>z3{g%*)NiesPh^9;6* zrr#f!JolmK3lbxm-3b{voY=hFwg}JGyu45>@!V~-Z0uTJ@`~|(Tco2rAO)vGdfY)d zD_+(D8$$mBZa5%mh?h~StVbv)KSa z%@SNwSUS4H>wKNTMUudPL@3pTs*U1Q9he<*am53r0K!@VbIq`JtdgECXPcjcwrX^N zI6!bcBG3moV&lN6ZlTqeR*z03cOF^Dv_&D@bFC7|hL7XiwVy7!+p@F~5l2FWoQZp3gG zF}3-J!N3X!63TlVC{4Iyc0_!{WkqTZln)&3b3VX6EB84K9M@;jpz1p9JrU`N0X+Jg z3g5c&8;5dt&72Z;B$_HYaexd}6?<~ua!fs6#p{W%hIS}^$}Q5A0b+qG$L+hSNDue| z`S5y}XER#xO2t_L6f!cR8u7J~6JwepltS3KSRgw^dz+6$wU~+RWiHLlAq_g66YtR! zT3w%&%a_x_oxOwO%p(t5k1U)FZ$p>SPO)q%kE7(&M$Oj7)utW2&Aw1<_7{6{zf=dt zZ@%s`;6mEg{{?C+?#xKCOBNbOw^5YQHoL}~nv}_+L+S|z0({;PIfciEf|U8+N@%f4 z*gl@wmbEY^>XaSi)an=;Kob5IMK z7K`)_VpOD6gP9XBH?h0F=7!%3hd7|MsSO=4wXxHrkUw;@y&v4HVafQ;UMBv_o7V=x z?~mN~SU&f824xSP$d9a;v7gF?rI}Y_siQU8uzpIzMNEB7#Gw~hi^<@Gssp}IrvW_^ zMI)a69DP$so6fMAFX@lQjSu|QIkC)$Pv+*3D(Oo0U0PtxWDt*4k`1HK5Rg$(APjHC zCXjSuow)y45$M%tGpD>Qfn;OAf81wJ!?+}|lyd0VYhbVAe&UCT~l;<_Z&Y4f0{zlPb z1md}`F)sy<31GLJm`ln!Rn$FcuT389h#KbxJ28rqqCO_2o|1LBA`6hWmyF;PcZcmE zt&k1UdTe$1549cr5oLia77|bvWh;zyC_~01!6n?D?QrZmS5^Jk@)P9xD{p* zyWYoN`_X7yKIZ`4pu2b)EIj9qx3gVq9?X-qm}w*%`+80lY2kLtLi@u#kXO$T%|Ik? z(GOB49T1+8qBl3&_fD*$;$lF&76_GejWop>^kFL!>hudY-r_F`t}q^VzLDN%&YWA1 zxC71^EHqVXuhJR)ip9TD(Z0K0mb6i7>5^$3hO}}}IM1;SKue`O_~jYmt3v1{2p>hY zw6uIdf|ux)1oGv6RC$eVf;?nAK%!XXiuhb}Ozh-^Rh2~iz7;OuHs)G-j-W(|utTyl z7x;Ag1GLgx=1YWO{zA%NQrqTPL{VUDUy>YFwWLI3I*ku;fj_e#sfH>8YrymYbj) z+n-PFS*a%D+c`;(JvgxWLKqROu(9LY+=mXDI=>fsm^Ldm)z?h4B^BC79Z)>e=qtp) zIKM0va}pX*Ea3rr93`r-<16Tj(}spCw0j67%jL>(Fy&Mg3N3YSnd>`-0qD_a_2ufx zdPMLEm5<;^d`04dJ6q0%xMC5^vkNYrPZdoV_#uf7nY)mbOy`C6;q>DB#&lY?S3#Fm#mncUxH#>s?>1^@bdy7r#sFUk+9epFuj>pK zXG7P~ywr>LI zWO40|cjVf{$)ao@vl(p=jiKK^R;%DYPPGmA^f)aFtM)eufjALrb2z~rJ^cmb3%YFI zel<*_gFMV@e|0X3-}yE}$eqvj5cex^iEHJv9EpYJ^D!!b!;i}T4Lzhh5n(rz1o;W{ zOLmM22{aIV7_L3X;xVZeHx%QNRw5nk%u|QxpT|D#ihXGn-Rqp^ipVADD^?TvK-V|G zm@}JXQB%88HU8n-XNG(gabb}mzT9o(gS$EE5 z0p)5>9C^H49%yL8`-~d|%i7J>Ji#3abY2$EtY}xA@%!)9Z26W{Ip8)snwuD0%CCF= z{*ZlTo`1rRR^n~tf<)76Ej3EZ-o?H9cs@fa7mHI6n{4}N-gLEU@%PrZ%*EFC=uWiz zXrE@~>BIBEqd0Nws%tsGu4q$uBkR`5oO4tFR%6O1^6NBKB6`eyj-}#L@=mdIFrQOF z0|6}uAW+~`Zb3VlSu!m=R=e8WnEkkpn19m3GxMLJIP{QcX)Je@MVpGC>F!Kzs@ss1 zykhIi0AicT*uZ_eir-KFh=AV5W3LE!+1lcK-=#}ELF@Gw!e1Npma?m7rD&T|{@vG- z^v8_+j^{(?#OM?59g{@~Mvk>UZW!^YU{@b`?e4gtSA>_3_nW$GTyp(A!J&Pg;C^b& zbH3wi5G+njNxX@Tm8&czgPR`zAf@1)LGv~KU@(6IST8PwO_cEE*;0#XB;7^8f(aV= z!FrXp_w9f>%j!Tu{;2@~TxzJinK3~=ErVYAug&Gz>+4&1#l=|NRZ^RZi z(CDPGV&b*V%2^hd&iBD-)%QB+Spgg>PiEd80D++SL_mGXyh@-n_B?D1DAt%F4=_~b zSEkxNxX#@MYESC{fYkH=Q1>!@~!pqXYm0Z$ts99tD?Obd;0ABWIxryoF5vM@!H-H`i zr3v3CN0HClyaPZeYap8z(}p_F6`*XMMK^G2M9+^e&wGFz*6y}w>c+9kYyZMXFg6@4 zgyZMMBa=G|E{iW(_yrr$kizWo=j=f7!iT7)K=d}%b;Dr*bjk~mGs#m=xdy6m$T9|4 zBqH40+*{>#8UH3z;e;5OvW!sF7#yd9B$3^0LL!yzWNl5`jPu@&AWza-jnx|Kk~faY5W!*wYvx& zlQ3k3&Aq{wsuY8T`XUvcD>@>Uo#4R&UBNI@NwLGN(rsOq4};q8wXVMHsPu0Pi28Sk z>?tQ8mF0t1$)7F{+dsMZDioXYP>NF@3LHtnYw~EO;gi|l2qW+4zW-2dNp8^7x$mM* z@{Ihe80;4k^TOtLWK6wSnv-jbetY(IhuI0o-dLVUi@~(~WK}b5?L9JHgRd7ms_;0) z4($=pPYJA~x`)M}Sm<{uo*2Z-#{SM-(W`nHR~1=v@NtgnzH|bC+*9Bz_y2+L{J#aB z<{B)|$HwVl z8<7>g=Y(9(URxg;(vw0pa(u4z&s5*ioL-GyXPzXG3ri9J>)-tRU@iEOllSO2<+oYnEQPLJwjR0OqoJqE`w;mdXzp9Z=1BQlRXFqX zFn;RJ&z^}^^xA*f5-2a8ALowal7MY+r^8z5cQQC^(85UOwCU*_|Ii|&7imo1Na6a1 zVNH5Te906k2X^+@h)Q*46s0N;UKXLX^Pl*x7K|m-*Oi#HW=Qf=6`_rMi)2SAbMz8h zBaigq?Vg~QcBCrJL;~SHt5j+t4%oRZ-I&mq@t>iuO}sXXpemHbuw9unIfxM8aS-Jl z#RX1n#ReO<2uoH(wuaCpRyAL073xc_!m^7WWKp#nQD%k=qy3jaOK$LLiZ-EOxi*Vq zMbTpgfbWraUV*s8q5lE1_vYez)p>wBap`0#Txn6ULEcy4vtZtIln-gi{2OF20vF;S z^3;~yP>M|5WTe3cK(ZRC;^vL;%N*?`Et`L2$M52U6D%4hRCk7Dh{xsK_g|dqQjqjY z%dwWveRF%&c;N3)8h4PQs?4HL!1+G;q5j?fg*^JC^mOj)XzK9RU;$vm!UdZ^v*)9*ka$sL%*M1 zY&30Bk`CS$tHGn%Yn~V`9QXwfKcljZWD!)1MND7rG2Xv!8X8~$VOx>tq=;Mdr|YuF zy@gbP@AI_HnMB2h`^@z~S9~?6_0lOwD&sUMZ2R<%OZ2wekMF=GdPU{)H`{Z9gS?3< z8E1Qh*`ZA#(#oO-cO=-&<-K}^ctH4~o(ce{SdZDHS){RN zh2AQOI@j{toUH&5R0;2I(rP%2g97X;Qv=l*no^c>oZ76r{Z-!2I5@lXmS@UY<*_JbI(!-xp2uRz#OD zGscstTg4%R@%p`P>t4Mu*1|Xq*b6PXJO&h|s>)Tlh-wjGdGf8uDd$A?n81dzBK&>> zMbf0D&7mFiVIxd zik%nIQ}<^uFNL*-d^EbEExzEXTqoT&W^AgMqFnhd@s2{AD6YUH$8jHll7lIvRq(4G z`!7C>n2yZVQ~JvoxzLdRngYPXS<|gN&svA3z$FLDh{oJ@aDp9qf3;#ma5=E+yZ9)K zRpP_OZTNgY0FZe-usWX%|BNo%i_N-T2c}cXwX*lZs=JVV@ zZ5b<=G2oPCb~VCsn1V62M*iE?TMJaH(=1&TgkAOkoxyg0p&2yb4;4>r4J1Ka*7)#5 zVzdV$@8}q1tz0R`!R9X{>#vxROiDei=tTacM2J1U? z4ySI#KdK>Hzj|Gl&0f4w5y}LDh zW!yGf!lSLi??A{hBtKlDFY}SZ8b23#IiG8?srmRd&C^l&Yf>UA7Y@W2XG=)o_-d+6 zjcagXS>HlUSMa4Wmf2Jw-%D1d0)`q^lL)86XGzU5)p1>&)3fc7+ds3Eu)hj8%)B@P z-+NwbK3u|Cl0c?u%FYGS7vWomRk3*QFSMstRT1$6B^fyUOFqvmTul@MNro|mRb=Jv zsIsI=>jR zGoxn@I1^K$7djyj84xYkZ}Qt|CB%lAlMWlKK71+(_QUrhsH1;R;HuMR3h7l!#bU*} z0}Exq%|CIkmPST^ajQr;N*ZIxNUp&+u%)$?Do>@4)6x3BN+~CYl?UPFvqy~T_T$qYQ&h+7DvyaocK%dVaNZMb^}RNPIx7JH6l{}vPPrmmpT zTS=!K8I~ZHpm1@e23PCEq;^kvEu;_W${asgvP0Kgcft`;d`Sk_s_}yC;);gxm2xAG z0H`MFYHE&W5V=68qp#XmVvP0aszU$MNm-P4$+XB8i_c9MrR5({CVj!hgr1nA??Tg+ z4>J-C%XL3)0ZJE;Ar0Ubs2y-eb273Dykm+J7s(c4OB(7DaK>gnV=k$(b39Ewu2;=( zgfR~v8eT~J#9;LPc`YRzQzTp2Q3sOyY;ksMBP?|QI<#f7=m|e~7zLG4oP#XP$stvM za^?OUJt3D8a#O8aUbJt$ih9nAPB4ubM_=}C6z>vKD~*4-2I8lV^L6YJ()3(ng?G5g zz6>UTplp}eE}OTbKlYblT-k5&L7ja{(Tiyk>MXIWo;>7bGI{d#H>CvR%pgpb)et1 z=EZI#QPA=;T}gqbZ!fHR2(*mMFKCUGSci1j+Wg+ESu*>^BhKe;o{kmaYkQqeqHJ)T29QbD3xP2osWkOTTM^U zU`4!E6y{6#XK?(7pn-t_pdN%=#PT2~Zrt24o-4M2R~Nh7O2v2A?l2~j^Kkvp&jGe0G^VMi41sND9A8H=ppV0l1E@S+)1Z z)awMTU`JZ{OcjrzE}8UbHKjhP?kT=a&WCpArk=Gm8)Qoi0i-^pM$x$!8HZl zsA?w%>)uQ|U2nZ&!@p z1!Ov2<7~RlY2B4h%ct|Aq(j{U;1HR46;%`%);xam>J^uXxD%k}F&vEldcg|8JTYli zSu)<@vGju12&w~)+y()P*Y=)IG=MlII~N;Ny>Bg``uZ!a=)b-L_(zx{HBXG0(@3## z4{&r#H*h*;&&knF&uEzinN9EGsfMv9>Q~x6ZB2+uYMyP$Dt_?#wv^19$(4DT7yrF?znTl< z$6z*TL9hS?sQA}g(_vhyv$UXI8PdPAf_}gc>nTgEWcK944}B`Dwh4dC`H`SK*}Tqq zOT2$nUv}*kFipx%1)w@}`G_Xn#TsOv*&{T2(i#25tbbhpye-cNtW(SIN>gA(h~kw~ z?h>6MY{IJ29(V(6=2n4+-W~z2RGlMx$w2B)_Wb`pNdky++&a8B{|Tx6K~Vu|11`6l z2%af@<_yXV5jd~j|MjFWw82wN}z zvGlV(S{To+LOGxdPfm9B(MDM|Nrn>`3*-9m{t6KBD&2yLI4JuX`~Wi9fURH}7~|&t z4=%QyD8>L&&@5W2)n9q7K?>a_%G;A$ib37Lj#h*Rg;?iQiNLtzI;!2*T$YUjs zi+CI*N$08|O_f&_K07qW)|I*;yyTD%B+BH~c65~sSe;LY+#rE_4qR5c!%?3#zBEXH z6kf8*CsJ`B`n(f`w&c_##G5!hvzI)H>9R9DHmH689Ruk;S@`-p_42Dh0>7zd zh$TH$XnqYN& z$yQorr=TO=$}=&lBZw&LH!Gy8e2%+jN1L!R4tXC*1Wu5nojtU!*jT2N(YW<0i>Byw2>!$$veoYG`V8g zHuI5p4dA?OT;@N#LEhth@~CBx`-O>{8*v1W%0I`de^qs zRxTO)t~e>->B4Ag@mefyhN6O^nnM=*F)xG zhT;mK|DJNU%*2T`;XIOTa}6<1T%ds`+nzi!aUkGq#k$_C^`64Dx~b|kv9yHV%uF^b8w%XDGQWx2gSmh@U zd)=5!ZOmT0diyuW7y1aSSz3L)_jv<`XAV>1*yLepWH_es!v))^d8Lkf{Xwx3?6x$^)d3#BYstScrjtn z(UKj&yd{~;cGOZZ9Xb_04J-XYN`Gc2ltKXo($a#zUCEAXznQT?@QU$zSv5}dR2i*& zsS~UzDm!dJc|$4FctKa(>kFq6K!>{V%J;@JY4grXW77f7RgnIXA8m{s8ij*V5!>Bi z;>xm-B1Rd|N)d=m@pZE{{d&=O2XzelNv>?hLEkoL?@7CSywyo)%$hH|c^W2xWPR(I z;&4<}QM=2k3~zEaj2pnCYHLIUYbsc~ATr{|M`8TsA$Xl)7?g->yKi#C_u3h$P_SWf z%c|#|=_}u!$3Qkt;gK=BS93CFV*Yt_JjN!d#D{Fuy<^dA#PW@RKD_!$mc-51pVEOd2L~wcbTTW`HN5LPG@PDoKF}N0!9=A zBA~I79WLnBM08)loMl1jRTDW{;WiyfYhRl$?OltL!^nD$>fW)|-65GK&|b&e(Y)!* z+vg-Ag{3u@(t_86f^nhEYoShIRisnZAwH9SJsZcH3xj^HRBFTLG0$TR1OVVeABSm| z)8BsRIwjAQU+OF=jDIs+lKzm0-{k<<_dUV8?5dEmI0ABb{sA$0Lj#g>S|VH)*~qGR zQb1(pt$ad~)pX5g-K@x=;u$A66-W=E|8U*vLVS&=X3#-@%L)e^TCg@wfYaC6eDS+$ zR4_Q@j7-|maUu*nt?J;!M+Km%vlod_7f#GGFj7M67M5KNp-RYaae`f&5mn55>2{f< zf^|7K!gngvuV{z)#f$CvP$-b(aN+m6_19=w`w-@zM=Tn3QUi;K3vZVXb@85Gru)C% zwLn0=8DCiW^8e{Yasevj4vre8XvHr_>%ZI)!a(l6A>#|}sQ=T&D zKS>c&J^@iE{a-%wR|_CSK+B(F4LtrI?)`-WoKFf6{dGWALmdCt_ihntsh6An>0Qem z#J9%0%769Z6%=|vd9~$%&c&Btvs^$IT(iBa3^0Q+z^%zWi3@NMEY+YChm1JiE|PD+ zI5>3)gsr!D9?*NR-+y(*q=ycs#Dy4v(9G&T@B2Tx{`3;?@*j8KG5slB@n4?RgyZcp zg)7NAY+ocnZ!3oc%fC_J=%Vl+df%kG8i-)SatsgR_X68D6VhDAo>ZmT{1O|oQWq81 za!ZREXftrk9fCeH$t?z&OGS9$=o9ZXbU9-Kr#Zp|Lpt+ z%-^-Bu$x^QK+DtQd)+MwxsqP2-`Za`mS)G3eNq$|kkaf3PsW(!qEGVi0FPh3XVCi3 zoWc-z9uL8Qphp8+!M~e&(B0*J(Nq?kpt(aIt-int#3|w&N&p?U=IjTyLzqY=$pv7k z=W1|q8iStjgsdlPgSl2n1q2kTppyPg~0Sj4R^UttHRNC3uW$+Csw%87Cbs9Kv6_@dq&5!GqcgN-~0z5+5 zB?!>*I%+?;%b-|h7HxgRHl|S|Mm(c|I)~mta3qp}nqziv zCqe-j@OvaH2S@ z)N~Ob3^;8{Yhsi|OgNNx?y%TwKr+@yQrSx(->{qLrp@lzbIwG{-|xM2D18y-#)O2lZ*fX~;dpEgm;)^o zaQ2)Gl$^j3#|Ds3y+eiNOEA#=Nyf5E)MfyKm4fG(3|J;00G2+xb%hge^@h!0FJh`;4w!Lu4f{l zo?!QI|D9I8BJd_k^ zK>H8b`R8E6Lll59Ivun9KVVHjwK4$@@sU;W{wv|`KL!Gf9SjWQaYg?{Aop*h^w+QX zq8L-jWUu{q%LCZ4A@><&?H6O@D1!INVcSkmcN=4j`vDd$L>jN!dHS*V2k)+c)g{n&62*YM@)4n11JH8g=pgka+6?TVn+*OS#bik>G2Z_|r;(8c4$ zWIns`V+;&|C*Ofh!IGxc)$W^V{P6i~@8erm&;w->L@iRS)4-7Hjbi4lu6#}UP85_&KCCISxQe+7@U7J}W?oWEiCsC6 zoA9t3>0}B1hP@E2x_*sx!#6;(yK_Waju;ay7X^0vp{f!6**JwfDQmr8vZM6vA2gg$%;SzH6MH;Rx@Jao*&YbqPYKd%j#`J2hwDxIB88AWK+a;lNwO(>I!DayLjcw8?fV&m{F z-|lQ1De^`jAa!d>>k32QH=&NSPvi85XwMZul2QeG zEbvx6ka^K5B@xK%DDSHfG7OBHJ6sG69TyA{S6+j7H+q)dH8ib;qt7)R=lMt7bG>ub zbFYEN#aN|j{a9y3>#e|H-A}%4(h~0`z|4mGPQ^HwaI0SC@&T!0#U?c&c~+cYGMtiz z-6z!faw`p8J+UQ?4rj*`4tGD9_{C4py%@XSu`gb7@3dR?h_jjEBR+bcx%XM{dxb|6 z3=fO^Jl(l)lDaz(8-~uC=)9ab^yMa*4%vO9v3&`bCskPvBSPotCr{H3H>9jftOdGt zcNDxW5ByV1d(J-+eXDi|4x#?``kpbl+yqhKKu$%JF>I`;PpZOb1;SJBH$+@vH+WOz zq#B)>hguzJHF9BWjyfKboJ)eCbkLPzbJIs9X;`L=j;EWa-Wp#*kM6d=*DgrcommXG zs~`)s)=c-B;vSK5nlAT!o(A`t^w>LE7?Ioeo=RA;gr9m3-FTZ}rJjT75CWh&nFtyQulKVL#XRtg?Yi;nuJb>z*ZYqr#F2=`0y zVX9fuX-tMzoBjuJ9;cO4qVb>*dqrVN9zUGE1U9Snc1Qg)hU5{tlo!T+#^f>+W1bM} z5}g8_p3$|5&V$kc=||PPxD`1kZyi6FU3cI6>fBRYKkRLyu<5iahEHvB8ufho%v)`Q z?QZ$__m0$MG5eXmbKjrIjJU7jm^L0S4~_SfK28suRMd};IS=01r2y0Yz@)y3aBkw9=pBFrOS-$az zPGE-1Gwh%*ITfI`r$xpEp9NWe*WJks%w+tUR)O}EMC!A%`zr`cHy@8gt&46nij0k& zwfZ~Dh!|wio=B`bFW<28k;QEf$SJ5QSqL;?`&Nl<-o)9`XRu)?{wS!&yC9Q4Anuc# zH>rek!`Z!^rBiQ)reN>RG5Yixg_D8JAri&zVVm(U7PrkedY!;?53B5}w)0jKLR>!~ z6`RF7JSOQ|$BPzp1MiAnTK69yg6wn*cTO^eeaOs0FX3LwJQzZ)8T<;yUfrBo?3b*X z`?t+8w>xGK-a|1_tXY1;-s_W$<=E;VVfv1?2GUTs>cXlrwM+t50@rD?PLvkc-SF&a zCm*`5F>r8UcOO8G8AWq>$hXZgddyF2_k~(RNtr}C1Ne98+K%5Ul8y@B7RAmxCp}~% zje}V$1rV&!B3Y4S;FmXR06_<1v*Tn5S-em<*#kU2aTyLXHHYcwlq4b5^Y)N&d? zi;J5ha=ku9B*6LqqwFo9qH5c=VHmmx0hI<(K|;D~C>2RXLAsOw|Lb3CmTMMkZD;RmUvZwtc^t=Cd&Z4o0{!A^C_U7_ zazk)^P%yT4&PsN193aGP6x{6DbGF?;yA8x~WQ7`WbLYP)yH4m~kTB=+>B0=Bq*ORd_ zI10&6S5ke{`bd8pLii3wG`{&vMDlT)=UkytZukR?3_iMbfdVxkj7y_MpT=^?eB6~w zk_<5Pio6Lt$h1f<=%H+~3*3ZzdsSzEh+0q@up3J6V%#!Li=VXE|6!wZWb3r8p@sP_ z7!9L@L%Y(!G!MjARoQg)-Jp$`M&>m`tYM5UE=!?55|e!Op-Bp*W{v z^v^K9yex|sUpF>Wx4H6bbOoiHGjcbG_!i2WSNbqNtylSd^t4T}l?1A3Rj(q7+8`sL zYM_oOb)>8+YinZl2@I2b)#kh1!$il!PkOhaO@nqPb<$1D=l41~rZ{D`c)_-Ort2)a zY2Q_HD4GKk@7~Dwp4nZ5Zo_Dd01j-lKy8!;gV^h5Gt<#!wRC)*UF(GkCE^P$YTg{R z7S|{o_GCtqN@{V90#wT$>8F1xS9@L~nWz?$WR*KVOsXR8QRu$3WTq6!? zCQ*KP-hSS)1Di~CEwE{;+le}HoYAT68aL}IrLJFhslBP-sBU1g!tQ;28XyPosFjd^X*wT8VwbLZNp!ItoQW>{ZAytG{l;bY$1Mj#qKD|2e0Y zAmrz}L$V4KZMj%P`{Z~Ph0{slNBY@DD>KTo6~fV!2##YH%AXv~&gzTdK;%S^UXT4F zGOXe+mgY^e9q0#~{Y|6zt-RFPOA-j3gHld!7wdKxY*jUW*LpD*rhQF+=@mziz2@wLE{0Keqqvr3)qeL4;YSe!P`%NKeSBq?F1ZSy}Zzm*Z0V68a!M<&@cAgXq@kW!$x83W+=va#85Vg;U0)w7{MH{PehJOEwS-T+r+ z=wE`ye`ON?z%TX`fsvT%xRDubuk*jgfxbY`HchOTXZs=-+=XT}*%o_}?}3`RH9hnd?RREYVlAsS0-C zQEENTGgzQoTh`Sz;s>yR`1vh|vQ>5JY$$l%T0BVhF#0i3MaS}n`i?>kb>%{nwU_Nz z?ZyZ;TTtC>pFXBsB?%J7Q?pV5*18RlTh?5L@-){IIkW|~U*OV8OFW50JDug(ZxqiM z$%peg$vHaMZ1U{{|>k@ zrv|)r{W`T9ys9mxYs$x~9E^j`JvWL&PIg*IdL+&^)C@iQD3tHbxXyYjv z(=BVbPIZsDA%s@|YWH_O(CcOPR9%bcoO`79WXQg2>dVzyXyDix+aA7 zt0}pwg;>fPOqB%}l#u=soF%XPB)%!$EXAVX^3{yb2@aa^4aJ6l54Z9eAVV_)Xj}2clI*{8zZdYBB|EFDcfMr&-(UO(I{-*p zJ|oP&{PM8q?XO$BrrmdWEr(c2S5i!Tq6f>ZG*I2Vy>a|jBXQ+cqX~#)(L}4l`Vr{* zNRQzGFb;`;wN+dUhjs$sj6GvLi)d67RHj3Cd@HD$$2XhAx})V4E^qy#75-W8qvX%qfUh#IxW_&hForxD`rhTG95X(7g$@gC%K%g*2q-F3 z)LhylXnXvz2$D7c+mU6`G%tETi+M~afa;S~c-k9yf?`N5bwmfD@4#6isIbn0% z9rfD{XGzAISL0@>J+4C{ew(BEKGToZKgbYs-_iFfwg9MJ!pO7$FSG3!jl97Befk5Y zQT~7&g3&Rn3fJrMWCdY`PAqZ!8=q)>(mggvS|q>TBg4`4ncW@s3{Bo#4E9#tmyeQuW=+e>t(Fxxyg@e!XUXz z*8vr3yO zJi9n5h0rIvI2)&8Q4eBu=Ficn%*GIa_A>rO7g~|%8_x-gtWA5dBi;|E!y5XOm%eu| z4Ep#rrvZi}cL=RZobXoFLekL*G2kbUNB$XIpLUMFVJ}_o{q5yeG8Mk0CANRUX8%E1 zulH8~Rbx!%IH3~0c6N92kF~2mP!Fomb(;$c-YzL`libYOEyugIl_RrX%G1W?Jy|d$ z^rG|L?+-40QR}*Gn;>XsOTwY=5ot2*RS&|*0({P#{nNYafG$^2^b1OFB=5SJfaB%~ ze~*`%QDgg_v={=;@jGj#(_vc3M{)ng)GtfVF)l;K*k>s_U9>FJv~j zH;gA1?5xW4G=k5#J;K?)GYOCS>lS{HrQH7xzTbNW99y_4ulRF{aCh2&t_|fZoO9it zMej0Ty-K?B#UN&}!^J4zYuNgooe|*(GYIq3!!|iF);^yrIGXjDwLfIV$n19Q;U5Lq z37IzAGxa^E2kXfOkGociz3yxf2~Pc9tlp|wPjUpj*5;$g?9^Gkk5?qGR?mgTHO-Pu zf=TGNXu7VBSusx?Pgk>I?Mm@3-%wo`s*X$)!VcJ0bnDqUUrbj_yI4f74A`O`Z%Wio zdxs6&`|!xm*=jUiIJJjc;WmeZX_aT!LYV8;ndOYejPBBUT|*3quIc63FQ1;Z=+8R#>Wc|3n>$#zYc^7#S3j{)yWMc@5bxqNwFGRuD~mRO zCH@Hv+vS*UR6Soyc=A}{&E?pg?*=eze~TpDuA?5H4?%^rcsH zj~cHM`3gf4B(zqltAn(|^{VT3Ur}XCZ=(d-4(6-FnZkE`(2LiL>E$<`VfzY0AFt2O z4@{u*%e~W|IQCF zc}Os{=hi?nN}FAeLWuTMucTNNG@P!Lfy&u=&8M$=XX2Z*`M2!OZ61GlCU2n{^)N~{ z2 z6?z{|cuF>++z}q5TH78W+a9B}vj^JSdWjAu_dXDVMqX&=pKfJt%dNzXgp>G1jIz*?WJ_^7K$C^sx7`|Q;%2ZtsZh1qO<>vzFeo! z%Uf!G&Rx330cG7AbR6G#RjVGBoYtr#I27Z_kQ;6U| zzfa7Zsg?P?5~K2iRDxY`$)$8I%Jx>+f|11iz5su&WRBO)U%yaB-hPm&gfDLsxW<%mxGH*tO(YJS|Q+F1+ohx2 zqvbVT3v2A|=y@`dtELV{hG}^b9r#E17X%7G8l^_1Y16m**s}@9BM+qRi@@R$ce5E~ zt#Ipc^Mf1wBK*uL(nHcI`W%sT9Waw`c+H>F5BkhmCe|t4&KLUb!3S3puVE6uA=2N@ z?#YRD1&u=v>VFA|D5V*!OR0D;6W=`o{Q!5Q6(U0}tYok#TY2KzGG09oZz9?19+x4J z+~+K06gu608gJN3*wQ_*tyI1?O!Jh(tk=cPhbF1a2PkYfS5fXQg4L>@n`Gscn%!=Y z_AlfsYNOnujo31n@mS(83kJG#`nb5z`p4JSZfPCoegv!&5=nj+Eq#~hj2^V&F+W)JJH(qCeF7WR$pVj{>g$kq&z|Xw?p~nKsFI znfqW+1jERKy}hR|9=!0_Z;t`7Mhgn}h532_oLX0D_Q=sUFcEllAu*I_A9PKIct+$W z{vprX{>wTie^_+xO_T<~CjC$<5TG3M?vM4R>M5prGrEG+g#YaP<>1C?ZgkQR4kwtV zR8Zf&l=m!JStqwmkH0&EQ^Jzia1DI=XG(_oBtT zsf#pmo&1(POQbGbg%1&cj9c1xLlp~cfPr@BMX*l+rg05kE~fkDn>;aW@!z%^8;{F@ z^ZJ=Mx~@--Ssu-w>`hu3;_AaFDaHCndI0gj8~VwHYhG&Fr@x=I8;w;aa&~)5a(_p_ zbl)ta+w=3&IO;3U3=1rr%?W<19#o`LNh|&HMoa}w4LPgV^Jb2q20Xr-T*S-(H6c({Q=f_0)3OpDILGO`eS|Jw+}WS$g^KwALTWT=l?!RPBFhgBH_}U zL|KbGlgmW?@Nt4+<~e+BTkb|7xJbm$(n_xeLsNyjGPejP@Pr}?`O<90>wYRGi~YnC zl+2DVF12nvKUY9?P3R|WQqQGrI&D~F&dOf^-Avav`Xotl@1To7R02+=Dy8TF7rkdz z?}+Fju2L&fS<)l!?|v&kd1@`*tBwep264kq>!N^})!9p&l^RS2>5?x2dC3p%7N zV|zvPWfN~2_x1ccE9lhYpgMOt1J`S5=7*P{n#-5(G(!IX7H{#u4I*Sbbi{);(ERw^?%L%XucO9;< znH_PMr*j(PUanCn;GLdu;a!L6nIWootV=!l8&cFBg3=#yk$w}s>w%MixtoKPobj5m zA;DLLvkihHMV2I93EjHV(l8>A?>x) zOo(JgYQdMPDUE8NVD}!nUd?)12E_h#eKU#}nA6`{G^_ul3W0HGIOV^z#(dmZ#!pw> z0BFOw8I~MU?=?OUjK7>0m(1ZTpG`+SV#igf>sm{RdhD$jr2gH7gV(Kt!Pti@tWeJF zu-~kQygO?8c=Ac<({ORc72D+QXhA&-%b~nEnpPn}Jy?cjl|i~v8x3r6ZW+K`xXy;A z+s7+BhN+y$=Fex_eP9HM?o+7aY17K-Ro6}>s~5Eo*yviT9;l(2$~3lVkncRLL9$N+ zvV}{G{NQ^%g$-gWUUwd+dhAeOL-P*(FG=P-%~Sp3T!fJikUa2G*Z4cL;(MZev?8Qh zILR2|M7yrkw;^|=Qh~C#4Wme~Fqx}Z>-#+tIPvlWRi9@u$(ZL8`kBK58wWdV`Xs(E z;`wejU7z@oHJ+?G*e0iwW!fhIJ6R<3W3crYT3u3fdLic&cf%)*<>h9gFBpfjB^bJ(7>1WqD_gCN)!W>r_C#1(;lU7 z`){dHD^DeqBT=0;pGyTYdVQ#)8rWsCa#ATqBS%~OlG4v;WpVQO5g)!Wr*cNmSPL4- zo3STLP&0^V)vHsoLGd{bZNqRiT1Ao@yh=*AjZ)Q1BOwl(oNAUdhaRok62OBD%=3It z+O25p&W(P~1}ukY%*DK(&cWR0p^)F0%@S33o+ltlMvLcME6L>+^99yY5X3~D9rjv# z=_PqcjsN(-WwTynQ7q?B_^Tj=1m6k$0o>?o{2OpZ&%^k43fd}%R@&m;l2{h)_aGU5 z>)$yKJ5y8P*Z&VZY(NqCalTYElYii0@lOnZE@Xg3x5V<-&&oW;ZRaN~p7H7=(Hj3s zPMV(pmlh?HM*o8_B&}%h>(hhfC^cp7>j;H8$=-i;y0-GLJqbf_wItuv24ZP z;st+uDgOcTtus2NGJD!F6_Z2_N{SnZHaQEhi7@09LhnEIMQ9-M%GEQT5S0S`+-j+u z$17~KSDf*t#({+Pb80F*f6BjI!yyG^z>&UtTh$ctZakF!8uRo8~pqYhQtD1K^_(?f8y0Y^dazP2w*m)1mIQI$YM8;QzosFkl?!S(Chyo1Vr-^b0}bZ~=wB9avJ9AJ?gaOiVA{{it5 zNTC#q?(fb`6_6VdNMpdEJ_z9KdY)X0g~rp~TIrVXiPt;TvGPgJ=hQQ7Dv3AgiiyXk zV2#rPK%{J&ABO7}Vgxqc3a^A|qbx8>zFh>>(;0H=XO?S63vz=jeL{C%S_9LN?^a*; z1Yfhp$bXT-4>_2CNmgQxoJRG(<^@9;=00MgFNKQY1`&VxB#rrHK}^X|bP;Y~2->9Z zWbGfM{2wFF<9~hpJ0t)mbik&^WK|L7qA+>xft^_C$V{X=~b*z*M0U|NPQ8)Kx_&Ch`q#z2Uf@|2M{U`-~+Wn#}M zljc5|-vGmJ=YbOM(&W(}{b z@{MXkWG_Ywwe2oZh3h?r;l#YUljqZAlUMdm!*Uvlv#<+}d#3Z>jb~_$Q#_Csr8eso ztt1f+t><i2H zQ_&1!ZAe<0&bxM= z^TNEBT=Cw1%m9ukbhDwlm$FP2-*~un@zXWc97x+9++$$22`w_&l*(DN6Y(1>e_a0N zLU+Xf^-oy_d~}%AvOd^M!8(xGH$fU|j|eL8?UdOaTY(P=roiE3R*DWRhov-0ex71c zdAoY?Cl5TQKg$VLavd3+z^Nbqx~G1j@F^t%urt4&K#V|yCFAyT;PqEcdk^KiqCYlD z6exJTHte0crUes=2ip=v4%Xr*)y(3465L7j_XnXC15^@>Nw#f?rwj9iFRFOEHT1of z$m82n#~0Xzl<5U95P%|$)+MYO6)bwqiBy{DyVISd>D6b`-Pl_{m3QWzEPNK|)SYI9 z9bapEn);b|5Oy7ygeH3=AaJgFLXS?eXlf}}oC&W(w|%5rop6B+32HWR%TaHT%X8V9 z*`|&1aGwj+9~XHy3W$^LD>sSQxM!4dM!^Rw@aFYbOn*^-ZJ%CfHy-bvE(pTKl!qu! zFjxdj<}nX7q7KBw`{7x|r*#2<>l4^~s~{L=jIr#h4Z^Vo;=MKZ9SaDVnrYgzR7%UTc6mjnZNwbh-mH!dF)sBBxIWfDUM|C zjaUHFVLM=R6^`~Y=na;grCmvr0UlaTeUCqk){TmrZAbuZ+f1eJ$na@z1>p{AdXoq? z+xkyPYe=m?m}QtDWo=+Pzm)Dn1@;b2 z`&$9I&F6NFPM<^;i$(xkk)WMrsVi2HzR6J3YJ4-F3kM14G~07qxDjJdx7~-% zYc4fV0C*l3o9aGpf7?Fj9fcx=P^?8Xs|F7R>zL<#v(J||@BDgaNPm{KNyq(ZUSbB` z=hiZU<8gOBJfMCTqWTadPiCM^jk#-DB>}w)O{t9tVkf{Gl1grqnuT~XR``*EIn;fe za1XbaTUpQBor!I=WH_av0|URLpWaFqiB5Z(R++BGAQEWjDTlQv@;^@ft62lu!9J@TgJn5$=_C9_q&w!>5?U!AT zWoJYp_T$x8bfEqODfShsJ~@D2IJm2|=sZgxdM35F!|;l1-nqwk9EULQ66=E2Nb|Pz z2F^pNGnq3flk}tXTvd8=q1KQWO6iv1K_kg#m?axjklZ4?cuvloRThV5@FOsaz=r)6 z9Ubnj%-x_1<4XFg^>~R~#K@dGP5bZu{j-*C>IFSPvt*x46yTq$?^%hA zlZg}>3Oj{HQS$8TnN2d2UH0w^XH^Op$r;wTin-OMw~S02xa!!=TrG;L`O@>&?#$jv zI7@Bl-KXvw-OzH@D0S)4Ds>+;MIC;(6YbZh+4ZO7H7Q#i%KuzC^Hr0~No%&*LEdiB zVShHV;unw@{sNEr1rRvu)D@>K_zAK=b#_b{=tOUSbVJEzwUzfl0lBFZ>F;m{J98DJ z4ibkYvGDaP&XsWDkNn_dmIZ*0(NJ3a_|>P&;8kzp{#|a+si5pr4xEP?5SqH8t3(o7 zP`*>kdhz+KJ*u+~#=eEVXRfQxF-0U_Kdj-+ctNw2@$1YFy8E&{bfViCN#`LuxA=)3B|P`FkJ!@b18-OP z@=SRWQ87%_x-fyid;Ct439EW8{Ah86mp7}I??QRa?lje_;B%@HiaE-!)QMWa7AX`# z@3YE{>Rwk#9oOiE6Ml@|49FCZ9J40cPO+U@dsFd_2_`l=EUO}-f3GgK7U6(TAK2$Ed?%N%pVX3r-j8Do zzi@XCTgPetJ-dVdjZ&D@*FQ>>Fz)5NfeTYot(rj z$Cd{j5m5`;@_0vx_c)~4&NfVMyf7wd53TWdu+3>NSjsi4xmA6KS(rlWWUr3-U2pDI zgZ7KobP_R(hT{!_!R>jN>HH{tqW3y#(m!Y_~yR~^8#5=Sz{*3>Qo#V=>cRw04) zh+85SO!FI|<*hlhLMeF9wH+ z-nay>LHG_CN03m{tD>pt-_x#K6(`~DZyMx2RFYJqt%pF4q@Y84D7eX6eX_3OKDhFR zq+M{>?jhr(G3~~dG5!7uk=p)vrNjLUmOn9^A(N_rHP%b_fv0KwGp{ErytQRQkle=( zMeC2N5m2kio)hjOr^jNG?#@!9Te``hy{FB`TUwX)ck{c7b(2i;d`!A-&oI|tbWS27 zBjnmHE{Vh}J7DH*yJ^ZMlbr+>5S^ehB>w6JT#9MP^V-Ls&e$EHh#;dDl?W zg)|PnMuS-%f;^|eB?>`yz=vn;A$`P{n3`eZ5Y{~VSUQ;Yp|t{yn_@^+GJ-|K z$-Mi$13nJV)Jt;#9}(+#6%})xxyQ3t?1NmcoMz+8GD$fsWPXbGxJZM(si9~A8au-) zqIoN;!ZJ-=u7wl+GM;LM4oFBFDr;+USKB^_peFUr|G8q z$Uuh%cAsbP#*?TX`<{>^jXQeZG&nJe$cxHEN`GfKq&{ainjIvvB#I6)-;gu*8u_@V%#Ul{lbbVWzg|iru(DMy1Brd zcrnu~oyo{m$&5pkV+Gsh?5M73gwPr;?|PSJaH+P>WU+O5u^1H)3_6d#ZkA#It*yvmw}ZO2Bz933Wg-xW2EK z=`h%~9B?GT<*u?A??JQ{Z)ICOG46w$vjGY--xD2}+7sm$m_;V{fgYU|*gQ3j$nCqwNej6=|sdCE-Dc5X%Y1 zyWv@};EP!4Mu;^%bg?Kk0YBVd!aoKA9b1$F0+P7ApoV}kABVQzOMT?<2ueDI(u-*| z+|tG~bS=F6;2EH~tMZ)IoJb`%y+~k;lobPUa7qBM)+{iQzF#vw5f8G03#lzhkPvu; znqF3@R^IwM3x2>7tq@o^b%zMRi@!o@q~cza)wGC}yNx7qF}*Oc2>mJTmtvoekcOb+ zYDY3zlwP|)cSBIbv=`ic2EBi^v*ethSt$a=^nHWCHi$}k!>%BtATSO=3>UzyAvV(I zqO2h@N~P$rzI34<9cNbZZ-Xc>jxh1i3H%j?@`R(|z<*Img8iQ{k2^+<>|!~ocR$Xr z8;I#Wbzbke)%`MG*OgwabVN@y+PG(p-?S%}KhrQHIMXq@Q*`ravIwv6^;nI`_XVAD zWgWXIpzzzmezwc3M!#JcKGhpvo@7&d5xIFC$NG%-E(qjJ8su)N54&IG3-(y!11QU< zVfprh+kl$~at|w6?{175Um;;whkS1W9Q3XtpaY4Ifl8B8O zPz~Xi&yj-t(67ImRnn?Z3M(BFePVE8Jgef<#A(u;Te8fSUmb4K(8@!n-Yl%JojtCg z6mdX8zqFWT%AjlNr<{!w4{`^&M;-e8&Zi*&3H&T$b>j@6K) z@X>RlY$0R)e()*jp2*j6l_I;j;}CTmE#l&ty7YOWE)h5>H0 zEZaNoz|B-9lBMn;!-9K9Sml=#x2V=W-$g~Zs@JL9`l~F;0Qzxj=d4fheyonC^tbr( z&@$qmL{R_`bvjR2Lys>aKI<|t z{8X;bh4dMtpS6XL-)Ku=qtd=k6=I^PE7n|?TrvwU`(kN-Rbxqxp%izg{@O_KCg&at zZR+^Q)YIg5j$N&Y*(?vS;Y(VP`v!;^J061oBQ>sTwhePK^b;kosiMnk;XYrKv8e&}<`TOPnnF7y*{Q$z>T|_~7h0#y^!Q9tu0IvJXbyDj(dw#^y zw==xgchlw>7>jvEz(S!_M&1GV{;v;1J8qfPC?bqn5RnJ)kJ#T7H39t$ngI)afNj)` z?9WJc9rS1s4t_fPx4u6L3_z1n2Cu)|tdXpeycZY_RmxP`L{zFBns3QEngP}kae5HLjY7z zQAmOHric)A4;}WJJ0aSLG%6am^*%V?+flnapiJ%N%`q4PHpw<$Cdm+QaQ;BwQTTrP zBbP?lmriM`6U*Dnff>qt0R@zAtHKZWTYBSoJ(EmQ+!r+m*X@`SXWI9?EzHwuW4LPY_f%uL)4Nq{&Fv(Qf{RS{~Z z8QwE@QtoY7K4IQV+B_HB+kCOysw|$oS9d)0Ue3a4_GtFP0I}`ux7wGNA-p*@*mL}1 zp<0+t2{ADsoWH1304;g>_|M*aGk-o~^38Pq8Z3DJpE5~QR1=wfNrZ=4S&n9HW)6TP zqS}fCdlrtwx9(To_X$l$9RTH4u4|IAlUWb{RsEdu4GM7jzA$mly(M{P&xEa;$yL&_ z;l#3HyvQ~{^9UuvTnC!8XuLR0EWI2YNG0^4TUvWxAaD&VX_ecW#IWmF2NTj6Wvay6 zsu$|m6x1$?l$!szrgVs#o#|u&82}0DbK$Rq+mUd#ng{(+H zr9>yVTAj_C#ve)03Hjv_Pn>u*yQ*eBhe+d;RK;USX1}h2+U{T5sT#UEQNPVTXT?!S z=5y&rY#MDzX>wLC-)b2Z4RgDw{~|O_&XTGWE$^h8$#^yB)!9^Ny0#uKVPAG*s`BS~ zK@8VV${dx?x&M&dsxI~!#Y^$WZ%-4VK6X&PnBuy%Q=3^onqD6>wA|u`{rVwrH1&~X zm)g;;IOC{D9DjYJU15)G8ZXon&1*44SoTbmCoOHeVKs;4@G6GaTgG9Z4ZBv*ddO0v zrl+>+LMVdjS6_eE>#?3L!75a(qV39;bGRGF?Oam|1H$d3CDNy~hACC4y^CQdznmF- z_?DSuG`mLVl?E9QDRYalN*`LCZFwk)7wdNA!1`bM32&S!AQiWMzkmOD_f>R|~f%Xv;58N%}Vrk{W4V`$7SDilhhN@ASLWb`E7eA40|m z0~2f4&5TdPTVbmf+4!;xGDnZVm8B$BXtcz-B;|p6{`cBFH`rdk<8-{% zGnzb_NO^ODMn&jeC}T$x^ymD!#QJ$g<`)E+sh>coMrFag3xVHnM#OHh4Mv*<6nEb->cLIQkE-qPm;~>tmruw zOq&p!w{6i1!e==hV`liikB+sBkCc0AtW*iM)!U}{tP4B!^k|4qmTsn+j$i}fnd3yw zFWpcRdN@Y7NFRS6!H1iARLRFClI&MLaAh(rAM;zSmv%J!JWyU)=2qNnap)uHJt)>b z&b~-%6}>dHs4O5Qa2e?T4)&Y69^X*@84=757nH%m>0?zZq#T@h%)RfTPqR;A_y5MCs%i8!Id)&aR5kSuAxYQM&Mep9Gd4vdb z7LYv|J&98`9f>VfZ2WwdSZ}$t)LmBIzhON|S{T_>YYXVDXWV+XcqS{i?VCME#s+tE z%eHIJj-LlK(hiPKi6Zr!W^In*AGvM*__|y7jlE2USbXeN_M0CkyF7@hWB@`(`MB9M zJ696%ozYh{hR5)Cm1wGcwtv0&hJ1w^9&l>Kh}gQ3eqPVC<2z>kh<5gI z8@FM=QXlfy`bKbxaDCZ{FTjPIo z5Gr9JkKYjPKFme?l3|Pu#d}3ELEOKQ<;4*wC?&%P0}bNcI&t5pthmX4eTjdWU*OW8 zwu;WMhE!EtS-^K_I;iOH_Or@_NJmORiFc(2Uv~7e8sX3@E1_f!X4@}&ChiFqVRX$h zM35S58gG0Jh*LJE>N>}0mDqRAW|qye#zA_)t<5($$Bzcwx@Y>V3p!*OWN_=k91nek z4lJ2k4`B}g-sEs#RWh;mUz6P*bLQlDGwx}U!xm+PQ-x`M{pGFg!!tYHsB$a2`Zm?F z7u#V1Npm6qc}UG9mUVfa_oTae=K$C1X!Am4mc(q+L+0r&P~~ww9QSE?+GrUwM_a~k zIda^kI4i8*%l+E*yCog$(r~aw*Xp`r82^i2xow>kKSZqkP;5k1^6_Q=t9-5DjkyM@ zADM|tU3=rN;y(N9)^!U;E{zHRoGK^z~YMm_XZ>jdcEWs{Hc-i-f{bX2lFZ3e_4A8(t=G<>zi|nW z2r2MAg}^F^M3K+R+j(aE`pB_$6w2t0b%je~XS=`juhxW+@hrb)mkdS(Hv|fyTvgz0 zIqdI~Rp;2i>i4Evbw{N!dHf(U5T@vqVK#XQc? z51kn4(e4(noH3@WJ|kTT`6X~!(O>2lP#$x}^j48m9f+w>)o1$o%QO5v8PDu^2bQgF z387*UlM%_nI%k7x{5v(1ms2T9jKWrGP68*dj>XJDt(teNHSKQXzoZ@NAZXv-llO>; ztE%WE_ybzH&2*J?Kd@QAu%FOb*mg!nMht>f#9>$k$tEK>+f@H=B@LnUT3J8@@!zc=}nITOP$d z_pJEyz=Ker$|(mwyw%E6qNv6waok8$L;A&GRtspTfDK>hIZ7vEJp2jx3zLKD)UVK! z@#_h9sq#VnV{Nejskac)dwm{U=d^reNarVb0m2XP6>%_ zr}p81&Gt&|(g&T$_eon}7Cccnn^^s;^6lVFVdOoeqxrp7iTCXTnwDqTSzrmudBVwA zMv>R!vHjLb&E?I`+XB?RTV^$*GVIiY8U2)=rDUvPFrnsAh|T~k;214TS$CuqB>Qk7 zz2JkOWFFvpQ--N0`|A>`|4}X8yf}H`4<^RRh!fQ~UOQozyBp&XX($_-wo->`rN6dI zX^>a_kGZFGTPz*cR06i<**cXSl%NN5KS+Ku3_OybSrvLXbE+;ocMV;MaK60<#`JTW z)Caq}4?fdqnBi%~$I)#_8+`xzP;o+o=8lNPCOf}fy``}Uy*Zt1NN9O3u01yOnYQ zD00BP_43^loSadi-8qOkMpnkSHlj!Nn#N z0(9u8rz`H%pj1qRco|4%BHyOT}^`_v=i07K`X(nN)Mo`*!X8W0s6LlA;6US zv6AYeSN5w%oq_tq?VFBK+0#eXIr%dB2c6%-}@4v;Ukuc*sg59$6JHN zj*kfTsf9?%hO{T)R9Jw1l4Y#OFkr%Qtykl@(@u(6PoB~v%LjTqpae~A9$S&;|Hfec zKr#%UajzQ~mYMf|$?i}};N#{s-KliUn7md>(WkBb4?JqW;}38L_yV0Mb)rAWd;8Bs z^S|F2*fEh_Hb8U87Y^6$8v$c}b8)RSKwN}azg1P}eRlXzQc_Zj&!$TiWYQVM9R*;C zvcR06}^`4EvZKUjoU}4`2^|T5+7aSk&Tv z1tbLq>^8{g;{R>-0n}3S(Sl@3LE9wFO50@3A_G54Zll1t?r{$7in#uar-=aH^6G(( z*3wIfM@&V`FZFPV{BwZ=tOL;JY9^9q$p-p2rdD3OlkZakCs#y5NFX^} z%E60g9KJmQh2H8Yr#=F3sV|#SyK4WLN4)?4=1~h9A&qto)Z#~HG~1?SbJx%v z4dJi85T>suG-bD`Fn=aktdHs+G&!)PnIGy~dr3?X+mQKT;vJVa27bc3|1r=}(QXmA z2M-Ijqi3L1OP|$F!IDeZ`pwpL-kEGM`SZab;*CqR#wKSi!tAXL6vh;4UeAa3R^g1` zW=0QKJ9U2F#}nG*G%U>PMgInnukJFSua{j=S`Y_+5w^KF{az1l{H&t6)qE(H>J6aX zp;Upp#>UPMaU;RHKb|H~@Y^--fxCSe%f zh=IjKEl)Vmk3Dwzc3go04%1kO->{+2DbNIOat@n>F<%soGHooq4{?xR$o64EI@ngnYV70b6*q+=v_k_6-N6`p`2z1}fZ;A`M;pXBe0?yCn!{<VFMWKG2rDb`xL#jVlB9EBJaLKE~H4*Arw^|NU`_QyIy< z;sSMT(o4XDD3yW3k*0%tsb9+UgNX%J|7El9{Tp(+Mw2!zwy%7Q)RDG4NO5-#V5bis zy?9ihKJd$mr!6)0mo-yq`VnK}VY7p;^9U@A%WP5W4CE(v9rfOq0*I>5Kzel{Tfy*1 zY0oxK2xRvjYH3i1H6i-Pljkw+n4K9Nu`;MM{j^L~GNcB4@^Jv46&3g|`(;fP2H=*J z8C>%f?5JY09!L#DR7S{9Ij7kIUA6Qx&Y8)h%bb^n`!(5jmMo;~ z4!ax{{PnCv8@O{-jNHZjBjuI$f* zKs$DYmb<*mI=twcDt;m8j9HCVy(>Fw-}E{iS&-U-W)4TOdSE zm*7v_b`8J2e$F+%!IZz{&OE=NmzM-V@?b~LUVRdTbTQ7z4*Q!IxAFcAuWV@FRg9dK z%>nfz1DC4aP0j{VK|*8{7$bh^`HMg@7^zS`cjpDQ+`7e8vEXgcFX6pw2BPa8cNfdg zib}etcl6owo#GO={4aaZtZ%hKSKY*^=JRGN84-JP4SL{BUigW9Qq%5Admke>pQIJ1 zhB}Q~RR-=t4Ha!Y<*xszT1Kz?V>yqvq!Sxh!RMVvu1)&uDQKR$9IYagoP}&wFn|b_ zinm+yySsuq9U!KS94xwC>SvyDd?F}xpVS9>p$El2GyGMj)2ug=)-{TY zCqYttRZX!6TRw4E^>aH9`&yhTGK)uXN&Q|f#Y|U?%X(aLB_0D>e~568hChF^wVbL)$Zzq<6) z<=xi0!=QVp17bR%rXq`}b-K?X-`LMF-GJ@OPU=KeC0j_8lJvGrp^R9?{Wh-_>zhrj zxm(%fS{3fc1vGVXU7cEd6QVq9+ZO`Vvu|68EeOt7hA#-s%om>%UZB^YN}oy5 zi1y(`;Q%FNO%Sjfq(d>}@2ArYQjZrlc)ws5(GVUhK%>RqBp42KQ}l)}yP}%>e(Eqk znQQz%w7qpyRbAIMPN%?;Fz5zJMY>BGM5GZ=0cj8#l;7?UcMjXAyB%m@nf7(ilx$NrnphFeJJ?dhLplZlN)2pd%D4Pf%nGx0_K7#1x zb97hP`OG5V7-D^oxq&dSOIjB<&~89J1V6yS3p4QeaKT+XWzq@ zG3MOfv@jpGhfIO95_&Rd%B+E~&6Yj%C6CsTyJ&AmqS*{1L6y#5tJ7Mdg(dq~NrC&2 zjnDXghvM_3pzHd3Fi}yLFHS@#mgCXdLs64^KdE5H88=t!ln(SbcEdS-ojFix{926i z*XTKTL?d#lQgo({I~oz_{P@6C}9%rac!7z@@n?CRZ>NHv>=R1 zu#?D~rh$PX6&_$D;Q3@-z8hn?bUVShrZu3*P<%BFvnIm5;m6{uZc)JoGq;-@N_Ew9 z&ryjjm6qS-K(Wwdyzz$-)GFfBr|kyeLm$Tv$bGSPgd?7hkGd2x)vq#XzHT0mExJ`T zxt;Vr9Q~&qRrC)L6?5`L+=EAh@*UkB?t!bmG@ee37UX=tx|Q~Y)`)bac%U300qY{J zw$&f^zPX$oV-IAj;Ejb;tOS$fqEv5QQgsn>%$m13IormnAI>??mfsQD<_$Avtr|N4 zisF{USQl$nS`L4gStB?SSq~Q-)=8jR3QjWSIC_Zhp*m;ZQrsz|3U3878Dnd##;^>F38*$0Kg4z4AKUs<;W+R56Olw`}CueEa7`qbIa z+B(NNJ%eRC=__8B<3V1{vi5twbTY-W$}@UaJL=-~v!^7joN3Kjq`AuTd4|@Q$L*5A z)0jmxKejAHR(Nu_8Z-Vzyzk4tW0L4uA6saM=k-#3GzCehl{|jmy+_!)ErZY2Q(}vL z1muW2^>ifSjt~agIr|p)<%NiQ^Id~Uk_+$p=P65j^Ia$ZqRJ_8Q=*8C%g}c0;7fyw zrC1hfle{FB=cL)G<#R7fx%2_AA*g17FK}H9@_efuqTDc6E0LfRhI6u0p_94mK zsAm;l)SJZIS{8|@z63@mvXAUTjG14*v`Nn^7prLub0#Na*A^4HR%A(*J=*}mT) z5)b7lRfMi&o)+aUff+>l)BQ9eb%L4wK~-*}cos<`YQcblH)bX@^>$N;U(Ve9Bp>(u z8KoS40VLxZn#*Z#Q8C&IM$u`BGK@=CcC z7SYFhKW6#k>PbhCw`v(3iml(^`}1Sn#=bYP(WAY@7uX^JQwoHsUi+D*IEuBE**L<2 zR#nY9;l2SYZ(Dcj`Z)E?-@K^iljq- z&S5U-dPsQ+stbetikupfJLy5bx9AzF6C>r6L}V#UWLfjTv`{&$Yk&np@hKRJS3ubm zX;j)lBf2~15MzYMTMbK{)0DXL_obAJ%GN$E7GdpJ54^OM6ADYvF2KbK@d0~;Hz!p_+7Mt zetMhRy$RVz*G3UsESOz4JWFBFAzpXrL)UF=9Q6cl?Z_?HZjXj^ogW2*ZEUJ z8bH)ei#Dy(c<*FAush16?kHu9x7<@^wKBOR=%ZGBQm;p5$KP0>fR?X48?QZ^uPbNT z9ay^5I>(>;rrLvm?MMMlS3|2SXpt&eFo{Mnz%0DQVOBXR^hHdx4CTbV%t`)m*q5>L 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 Uh2dz~eZbE}4XAqVS?fFh1B&^1K>z>% diff --git a/docs/Domain Model/Domeinmodel_version12.drawio b/docs/Domain Model/Domeinmodel_version12.drawio new file mode 100644 index 00000000..670e11ce --- /dev/null +++ b/docs/Domain Model/Domeinmodel_version12.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/Domain Model/Domeinmodel_version12.png b/docs/Domain Model/Domeinmodel_version12.png new file mode 100644 index 0000000000000000000000000000000000000000..ae9a6fe2096fe7b92a13b8198011e0557ca8e23d GIT binary patch literal 152300 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 VV{ literal 0 HcmV?d00001 diff --git a/docs/Domain Model/README.md b/docs/Domain Model/README.md index 9cc60080..a4942cc2 100644 --- a/docs/Domain Model/README.md +++ b/docs/Domain Model/README.md @@ -1,4 +1,4 @@ # Domain Model The domain model is made with drawio. The file and a png image of the latest model can be found in this folder. -A link to the all the previous domain models can be found [here](https://ugentbe-my.sharepoint.com/personal/bart_mesuere_ugent_be/_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fbart%5Fmesuere%5Fugent%5Fbe%2FDocuments%2FOnderwijs%2FSELab2%2F2021%2D2022%2Fgroep2%2FDocumentatie%2Fdomeinmodel). +A link to the all the previous domain models can be found [here](https://ugentbe-my.sharepoint.com/personal/bart_mesuere_ugent_be/_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fbart%5Fmesuere%5Fugent%5Fbe%2FDocuments%2FOnderwijs%2FSELab2%2F2021%2D2022%2Fgroep2%2FDocumentatie%2Fdomeinmodel%2FDomeinmodel%5Fversion12%2Edrawio&parent=%2Fpersonal%2Fbart%5Fmesuere%5Fugent%5Fbe%2FDocuments%2FOnderwijs%2FSELab2%2F2021%2D2022%2Fgroep2%2FDocumentatie%2Fdomeinmodel). From 2117849e370491dde7f013424220e86451da8992 Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Mon, 4 Apr 2022 10:22:05 +0200 Subject: [PATCH 133/827] fix: parseNewProjectRequest in request.ts --- backend/request.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/backend/request.ts b/backend/request.ts index 71ea2c77..e8735a0d 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -279,14 +279,14 @@ export async function parseRequestCoachRequest(req: express.Request): */ export async function parseNewProjectRequest(req: express.Request): Promise { - return hasFields(req, [ "name", "partner", "start", "end", "positions" ], - types.key) + return hasFields(req, [ "name", "partner", "start", "end", "positions", "osocId" ], types.key) .then(() => Promise.resolve({ - sessionkey : getSessionKey(req), + sessionkey : req.body.sessionkey, name : req.body.name, partner : req.body.partner, start : req.body.start, end : req.body.end, + osocId: req.body.osocId, positions : req.body.positions })); } @@ -439,6 +439,21 @@ export async function parseResetPasswordRequest(req: express.Request): return Promise.resolve({code : req.params.id, password : req.body.password}); } +/** + * Parses a request to `POST /student/role`. + * @param req The request to check. + * @returns A Promise resolving to the parsed data or rejecting with an + * Argument or Unauthenticated error. + */ +export async function parseStudentRoleRequest(req: express.Request): + Promise { + return hasFields(req, [ "name" ], types.neither) + .then(() => Promise.resolve({ + sessionkey : req.body.sessionkey, + name : req.body.name + })); +} + /** * A request to `DELETE /login/` only requires a session key * {@link parseKeyRequest}. From 6e4821cb3d090733bb7a0732efce6b37f4553a49 Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Mon, 4 Apr 2022 10:25:03 +0200 Subject: [PATCH 134/827] fix: parseRolesAllRequest in backend/request.ts --- backend/request.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/request.ts b/backend/request.ts index e8735a0d..1621674f 100644 --- a/backend/request.ts +++ b/backend/request.ts @@ -464,6 +464,11 @@ export const parseLogoutRequest = parseKeyRequest; * {@link parseKeyRequest}. */ export const parseStudentAllRequest = parseKeyRequest; +/** + * A request to `GET /roles/all` only requires a session key + * {@link parseKeyRequest}. + */ +export const parseRolesAllRequest = parseKeyRequest; /** * A request to `GET /coach/all` only requires a session key * {@link parseKeyRequest}. From 1a73d62388ef57a26280b7e618ec81daca6cfc5c Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Mon, 4 Apr 2022 10:37:09 +0200 Subject: [PATCH 135/827] docs: update comments --- frontend/contexts/sessionProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/contexts/sessionProvider.tsx b/frontend/contexts/sessionProvider.tsx index bdb423b4..bdef7054 100644 --- a/frontend/contexts/sessionProvider.tsx +++ b/frontend/contexts/sessionProvider.tsx @@ -24,8 +24,8 @@ const defaultState = { const SessionContext = createContext(defaultState); /** - * The sesssion provider is responsible for storing the user session both at runtime and in cookies - * The session prodiver also reads all cookies upon reload + * The sesssion provider is responsible for storing the user session both at runtime and in local storage + * The session prodiver also reads the session from local storage if present * @param children * @constructor */ From 0159b6906dae83502e9449f5a825a2330f0485dd Mon Sep 17 00:00:00 2001 From: KoenDesplenter Date: Mon, 4 Apr 2022 10:48:07 +0200 Subject: [PATCH 136/827] test: created int. tests for password reset --- .../orm_integration/integration_setup.ts | 28 ++++++++++++- .../tests/orm_integration/login_user.test.ts | 42 +++++++++---------- .../orm_integration/password_reset.test.ts | 42 +++++++++++++++++++ backend/tests/orm_integration/person.test.ts | 16 +++---- 4 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 backend/tests/orm_integration/password_reset.test.ts diff --git a/backend/tests/orm_integration/integration_setup.ts b/backend/tests/orm_integration/integration_setup.ts index bc543bd5..144938b8 100644 --- a/backend/tests/orm_integration/integration_setup.ts +++ b/backend/tests/orm_integration/integration_setup.ts @@ -24,6 +24,11 @@ beforeAll(async () => { email: 'studentmail@mail.com', firstname: 'student', lastname: 'student' + }, + { + email: 'coachmail@mail.com', + firstname: 'coach', + lastname: 'testcoach' } ], }); @@ -46,6 +51,13 @@ beforeAll(async () => { is_coach: false, account_status: "PENDING" }, + { + person_id: persons[4].person_id, + password: "Mypassword", + is_admin: true, + is_coach: true, + account_status: "ACTIVATED" + }, ], }); @@ -320,6 +332,7 @@ beforeAll(async () => { ], }); + // create session keys await prisma.session_keys.createMany({ data: [ { @@ -333,10 +346,22 @@ beforeAll(async () => { ] }); + + // create password reset + await prisma.password_reset.createMany({ + data: [ + { + login_user_id: login_users[2].login_user_id, + reset_id: "5444024611562312170969914212450321", + valid_until: new Date("2022-07-13") + } + ] + + }); }); afterAll(async () => { - + const deletePasswordReset = prisma.password_reset.deleteMany(); const deleteJobApplicationSkillDetails = prisma.job_application_skill.deleteMany(); const deleteLanguageDetails = prisma.language.deleteMany(); const deleteAttachmentDetails = prisma.attachment.deleteMany(); @@ -355,6 +380,7 @@ afterAll(async () => { const deletePersonDetails = prisma.person.deleteMany(); await prisma.$transaction([ + deletePasswordReset, deleteJobApplicationSkillDetails, deleteLanguageDetails, deleteAttachmentDetails, diff --git a/backend/tests/orm_integration/login_user.test.ts b/backend/tests/orm_integration/login_user.test.ts index ce1300cc..e7228e7e 100644 --- a/backend/tests/orm_integration/login_user.test.ts +++ b/backend/tests/orm_integration/login_user.test.ts @@ -41,13 +41,13 @@ it('should create 1 new login user', async () => { } }); -it('should find all the login users in the db, 3 in total', async () => { +it('should find all the login users in the db, 4 in total', async () => { const searched_login_users = await getAllLoginUsers(); - expect(searched_login_users.length).toEqual(3); - expect(searched_login_users[2]).toHaveProperty("password", login_user.password); - expect(searched_login_users[2]).toHaveProperty("is_admin", login_user.isAdmin); - expect(searched_login_users[2]).toHaveProperty("is_coach", login_user.isCoach); - expect(searched_login_users[2]).toHaveProperty("account_status", login_user.accountStatus); + expect(searched_login_users.length).toEqual(4); + expect(searched_login_users[3]).toHaveProperty("password", login_user.password); + expect(searched_login_users[3]).toHaveProperty("is_admin", login_user.isAdmin); + expect(searched_login_users[3]).toHaveProperty("is_coach", login_user.isCoach); + expect(searched_login_users[3]).toHaveProperty("account_status", login_user.accountStatus); }); it('should return the password, by searching for its person id', async () => { @@ -68,8 +68,17 @@ it('should return the login user, by searching for its person id', async () => { expect(searched_login_user).toHaveProperty("account_status", login_user.accountStatus); }); -it('should find all the login users in the db that are admin, 3 in total', async () => { +it('should find all the login users in the db that are admin, 4 in total', async () => { const searched_login_users = await searchAllAdminLoginUsers(true); + expect(searched_login_users.length).toEqual(4); + expect(searched_login_users[3]).toHaveProperty("password", login_user.password); + expect(searched_login_users[3]).toHaveProperty("is_admin", login_user.isAdmin); + expect(searched_login_users[3]).toHaveProperty("is_coach", login_user.isCoach); + expect(searched_login_users[3]).toHaveProperty("account_status", login_user.accountStatus); +}); + +it('should find all the login users in the db that are coach, 3 in total', async () => { + const searched_login_users = await searchAllCoachLoginUsers(true); expect(searched_login_users.length).toEqual(3); expect(searched_login_users[2]).toHaveProperty("password", login_user.password); expect(searched_login_users[2]).toHaveProperty("is_admin", login_user.isAdmin); @@ -77,22 +86,13 @@ it('should find all the login users in the db that are admin, 3 in total', async expect(searched_login_users[2]).toHaveProperty("account_status", login_user.accountStatus); }); -it('should find all the login users in the db that are coach, 2 in total', async () => { - const searched_login_users = await searchAllCoachLoginUsers(true); - expect(searched_login_users.length).toEqual(2); - expect(searched_login_users[1]).toHaveProperty("password", login_user.password); - expect(searched_login_users[1]).toHaveProperty("is_admin", login_user.isAdmin); - expect(searched_login_users[1]).toHaveProperty("is_coach", login_user.isCoach); - expect(searched_login_users[1]).toHaveProperty("account_status", login_user.accountStatus); -}); - it('should find all the login users in the db that are admin or coach, 3 in total', async () => { const searched_login_users = await searchAllAdminAndCoachLoginUsers(true); - expect(searched_login_users.length).toEqual(2); - expect(searched_login_users[1]).toHaveProperty("password", login_user.password); - expect(searched_login_users[1]).toHaveProperty("is_admin", login_user.isAdmin); - expect(searched_login_users[1]).toHaveProperty("is_coach", login_user.isCoach); - expect(searched_login_users[1]).toHaveProperty("account_status", login_user.accountStatus); + expect(searched_login_users.length).toEqual(3); + expect(searched_login_users[2]).toHaveProperty("password", login_user.password); + expect(searched_login_users[2]).toHaveProperty("is_admin", login_user.isAdmin); + expect(searched_login_users[2]).toHaveProperty("is_coach", login_user.isCoach); + expect(searched_login_users[2]).toHaveProperty("account_status", login_user.accountStatus); }); it('should update login user based upon login user id', async () => { diff --git a/backend/tests/orm_integration/password_reset.test.ts b/backend/tests/orm_integration/password_reset.test.ts new file mode 100644 index 00000000..cd1d53df --- /dev/null +++ b/backend/tests/orm_integration/password_reset.test.ts @@ -0,0 +1,42 @@ +import prisma from "../../prisma/prisma"; +import {createOrUpdateReset, findResetByCode, deleteResetWithLoginUser, + deleteResetWithResetId} from "../../orm_functions/password_reset"; + +const newReset = "5444024619724212170969914212450321"; +const date = new Date("2022-07-13"); + +it("should create a new password resety for the given login user", async () =>{ + const loginUser = await prisma.login_user.findFirst(); + + if (loginUser){ + const created_password_reset = await createOrUpdateReset(loginUser.login_user_id, newReset, date); + + expect(created_password_reset).toHaveProperty("login_user_id", loginUser.login_user_id); + expect(created_password_reset).toHaveProperty("reset_id", newReset); + expect(created_password_reset).toHaveProperty("valid_until", date); + } +}); + +it("should return the password reset by searching for its the key", async () => { + const found_password_reset = await findResetByCode(newReset); + expect(found_password_reset).toHaveProperty("reset_id", newReset); + expect(found_password_reset).toHaveProperty("valid_until", date); +}); + +it('should delete the password reset based upon its id', async () => { + const deleted_password_reset = await deleteResetWithResetId(newReset); + expect(deleted_password_reset).toHaveProperty("reset_id", newReset); + expect(deleted_password_reset).toHaveProperty("valid_until", date); +}); + +it('should delete the password reset based upon login user id', async () => { + const loginUsers = await prisma.login_user.findMany(); + const password_reset = await prisma.password_reset.findFirst() + + if (loginUsers[2] && password_reset){ + const deleted_password_reset = await deleteResetWithLoginUser(loginUsers[2].login_user_id); + expect(deleted_password_reset).toHaveProperty("reset_id", password_reset.reset_id); + expect(deleted_password_reset).toHaveProperty("valid_until", password_reset.valid_until); + } +}); + diff --git a/backend/tests/orm_integration/person.test.ts b/backend/tests/orm_integration/person.test.ts index 910a5b11..a2200b08 100644 --- a/backend/tests/orm_integration/person.test.ts +++ b/backend/tests/orm_integration/person.test.ts @@ -45,15 +45,15 @@ it('should create 1 new person where email is null', async () => { it('should find all the persons in the db, 2 in total', async () => { const searched_persons = await getAllPersons(); - expect(searched_persons[4]).toHaveProperty("github", null); - expect(searched_persons[4]).toHaveProperty("firstname", person4.firstname); - expect(searched_persons[4]).toHaveProperty("lastname", person4.lastname); - expect(searched_persons[4]).toHaveProperty("email", person4.email); + expect(searched_persons[5]).toHaveProperty("github", null); + expect(searched_persons[5]).toHaveProperty("firstname", person4.firstname); + expect(searched_persons[5]).toHaveProperty("lastname", person4.lastname); + expect(searched_persons[5]).toHaveProperty("email", person4.email); - expect(searched_persons[5]).toHaveProperty("github", person5.github); - expect(searched_persons[5]).toHaveProperty("firstname", person5.firstname); - expect(searched_persons[5]).toHaveProperty("lastname", person5.lastname); - expect(searched_persons[5]).toHaveProperty("email", null); + expect(searched_persons[6]).toHaveProperty("github", person5.github); + expect(searched_persons[6]).toHaveProperty("firstname", person5.firstname); + expect(searched_persons[6]).toHaveProperty("lastname", person5.lastname); + expect(searched_persons[6]).toHaveProperty("email", null); }); // Can only be tested with a login user, should therefore be tested in the login user tests? From 5c5e599acff4ef49a6b957401127223441af49d9 Mon Sep 17 00:00:00 2001 From: Jonathan Vanbrabant Date: Mon, 4 Apr 2022 10:59:45 +0200 Subject: [PATCH 137/827] feat: fetch datafunction outside of useEffect --- frontend/pages/students.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/pages/students.tsx b/frontend/pages/students.tsx index b93bcf22..4220038b 100644 --- a/frontend/pages/students.tsx +++ b/frontend/pages/students.tsx @@ -9,17 +9,17 @@ const Students: NextPage = () => { const {sessionKey} = useContext(SessionContext); //const [students, setStudents] = useState>([]); - useEffect(() => { - const fetchData = async () => { - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`, { - method: 'GET', - headers: { - 'Authorization': `auth/osoc2 ${sessionKey}` - } - }); - console.log(response); - } + const fetchData = async () => { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/student/all`, { + method: 'GET', + headers: { + 'Authorization': `auth/osoc2 ${sessionKey}` + } + }); + console.log(response); + } + useEffect(() => { fetchData(); }, []); From 7b363f35a98088eedd228afde32d414dd1ef9b8d Mon Sep 17 00:00:00 2001 From: Norick Beterams Date: Mon, 4 Apr 2022 11:57:40 +0200 Subject: [PATCH 138/827] fix: get all students route returned an empty list --- backend/routes/student.ts | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/backend/routes/student.ts b/backend/routes/student.ts index c440863c..ceb0fe50 100644 --- a/backend/routes/student.ts +++ b/backend/routes/student.ts @@ -16,7 +16,7 @@ import {errors} from '../utility'; * @returns See the API documentation. Successes are passed using * `Promise.resolve`, failures using `Promise.reject`. */ -async function listStudents(req: express.Request): Promise { +/*async function listStudents(req: express.Request): Promise { return rq.parseStudentAllRequest(req) .then(parsed => util.checkSessionKey(parsed)) .then(parsed => { @@ -61,6 +61,42 @@ async function listStudents(req: express.Request): Promise Promise.resolve({data : studentList, sessionkey : parsed.data.sessionkey})); }); +}*/ +async function listStudents(req: express.Request): Promise { + const parsedRequest = await rq.parseStudentAllRequest(req); + const checkedSessionKey = await util.checkSessionKey(parsedRequest).catch(res => res); + if(checkedSessionKey.data == undefined) { + return Promise.reject(errors.cookInvalidID); + } + const studentList : object[] = []; + const students = await ormSt.getAllStudents(); + for(let studentIndex = 0; studentIndex < students.length; studentIndex++) { + const jobApplication = await ormJo.getLatestJobApplicationOfStudent(students[studentIndex].student_id); + if(jobApplication != null) { + const evaluations = await ormJo.getStudentEvaluationsTotal(students[studentIndex].student_id); + + const languages : string[] = []; + for(let skillIndex = 0; skillIndex < jobApplication.job_application_skill.length; skillIndex++) { + const language = await ormLa.getLanguage(jobApplication.job_application_skill[skillIndex].language_id); + if(language != null) { + languages.push(language.name); + } else { + return Promise.reject(errors.cookInvalidID); + } + } + + studentList.push({ + student: students[studentIndex], + jobApplication: jobApplication, + evaluations: evaluations, + languages: languages + }) + } else { + return Promise.reject(errors.cookInvalidID); + } + } + + return Promise.resolve({data : studentList, sessionkey : checkedSessionKey.data.sessionkey}); } /** From e360eff6babd8abbf6811eba1000018fe75f0f83 Mon Sep 17 00:00:00 2001 From: Huan Date: Mon, 4 Apr 2022 12:20:04 +0200 Subject: [PATCH 139/827] fix: sessionkey not updating --- frontend/components/User/User.tsx | 14 ++++++++------ frontend/pages/users.tsx | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index c0228486..ad65bcbc 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -37,14 +37,16 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: setIsCoach(isCoach => !isCoach); } else if (changed_val === "activated") { //TODO This doesn't work because setStatus is an async function(need to find a way to revert it). - if (status === "ACTIVATED") { - setStatus('DISABLED'); - } else { - setStatus('ACTIVATED'); - } + setStatus(revertStatus()) + } + } + const revertStatus = () => { + if (status === "ACTIVATED") { + return 'DISABLED'; + } else { + return 'ACTIVATED'; } } - const setUserRole = async (route: string, changed_val: string) => { console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString()) return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString(), { diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index 4209019e..a23dfe34 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -14,19 +14,19 @@ const Users: NextPage = () => { const {sessionKey, setSessionKey} = useContext(SessionContext) const [users, setUsers] = useState>() const userSet = new Set<{ person_data: { id: number, name: string }, coach: boolean, admin: boolean, activated: string }>() - const test: Array<{ person_data: { id: number, name: string }, coach: boolean, admin: boolean, activated: string }> = []; + let test: Array<{ person_data: { id: number, name: string }, coach: boolean, admin: boolean, activated: string }> = []; // Load all users upon page load useEffect(() => { // boilerplate for the admin/coaches route (pass admin/coach as string) - const getAllUsers = async (route: string) => { + const getAllUsers = async (route: string,sessionkey:string) => { console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all") return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/all", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', - 'Authorization': `auth/osoc2 ${sessionKey}` + 'Authorization': `auth/osoc2 ${sessionkey}` } }) .then(response => response.json()).then(json => { @@ -43,21 +43,23 @@ const Users: NextPage = () => { }) } - getAllUsers("admin").then(response => { + let tempSes = ""; + getAllUsers("admin",sessionKey).then(response => { console.log(response) - if (setSessionKey) { - setSessionKey(response.sessionkey) - } - test.concat(response.data); + tempSes = response.sessionkey + console.log(test) + test = [ ...test, ...response.data]; + console.log(test) test.forEach(userSet.add, userSet); }).then(() => { - getAllUsers("coach").then(response => { + getAllUsers("coach",tempSes).then(response => { console.log(response) if (setSessionKey) { setSessionKey(response.sessionkey) } test.concat(response.data); test.forEach(userSet.add, userSet); + console.log(userSet) }).then(() => setUsers(Array.from(userSet))); }) //TODO The code above doesn't work now because the sessionkey doesn't update for some reason. Await doesn't work. From b0efc9799f3d68e383ee6d358357d822ecbbef7f Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Mon, 4 Apr 2022 17:27:50 +0200 Subject: [PATCH 140/827] feat: install bulma and sass, prepare emailHTML --- backend/routes/reset.ts | 11 +- frontend/package-lock.json | 4608 ++++++++++++++++- frontend/package.json | 2 + frontend/pages/_app.tsx | 2 +- frontend/pages/login/index.tsx | 21 +- frontend/pages/pending.tsx | 2 +- frontend/styles/{globals.css => globals.scss} | 7 + .../{index.module.css => index.module.scss} | 0 .../{login.module.css => login.module.scss} | 8 +- ...pending.module.css => pending.module.scss} | 0 10 files changed, 4531 insertions(+), 130 deletions(-) rename frontend/styles/{globals.css => globals.scss} (91%) rename frontend/styles/{index.module.css => index.module.scss} (100%) rename frontend/styles/{login.module.css => login.module.scss} (96%) rename frontend/styles/{pending.module.css => pending.module.scss} (100%) diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index c669980a..b4cbcf1f 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -47,6 +47,15 @@ export async function sendMail(mail: Email) { }); } +/** + * Returns the html body for the email with the reset code applied. + * @param resetID + */ +function createEmail(resetID: string) { + return '

' + resetID + '

' +} + + async function requestReset(req: express.Request): Promise { return rq.parseRequestResetRequest(req).then( (parsed) => @@ -63,7 +72,7 @@ async function requestReset(req: express.Request): Promise { return sendMail({ to : parsed.email, subject : config.email.header, - html : '

' + code.reset_id + '

' + html : createEmail(code.reset_id) }) .then(data => { console.log(data); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e63eb6d6..eb5f1406 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "bulma": "^0.9.3", "crypto-js": "^4.1.1", "next": "12.1.4", "react": "18.0.0", @@ -21,10 +22,127 @@ "cross-env": "^7.0.3", "eslint": "8.12.0", "eslint-config-next": "12.1.4", + "sass": "^1.49.11", "typedoc": "^0.22.12", "typescript": "4.6.3" } }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "optional": true, + "peer": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "optional": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "optional": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "optional": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "optional": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "optional": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "optional": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/runtime": { "version": "7.17.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", @@ -70,6 +188,13 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true, + "peer": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -319,12 +444,47 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "peer": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "peer": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/bcrypt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", @@ -340,6 +500,13 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "optional": true, + "peer": true + }, "node_modules/@types/node": { "version": "17.0.23", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", @@ -355,6 +522,13 @@ "@types/node": "*" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "optional": true, + "peer": true + }, "node_modules/@types/prop-types": { "version": "15.7.4", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", @@ -479,6 +653,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true, + "peer": true + }, "node_modules/acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -500,11 +681,53 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "peer": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "peer": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "devOptional": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -520,7 +743,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -529,7 +752,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -540,6 +763,40 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "devOptional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true, + "peer": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -621,12 +878,76 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, + "node_modules/async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true, + "peer": true + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "optional": true, + "peer": true + }, "node_modules/axe-core": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", @@ -646,13 +967,32 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "devOptional": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "peer": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "devOptional": true, + "engines": { + "node": ">=8" + } }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "devOptional": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -662,7 +1002,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "devOptional": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -670,6 +1010,41 @@ "node": ">=8" } }, + "node_modules/bulma": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.3.tgz", + "integrity": "sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==" + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "peer": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -692,6 +1067,34 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "optional": true, + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001313", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz", @@ -701,11 +1104,18 @@ "url": "https://opencollective.com/browserslist" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true, + "peer": true + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -717,11 +1127,82 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "optional": true, + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -733,13 +1214,43 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "peer": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "devOptional": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true, + "peer": true }, "node_modules/core-js-pure": { "version": "3.21.1", @@ -752,8 +1263,15 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/cross-env": { - "version": "7.0.3", + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "optional": true, + "peer": true + }, + "node_modules/cross-env": { + "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "dev": true, @@ -774,7 +1292,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, + "devOptional": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -801,11 +1319,24 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "devOptional": true, "dependencies": { "ms": "2.1.2" }, @@ -818,6 +1349,40 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "optional": true, + "peer": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -836,6 +1401,33 @@ "node": ">= 0.4" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true, + "peer": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -860,12 +1452,60 @@ "node": ">=6.0.0" } }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "peer": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true, + "peer": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "optional": true, + "peer": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", @@ -917,6 +1557,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1359,11 +2009,28 @@ "node": ">=0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true, + "peer": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ], + "optional": true, + "peer": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "devOptional": true }, "node_modules/fast-glob": { "version": "3.2.11", @@ -1397,7 +2064,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "devOptional": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -1430,7 +2097,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1469,17 +2136,69 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "optional": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "devOptional": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "devOptional": true }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -1487,6 +2206,50 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "peer": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "optional": true, + "peer": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "optional": true, + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -1501,6 +2264,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -1517,11 +2290,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, + "devOptional": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1584,11 +2367,81 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globule": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", + "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "optional": true, + "peer": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/globule/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "optional": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "optional": true, + "peer": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, + "devOptional": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -1609,7 +2462,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -1641,6 +2494,101 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true, + "peer": true + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "optional": true, + "peer": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "peer": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -1650,6 +2598,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "devOptional": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -1670,16 +2624,33 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true, + "peer": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, + "devOptional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1689,7 +2660,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "devOptional": true }, "node_modules/internal-slot": { "version": "1.0.3", @@ -1705,6 +2676,20 @@ "node": ">= 0.4" } }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "optional": true, + "peer": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "optional": true, + "peer": true + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -1717,10 +2702,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -1749,7 +2746,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, + "devOptional": true, "dependencies": { "has": "^1.0.3" }, @@ -1776,16 +2773,26 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -1793,6 +2800,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "optional": true, + "peer": true + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -1809,7 +2823,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -1829,6 +2843,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -1884,6 +2908,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true, + "peer": true + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -1896,11 +2927,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true, + "peer": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "devOptional": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true, + "peer": true + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "optional": true, + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -1919,11 +2971,32 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true, + "peer": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "optional": true, + "peer": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "optional": true, + "peer": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "devOptional": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -1931,6 +3004,13 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true, + "peer": true + }, "node_modules/json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -1949,6 +3029,22 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "optional": true, + "peer": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", @@ -1962,6 +3058,16 @@ "node": ">=4.0" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -1990,6 +3096,13 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "optional": true, + "peer": true + }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -2003,6 +3116,13 @@ "node": ">=4" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true, + "peer": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2024,7 +3144,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "devOptional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2038,6 +3158,47 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "peer": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/marked": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", @@ -2050,6 +3211,46 @@ "node": ">= 12" } }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "optional": true, + "peer": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2072,11 +3273,44 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "devOptional": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2090,11 +3324,143 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "optional": true, + "peer": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "devOptional": true + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true, + "peer": true }, "node_modules/nanoid": { "version": "3.3.1", @@ -2113,6 +3479,16 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "12.1.4", "resolved": "https://registry.npmjs.org/next/-/next-12.1.4.tgz", @@ -2162,11 +3538,181 @@ } } }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "peer": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "optional": true, + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "peer": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", + "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "optional": true, + "peer": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/node-sass": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.1.tgz", + "integrity": "sha512-uMy+Xt29NlqKCFdFRZyXKOTqGt+QaKHexv9STj2WeLottnlqZEEWx6Bj0MXNthmFRRdM/YwyNo/8Tr46TOM0jQ==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "async-foreach": "^0.1.3", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^9.0.0", + "nan": "^2.13.2", + "node-gyp": "^8.4.1", + "npmlog": "^5.0.0", + "request": "^2.88.0", + "sass-graph": "4.0.0", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "bin": { + "node-sass": "bin/node-sass" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "optional": true, + "peer": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "peer": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -2272,7 +3818,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, + "devOptional": true, "dependencies": { "wrappy": "1" } @@ -2318,6 +3864,22 @@ "node": ">=4" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "peer": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -2339,6 +3901,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "optional": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -2352,7 +3933,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -2361,7 +3942,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -2370,7 +3951,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "devOptional": true }, "node_modules/path-type": { "version": "4.0.0", @@ -2381,6 +3962,13 @@ "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true, + "peer": true + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2390,7 +3978,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8.6" }, @@ -2424,6 +4012,34 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true, + "peer": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "optional": true, + "peer": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "peer": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -2435,15 +4051,32 @@ "react-is": "^16.13.1" } }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true, + "peer": true + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2464,6 +4097,16 @@ } ] }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/react": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", @@ -2480,19 +4123,220 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz", "integrity": "sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.21.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.21.0" + }, + "peerDependencies": { + "react": "18.0.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "optional": true, + "peer": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "optional": true, + "peer": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "optional": true, + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "optional": true, + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "optional": true, + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "optional": true, + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "optional": true, + "peer": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "optional": true, + "peer": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "devOptional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "optional": true, + "peer": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, - "peerDependencies": { - "react": "18.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -2527,11 +4371,54 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "optional": true, + "peer": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, + "devOptional": true, "dependencies": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -2553,6 +4440,16 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "optional": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2567,7 +4464,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "devOptional": true, "dependencies": { "glob": "^7.1.3" }, @@ -2601,6 +4498,70 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true + }, + "node_modules/sass": { + "version": "1.49.11", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.11.tgz", + "integrity": "sha512-wvS/geXgHUGs6A/4ud5BFIWKO1nKd7wYIGimDk4q4GFkJicILActpv9ueMT4eRGSsp1BdKHuw1WwAHXbhsJELQ==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-graph": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.0.tgz", + "integrity": "sha512-WSO/MfXqKH7/TS8RdkCX3lVkPFQzCgbqdGsmSKq6tlPU+GpGEsa/5aW18JqItnqh+lPtcjifqdZ/VmiILkKckQ==", + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "lodash": "^4.17.11", + "scss-tokenizer": "^0.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "sassgraph": "bin/sassgraph" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", @@ -2609,11 +4570,22 @@ "loose-envify": "^1.1.0" } }, + "node_modules/scss-tokenizer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.3.0.tgz", + "integrity": "sha512-14Zl9GcbBvOT9057ZKjpz5yPOyUWG2ojd9D5io28wHRYsOrs7U95Q+KNL87+32p8rc+LvDpbu/i9ZYjM9Q+FsQ==", + "optional": true, + "peer": true, + "dependencies": { + "js-base64": "^2.4.3", + "source-map": "^0.7.1" + } + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2624,11 +4596,18 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true, + "peer": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2640,7 +4619,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -2670,6 +4649,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true, + "peer": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2679,6 +4665,57 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "optional": true, + "peer": true, + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -2687,6 +4724,156 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "optional": true, + "peer": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "optional": true, + "peer": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "optional": true, + "peer": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "optional": true, + "peer": true + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "optional": true, + "peer": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "optional": true, + "peer": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/stdout-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stdout-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true, + "peer": true + }, + "node_modules/stdout-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true, + "peer": true + }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -2736,7 +4923,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "devOptional": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2753,6 +4940,19 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "optional": true, + "peer": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2788,7 +4988,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2800,7 +5000,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 0.4" }, @@ -2808,6 +5008,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "peer": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2818,7 +5036,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, @@ -2826,6 +5044,40 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "optional": true, + "peer": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.1.2" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -2859,6 +5111,26 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true, + "peer": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2986,21 +5258,85 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "peer": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "devOptional": true, "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true, + "peer": true + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "optional": true, + "peer": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "optional": true, + "peer": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "optional": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/vscode-oniguruma": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", @@ -3017,7 +5353,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3044,6 +5380,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "peer": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -3053,20 +5399,181 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "optional": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "devOptional": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "devOptional": true + }, + "node_modules/yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "optional": true, + "peer": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } } }, "dependencies": { + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "optional": true, + "peer": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "optional": true, + "peer": true + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "optional": true, + "peer": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "optional": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "optional": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "optional": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "optional": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "optional": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "optional": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "optional": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@babel/runtime": { "version": "7.17.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", @@ -3103,6 +5610,13 @@ "strip-json-comments": "^3.1.1" } }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true, + "peer": true + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -3232,12 +5746,41 @@ "fastq": "^1.6.0" } }, + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "peer": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "peer": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, "@rushstack/eslint-patch": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", "dev": true }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "peer": true + }, "@types/bcrypt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", @@ -3253,6 +5796,13 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "optional": true, + "peer": true + }, "@types/node": { "version": "17.0.23", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", @@ -3268,6 +5818,13 @@ "@types/node": "*" } }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "optional": true, + "peer": true + }, "@types/prop-types": { "version": "15.7.4", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", @@ -3344,6 +5901,13 @@ "eslint-visitor-keys": "^3.0.0" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true, + "peer": true + }, "acorn": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", @@ -3357,11 +5921,44 @@ "dev": true, "requires": {} }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "peer": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "optional": true, + "peer": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "peer": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "devOptional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3373,17 +5970,45 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "devOptional": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "requires": { "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "devOptional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true, + "peer": true + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "peer": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3441,12 +6066,64 @@ "es-abstract": "^1.19.0" } }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "optional": true, + "peer": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "optional": true, + "peer": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true, + "peer": true + }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "optional": true, + "peer": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true, + "peer": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true, + "peer": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "optional": true, + "peer": true + }, "axe-core": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", @@ -3463,13 +6140,29 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "devOptional": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "peer": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "devOptional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "devOptional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3479,11 +6172,43 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "devOptional": true, "requires": { "fill-range": "^7.0.1" } }, + "bulma": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.3.tgz", + "integrity": "sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==" + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "peer": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3500,26 +6225,105 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "optional": true, + "peer": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "optional": true, + "peer": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, "caniuse-lite": { "version": "1.0.30001313", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz", "integrity": "sha512-rI1UN0koZUiKINjysQDuRi2VeSCce3bYJNmDcj3PIKREiAmjakugBul1QSkg/fPrlULYl6oWfGg3PbgOSY9X4Q==" }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true, + "peer": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "devOptional": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "peer": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "peer": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "optional": true, + "peer": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "requires": { "color-name": "~1.1.4" } @@ -3528,13 +6332,37 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "peer": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "peer": true, + "requires": { + "delayed-stream": "~1.0.0" + } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "devOptional": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true, + "peer": true }, "core-js-pure": { "version": "3.21.1", @@ -3542,6 +6370,13 @@ "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", "dev": true }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "optional": true, + "peer": true + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -3555,7 +6390,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, + "devOptional": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3579,15 +6414,52 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "peer": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "devOptional": true, "requires": { "ms": "2.1.2" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "optional": true, + "peer": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "optional": true, + "peer": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "optional": true, + "peer": true + } + } + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3603,6 +6475,27 @@ "object-keys": "^1.0.12" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true, + "peer": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true, + "peer": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "optional": true, + "peer": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3621,12 +6514,57 @@ "esutils": "^2.0.2" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "peer": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "peer": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true, + "peer": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "optional": true, + "peer": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-abstract": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", @@ -3666,6 +6604,13 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "optional": true, + "peer": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4017,11 +6962,25 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true, + "peer": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "optional": true, + "peer": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "devOptional": true }, "fast-glob": { "version": "3.2.11", @@ -4051,7 +7010,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "devOptional": true }, "fast-levenshtein": { "version": "2.0.6", @@ -4081,7 +7040,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "devOptional": true, "requires": { "to-regex-range": "^5.0.1" } @@ -4111,17 +7070,53 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true, + "peer": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "optional": true, + "peer": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "devOptional": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "devOptional": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -4129,6 +7124,41 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "peer": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "optional": true, + "peer": true, + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "optional": true, + "peer": true + }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -4140,6 +7170,13 @@ "has-symbols": "^1.0.1" } }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "optional": true, + "peer": true + }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -4150,11 +7187,21 @@ "get-intrinsic": "^1.1.1" } }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "peer": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, + "devOptional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4196,11 +7243,67 @@ "slash": "^3.0.0" } }, + "globule": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", + "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "optional": true, + "peer": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "dependencies": { + "minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "optional": true, + "peer": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "optional": true, + "peer": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true, + "peer": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "optional": true, + "peer": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "optional": true, + "peer": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, + "devOptional": true, "requires": { "function-bind": "^1.1.1" } @@ -4215,7 +7318,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "devOptional": true }, "has-symbols": { "version": "1.0.3", @@ -4232,12 +7335,97 @@ "has-symbols": "^1.0.2" } }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true, + "peer": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "optional": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "optional": true, + "peer": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "peer": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "peer": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "peer": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "optional": true, + "peer": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "devOptional": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4252,13 +7440,27 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "devOptional": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "peer": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true, + "peer": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, + "devOptional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4268,7 +7470,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "devOptional": true }, "internal-slot": { "version": "1.0.3", @@ -4281,6 +7483,20 @@ "side-channel": "^1.0.4" } }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "optional": true, + "peer": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "optional": true, + "peer": true + }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -4290,6 +7506,15 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -4310,7 +7535,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, + "devOptional": true, "requires": { "has": "^1.0.3" } @@ -4328,17 +7553,31 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "devOptional": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "peer": true }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "optional": true, + "peer": true + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -4349,7 +7588,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "devOptional": true }, "is-number-object": { "version": "1.0.6", @@ -4360,6 +7599,13 @@ "has-tostringtag": "^1.0.0" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "optional": true, + "peer": true + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4394,6 +7640,13 @@ "has-symbols": "^1.0.2" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true, + "peer": true + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -4403,11 +7656,32 @@ "call-bind": "^1.0.2" } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true, + "peer": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "devOptional": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true, + "peer": true + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "optional": true, + "peer": true }, "js-tokens": { "version": "4.0.0", @@ -4423,11 +7697,32 @@ "argparse": "^2.0.1" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true, + "peer": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "optional": true, + "peer": true + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "optional": true, + "peer": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "devOptional": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -4435,6 +7730,13 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true, + "peer": true + }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -4450,6 +7752,19 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "optional": true, + "peer": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "jsx-ast-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", @@ -4460,6 +7775,13 @@ "object.assign": "^4.1.2" } }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true, + "peer": true + }, "language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -4485,6 +7807,13 @@ "type-check": "~0.4.0" } }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "optional": true, + "peer": true + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -4495,6 +7824,13 @@ "path-exists": "^3.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true, + "peer": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4513,7 +7849,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, + "devOptional": true, "requires": { "yallist": "^4.0.0" } @@ -4524,12 +7860,74 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "peer": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "optional": true, + "peer": true + }, "marked": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", "dev": true }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "optional": true, + "peer": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "optional": true, + "peer": true + } + } + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4546,11 +7944,35 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true, + "peer": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, + "peer": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "optional": true, + "peer": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "devOptional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4561,11 +7983,111 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "optional": true, + "peer": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "peer": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "peer": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "devOptional": true + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true, + "peer": true }, "nanoid": { "version": "3.3.1", @@ -4578,6 +8100,13 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "optional": true, + "peer": true + }, "next": { "version": "12.1.4", "resolved": "https://registry.npmjs.org/next/-/next-12.1.4.tgz", @@ -4601,11 +8130,146 @@ "styled-jsx": "5.0.1" } }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "peer": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "are-we-there-yet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "optional": true, + "peer": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "peer": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "npmlog": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", + "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "optional": true, + "peer": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + } + } + } + }, + "node-sass": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.1.tgz", + "integrity": "sha512-uMy+Xt29NlqKCFdFRZyXKOTqGt+QaKHexv9STj2WeLottnlqZEEWx6Bj0MXNthmFRRdM/YwyNo/8Tr46TOM0jQ==", + "optional": true, + "peer": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^9.0.0", + "nan": "^2.13.2", + "node-gyp": "^8.4.1", + "npmlog": "^5.0.0", + "request": "^2.88.0", + "sass-graph": "4.0.0", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "peer": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "optional": true, + "peer": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "peer": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true, + "peer": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "devOptional": true }, "object-inspect": { "version": "1.12.0", @@ -4678,7 +8342,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, + "devOptional": true, "requires": { "wrappy": "1" } @@ -4715,6 +8379,16 @@ "p-limit": "^1.1.0" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "peer": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -4730,6 +8404,19 @@ "callsites": "^3.0.0" } }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "optional": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -4740,19 +8427,19 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "devOptional": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "devOptional": true }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "devOptional": true }, "path-type": { "version": "4.0.0", @@ -4760,6 +8447,13 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true, + "peer": true + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4769,7 +8463,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "devOptional": true }, "postcss": { "version": "8.4.5", @@ -4787,6 +8481,31 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true, + "peer": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "optional": true, + "peer": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "peer": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4798,11 +8517,25 @@ "react-is": "^16.13.1" } }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true, + "peer": true + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "devOptional": true + }, + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "optional": true, + "peer": true }, "queue-microtask": { "version": "1.2.3", @@ -4810,6 +8543,13 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "optional": true, + "peer": true + }, "react": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", @@ -4833,6 +8573,163 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "optional": true, + "peer": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "optional": true, + "peer": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "optional": true, + "peer": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true, + "peer": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "optional": true, + "peer": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "optional": true, + "peer": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "optional": true, + "peer": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "optional": true, + "peer": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "optional": true, + "peer": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "optional": true, + "peer": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "optional": true, + "peer": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "optional": true, + "peer": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "optional": true, + "peer": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "devOptional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "optional": true, + "peer": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -4855,11 +8752,47 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "optional": true, + "peer": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "optional": true, + "peer": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, + "devOptional": true, "requires": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -4872,6 +8805,13 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "optional": true, + "peer": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4882,7 +8822,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "devOptional": true, "requires": { "glob": "^7.1.3" } @@ -4896,6 +8836,44 @@ "queue-microtask": "^1.2.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true, + "peer": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true, + "peer": true + }, + "sass": { + "version": "1.49.11", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.11.tgz", + "integrity": "sha512-wvS/geXgHUGs6A/4ud5BFIWKO1nKd7wYIGimDk4q4GFkJicILActpv9ueMT4eRGSsp1BdKHuw1WwAHXbhsJELQ==", + "devOptional": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-graph": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.0.tgz", + "integrity": "sha512-WSO/MfXqKH7/TS8RdkCX3lVkPFQzCgbqdGsmSKq6tlPU+GpGEsa/5aW18JqItnqh+lPtcjifqdZ/VmiILkKckQ==", + "optional": true, + "peer": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.17.11", + "scss-tokenizer": "^0.3.0", + "yargs": "^17.2.1" + } + }, "scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", @@ -4904,20 +8882,38 @@ "loose-envify": "^1.1.0" } }, + "scss-tokenizer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.3.0.tgz", + "integrity": "sha512-14Zl9GcbBvOT9057ZKjpz5yPOyUWG2ojd9D5io28wHRYsOrs7U95Q+KNL87+32p8rc+LvDpbu/i9ZYjM9Q+FsQ==", + "optional": true, + "peer": true, + "requires": { + "js-base64": "^2.4.3", + "source-map": "^0.7.1" + } + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "devOptional": true, "requires": { "lru-cache": "^6.0.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true, + "peer": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "requires": { "shebang-regex": "^3.0.0" } @@ -4926,7 +8922,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "devOptional": true }, "shiki": { "version": "0.10.1", @@ -4950,17 +8946,201 @@ "object-inspect": "^1.9.0" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true, + "peer": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "peer": true + }, + "socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "optional": true, + "peer": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "optional": true, + "peer": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "optional": true, + "peer": true + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "optional": true, + "peer": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "optional": true, + "peer": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "optional": true, + "peer": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "optional": true, + "peer": true + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "optional": true, + "peer": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "optional": true, + "peer": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "peer": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true, + "peer": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "peer": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "peer": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "peer": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true, + "peer": true + } + } + }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -5001,7 +9181,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "devOptional": true, "requires": { "ansi-regex": "^5.0.1" } @@ -5012,6 +9192,16 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "optional": true, + "peer": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5028,7 +9218,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "requires": { "has-flag": "^4.0.0" } @@ -5037,7 +9227,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "devOptional": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "peer": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } }, "text-table": { "version": "0.2.0", @@ -5049,11 +9254,39 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "requires": { "is-number": "^7.0.0" } }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "optional": true, + "peer": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "optional": true, + "peer": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "optional": true, + "peer": true, + "requires": { + "glob": "^7.1.2" + } + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -5081,6 +9314,23 @@ "tslib": "^1.8.1" } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "peer": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true, + "peer": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5174,21 +9424,78 @@ "which-boxed-primitive": "^1.0.2" } }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "peer": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "devOptional": true, "requires": { "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true, + "peer": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "optional": true, + "peer": true + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "optional": true, + "peer": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "optional": true, + "peer": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "vscode-oniguruma": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", @@ -5205,7 +9512,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "requires": { "isexe": "^2.0.0" } @@ -5223,23 +9530,84 @@ "is-symbol": "^1.0.3" } }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "peer": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "optional": true, + "peer": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "devOptional": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "optional": true, + "peer": true }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "devOptional": true + }, + "yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "optional": true, + "peer": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "optional": true, + "peer": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "optional": true, + "peer": true } } } diff --git a/frontend/package.json b/frontend/package.json index df7600e1..acaf766b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "bulma": "^0.9.3", "crypto-js": "^4.1.1", "next": "12.1.4", "react": "18.0.0", @@ -22,6 +23,7 @@ "cross-env": "^7.0.3", "eslint": "8.12.0", "eslint-config-next": "12.1.4", + "sass": "^1.49.11", "typedoc": "^0.22.12", "typescript": "4.6.3" } diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index bbf77cc7..905cd258 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,4 +1,4 @@ -import '../styles/globals.css' +import '../styles/globals.scss' import type {AppProps} from 'next/app' import { SessionProvider } from "../contexts/sessionProvider"; diff --git a/frontend/pages/login/index.tsx b/frontend/pages/login/index.tsx index 44ecc7a6..5b0e1790 100644 --- a/frontend/pages/login/index.tsx +++ b/frontend/pages/login/index.tsx @@ -1,5 +1,5 @@ import type {NextPage} from 'next' -import styles from '../../styles/login.module.css' +import styles from '../../styles/login.module.scss' import Image from "next/image" import GitHubLogo from "../../public/images/github-logo.svg" import {SyntheticEvent, useContext, useEffect, useState} from "react"; @@ -223,7 +223,7 @@ const Index: NextPage = () => { * * @param e - The event triggering this function call */ - const resetPassword = (e: SyntheticEvent) => { + const resetPassword = async (e: SyntheticEvent) => { e.preventDefault() let error = false @@ -238,6 +238,15 @@ const Index: NextPage = () => { // Field is not empty if (!error) { console.log("RESETTING PASSWORD") + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset`, { + method: 'POST', + body: JSON.stringify({email: passwordResetMail}), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }).catch() + console.log(response) setShowPasswordReset(false) } @@ -264,8 +273,8 @@ const Index: NextPage = () => {

Login

-
{ - submitLogin(e) + { + await submitLogin(e) }}>

Register

- { - submitRegister(e) + { + await submitRegister(e) }}>

{loginPasswordError}

Forgot password? - -

Please enter your email below and we will send you a link to reset your password.

+

Selections

-
- {sessionKey !== "" ? Students : null} - {sessionKey !== "" ? Projects : null} - {isAdmin ? Manage Users : null} - +
+ {sessionKey !== "" && router.pathname !== "/reset" ? Students : null} + {sessionKey !== "" && router.pathname !== "/reset" ? Projects : null} + {isAdmin && router.pathname !== "/reset" ? Manage Users : null} + {router.pathname !== "/login" && router.pathname !== "/reset" ? + : router.pathname === "/reset" ? + : null + }
) diff --git a/frontend/pages/login/index.tsx b/frontend/pages/login/index.tsx index 1e4add53..1bc1b530 100644 --- a/frontend/pages/login/index.tsx +++ b/frontend/pages/login/index.tsx @@ -237,7 +237,6 @@ const Index: NextPage = () => { // Field is not empty if (!error) { - console.log("RESETTING PASSWORD") const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset`, { method: 'POST', body: JSON.stringify({email: passwordResetMail}), diff --git a/frontend/pages/reset/[pid].tsx b/frontend/pages/reset/[pid].tsx new file mode 100644 index 00000000..81a0cc80 --- /dev/null +++ b/frontend/pages/reset/[pid].tsx @@ -0,0 +1,92 @@ +import {NextPage} from "next"; +import {useRouter} from "next/router"; +import {Header} from "../../components/Header/Header"; +import styles from "../../styles/login.module.scss"; +import {SyntheticEvent, useEffect, useState} from "react"; + +/** + * Landing page for when you click the reset password link from your email + * Being of form /reset/resetID + * @constructor + */ +const Pid: NextPage = () => { + + const router = useRouter() + const {pid} = router.query // pid is the session key + + + /** + * Check if the provided code is a valid code + */ + useEffect(() => { + fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset/${pid}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'applicatin' + } + }).then(response => { + // The code is not valid + if (!response.ok) { + router.push("/reset").then() + } + }) + }) + + const [newPassword, setNewPassword] = useState(""); + const [newPasswordError, setNewPasswordError] = useState(""); + const [confirmNewPassword, setConfirmNewPassword] = useState(""); + const [confirmNewPasswordError, setConfirmNewPasswordError] = useState(""); + const [backendError] = useState(""); + + /** + * Handles all the logic for resetting the password + * @param e + */ + const resetPassword = (e: SyntheticEvent) => { + e.preventDefault() + let error = false + if (newPassword === "") { + setNewPasswordError("Password cannot be empty") + error = true + } else { + setNewPasswordError("") + } + + if (confirmNewPassword != newPassword) { + setConfirmNewPasswordError("Passwords do not match") + error = true + } else if (confirmNewPassword === "") { + setConfirmNewPasswordError("Please confirm your new password") + error = true + } else { + setConfirmNewPasswordError("") + } + + if (!error) { + console.log('RESETTING PASSWORD') + } + } + + return <> +
+ + +

{newPasswordError}

+ +

{confirmNewPasswordError}

+ +

{backendError}

+ + +} + +export default Pid; diff --git a/frontend/pages/reset/index.tsx b/frontend/pages/reset/index.tsx new file mode 100644 index 00000000..842d12b5 --- /dev/null +++ b/frontend/pages/reset/index.tsx @@ -0,0 +1,15 @@ +import {NextPage} from "next"; +import {Header} from "../../components/Header/Header"; + +/** + * You are redirected here when your resetID to reset the password is not valid + * @constructor + */ +const Reset: NextPage = () => { + return <> +
+

The provided code to reset the password was not valid.

+ +} + +export default Reset; diff --git a/frontend/styles/globals.scss b/frontend/styles/globals.scss index 6e9e6972..ffd5395f 100644 --- a/frontend/styles/globals.scss +++ b/frontend/styles/globals.scss @@ -22,6 +22,7 @@ html, body { padding: 0; margin: 0; + width: 100vw; font-family: Montserrat, sans-serif; } diff --git a/frontend/styles/login.module.scss b/frontend/styles/login.module.scss index 6c7e0d9a..1aa6e861 100644 --- a/frontend/styles/login.module.scss +++ b/frontend/styles/login.module.scss @@ -1,5 +1,4 @@ .body { - padding-block: clamp(1rem, 2vw, 2.5rem); width: clamp(18rem, 80vw, 45rem); margin-inline: auto; } From 2b3277b893219738a9f4c5c76c4b364bc7acd321 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Mon, 4 Apr 2022 19:42:02 +0200 Subject: [PATCH 143/827] feat: reset password seems to work --- frontend/components/Header/Header.tsx | 10 +-- frontend/pages/reset/[pid].tsx | 91 ++++++++++++++++----------- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/frontend/components/Header/Header.tsx b/frontend/components/Header/Header.tsx index c52bc64d..b96d1857 100644 --- a/frontend/components/Header/Header.tsx +++ b/frontend/components/Header/Header.tsx @@ -43,12 +43,12 @@ export const Header: React.FC = () => {

Selections

-
- {sessionKey !== "" && router.pathname !== "/reset" ? Students : null} - {sessionKey !== "" && router.pathname !== "/reset" ? Projects : null} +
+ {sessionKey !== "" && !router.pathname.startsWith("/reset") ? Students : null} + {sessionKey !== "" && !router.pathname.startsWith("/reset") ? Projects : null} {isAdmin && router.pathname !== "/reset" ? Manage Users : null} - {router.pathname !== "/login" && router.pathname !== "/reset" ? - : router.pathname === "/reset" ? + {router.pathname !== "/login" && !router.pathname.startsWith("/reset") ? + : router.pathname.startsWith("/reset") ? : null }
diff --git a/frontend/pages/reset/[pid].tsx b/frontend/pages/reset/[pid].tsx index 81a0cc80..1ed6618c 100644 --- a/frontend/pages/reset/[pid].tsx +++ b/frontend/pages/reset/[pid].tsx @@ -3,6 +3,7 @@ import {useRouter} from "next/router"; import {Header} from "../../components/Header/Header"; import styles from "../../styles/login.module.scss"; import {SyntheticEvent, useEffect, useState} from "react"; +import crypto from "crypto"; /** * Landing page for when you click the reset password link from your email @@ -14,31 +15,33 @@ const Pid: NextPage = () => { const router = useRouter() const {pid} = router.query // pid is the session key - - /** - * Check if the provided code is a valid code - */ - useEffect(() => { - fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset/${pid}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'applicatin' - } - }).then(response => { - // The code is not valid - if (!response.ok) { - router.push("/reset").then() - } - }) - }) - const [newPassword, setNewPassword] = useState(""); const [newPasswordError, setNewPasswordError] = useState(""); const [confirmNewPassword, setConfirmNewPassword] = useState(""); const [confirmNewPasswordError, setConfirmNewPasswordError] = useState(""); const [backendError] = useState(""); + + /** + * Check if the provided code is a valid code + */ + useEffect(() => { + if (pid !== undefined) { + fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset/${pid}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'applicatin' + } + }).then(response => { + // The code is not valid + if (!response.ok) { + router.push("/reset").then() + } + }) + } + }, [pid, router]) + /** * Handles all the logic for resetting the password * @param e @@ -64,28 +67,44 @@ const Pid: NextPage = () => { } if (!error) { - console.log('RESETTING PASSWORD') + // We encrypt the password before sending it to the backend api + const encryptedPassword = crypto.createHash('sha256').update(newPassword).digest('hex'); + fetch(`${process.env.NEXT_PUBLIC_API_URL}/reset/${pid}`, { + method: 'POST', + body: JSON.stringify({password: encryptedPassword}), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + }).then(response => { + if (response.ok) { + router.push("/login").then() + } + }).catch(error => console.log(error)) } } return <>
-
- -

{newPasswordError}

- -

{confirmNewPasswordError}

- -

{backendError}

-
+
+

Reset Password

+
+ +

{newPasswordError}

+ +

{confirmNewPasswordError}

+ +

{backendError}

+
+
} From 94ff3214e47f876569dc5ff92ad3784aaac2e90f Mon Sep 17 00:00:00 2001 From: Huan Date: Mon, 4 Apr 2022 22:39:11 +0200 Subject: [PATCH 144/827] fix: the buttons now communicates properly with the backend and the login users are now unique --- frontend/components/User/User.tsx | 8 +++++--- frontend/pages/users.tsx | 8 ++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/components/User/User.tsx b/frontend/components/User/User.tsx index ad65bcbc..da870c93 100644 --- a/frontend/components/User/User.tsx +++ b/frontend/components/User/User.tsx @@ -51,20 +51,22 @@ export const User: React.FC<{ userName: string, userEmail: string, userIsAdmin: console.log(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString()) return await fetch(`${process.env.NEXT_PUBLIC_API_URL}/` + route + "/" + userId.toString(), { method: 'POST', - body: JSON.stringify({coach: isCoach, admin: isAdmin}), + body: JSON.stringify({isCoach: isCoach, isAdmin: isAdmin, accountStatus: status}), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': `auth/osoc2 ${sessionKey}` } }) - .then(response => response.json()).then(json => { + .then(response => response.json()).then(async json => { if (!json.success) { reverseRole(changed_val); return {success: false}; } else { + console.log(json) if (setSessionKey) { - setSessionKey(json.sessionKey) + await setSessionKey(json.sessionkey) + } return json; } diff --git a/frontend/pages/users.tsx b/frontend/pages/users.tsx index a23dfe34..e29d692f 100644 --- a/frontend/pages/users.tsx +++ b/frontend/pages/users.tsx @@ -32,24 +32,21 @@ const Users: NextPage = () => { .then(response => response.json()).then(json => { if (!json.success) { - //TODO logica van niet gelukt + //TODO Popup of zoiets return {success: false}; } else return json; }) .catch(err => { console.log(err) - //TODO logica van niet gelukt + //TODO Popup of zoiets return {success: false}; }) } let tempSes = ""; getAllUsers("admin",sessionKey).then(response => { - console.log(response) tempSes = response.sessionkey - console.log(test) test = [ ...test, ...response.data]; - console.log(test) test.forEach(userSet.add, userSet); }).then(() => { getAllUsers("coach",tempSes).then(response => { @@ -62,7 +59,6 @@ const Users: NextPage = () => { console.log(userSet) }).then(() => setUsers(Array.from(userSet))); }) - //TODO The code above doesn't work now because the sessionkey doesn't update for some reason. Await doesn't work. // We need to disable this warning. We of course do not want do reload the page when the data is changed // because that is exactly what this function does. From 66dd4fc1420c4a2066fe4600f4e9aaa86cb90878 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 03:29:16 +0000 Subject: [PATCH 145/827] npm-root(deps-dev): bump @typescript-eslint/parser from 5.17.0 to 5.18.0 Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.17.0 to 5.18.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.18.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 143 +++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 131 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4898740..6bb77384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.17.0", - "@typescript-eslint/parser": "^5.17.0", + "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.4", "eslint-plugin-jest": "^26.1.3", @@ -767,14 +767,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.18.0.tgz", + "integrity": "sha512-+08nYfurBzSSPndngnHvFw/fniWYJ5ymOrn/63oMIbgomVQOvIDhBoJmYZ9lwQOCnQV9xHGvf88ze3jFGUYooQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/typescript-estree": "5.18.0", "debug": "^4.3.2" }, "engines": { @@ -793,6 +793,80 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz", + "integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", + "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", + "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", + "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.18.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", @@ -5897,15 +5971,58 @@ } }, "@typescript-eslint/parser": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", - "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.18.0.tgz", + "integrity": "sha512-+08nYfurBzSSPndngnHvFw/fniWYJ5ymOrn/63oMIbgomVQOvIDhBoJmYZ9lwQOCnQV9xHGvf88ze3jFGUYooQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/typescript-estree": "5.18.0", "debug": "^4.3.2" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz", + "integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0" + } + }, + "@typescript-eslint/types": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", + "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", + "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", + "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.18.0", + "eslint-visitor-keys": "^3.0.0" + } + } } }, "@typescript-eslint/scope-manager": { diff --git a/package.json b/package.json index 2fdf3537..10d2f36e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", "@typescript-eslint/eslint-plugin": "^5.17.0", - "@typescript-eslint/parser": "^5.17.0", + "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.4", "eslint-plugin-jest": "^26.1.3", From 0eebfd9fd5da412a3ec32c4a72f0b8b3b2ee43f2 Mon Sep 17 00:00:00 2001 From: Huan Date: Tue, 5 Apr 2022 10:51:55 +0200 Subject: [PATCH 146/827] docs: fixed some inconsistency in the api doc --- docs/API/api.json | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/API/api.json b/docs/API/api.json index 0ddecef7..de582b79 100644 --- a/docs/API/api.json +++ b/docs/API/api.json @@ -629,26 +629,25 @@ "in": "header", "name": "name", "description": "The name of the project.", - "required": true, "type": "string" }, { "in": "header", - "name": "coach", + "name": "isCoach", "description": "Shows the user is a coach.", "required": true, "type": "number" }, { "in": "header", - "name": "admin", + "name": "isAdmin", "description": "Shows if the user is a coach.", "required": true, "type": "string" }, { "in": "header", - "name": "status", + "name": "accountStatus", "description": "Shows the status of the account.", "required": true, "type": "string" @@ -764,27 +763,26 @@ "in": "header", "name": "name", "description": "The name of the project.", - "required": true, "type": "string" }, { "in": "header", - "name": "coach", + "name": "isCoach", "description": "Shows the user is a coach.", "required": true, "type": "number" }, { "in": "header", - "name": "admin", + "name": "isAdmin", "description": "Shows if the user is a coach.", "required": true, "type": "string" }, { "in": "header", - "name": "activated", - "description": "Shows if the account is activated", + "name": "accountStatus", + "description": "Shows the status of the account", "required": true, "type": "string" } From 0cccd1b2f2ac2a2fd85170f39a5bdb4fdac278a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 08:58:03 +0000 Subject: [PATCH 147/827] npm-root(deps-dev): bump @typescript-eslint/eslint-plugin Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.17.0 to 5.18.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.18.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 243 ++++++++++++---------------------------------- package.json | 2 +- 2 files changed, 64 insertions(+), 181 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6bb77384..8bd0151a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@typescript-eslint/eslint-plugin": "^5.17.0", + "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.4", @@ -734,14 +734,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.18.0.tgz", + "integrity": "sha512-tzrmdGMJI/uii9/V6lurMo4/o+dMTKDH82LkNjhJ3adCW22YQydoRs5MwTiqxGF9CSYxPxQ7EYb4jLNlIs+E+A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/type-utils": "5.18.0", + "@typescript-eslint/utils": "5.18.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -793,7 +793,7 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/scope-manager": { "version": "5.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz", "integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==", @@ -810,87 +810,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", - "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", - "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.18.0", - "@typescript-eslint/visitor-keys": "5.18.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", - "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.18.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.18.0.tgz", + "integrity": "sha512-vcn9/6J5D6jtHxpEJrgK8FhaM8r6J1/ZiNu70ZUJN554Y3D9t3iovi6u7JF8l/e7FcBIxeuTEidZDR70UuCIfA==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/utils": "5.18.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -911,9 +837,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", + "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -924,13 +850,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", + "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -951,15 +877,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.18.0.tgz", + "integrity": "sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/typescript-estree": "5.18.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -997,12 +923,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", + "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/types": "5.18.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -5954,14 +5880,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", - "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.18.0.tgz", + "integrity": "sha512-tzrmdGMJI/uii9/V6lurMo4/o+dMTKDH82LkNjhJ3adCW22YQydoRs5MwTiqxGF9CSYxPxQ7EYb4jLNlIs+E+A==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/type-utils": "5.17.0", - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/type-utils": "5.18.0", + "@typescript-eslint/utils": "5.18.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -5980,86 +5906,43 @@ "@typescript-eslint/types": "5.18.0", "@typescript-eslint/typescript-estree": "5.18.0", "debug": "^4.3.2" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz", - "integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.18.0", - "@typescript-eslint/visitor-keys": "5.18.0" - } - }, - "@typescript-eslint/types": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", - "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", - "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.18.0", - "@typescript-eslint/visitor-keys": "5.18.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", - "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.18.0", - "eslint-visitor-keys": "^3.0.0" - } - } } }, "@typescript-eslint/scope-manager": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", - "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz", + "integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0" + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0" } }, "@typescript-eslint/type-utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", - "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.18.0.tgz", + "integrity": "sha512-vcn9/6J5D6jtHxpEJrgK8FhaM8r6J1/ZiNu70ZUJN554Y3D9t3iovi6u7JF8l/e7FcBIxeuTEidZDR70UuCIfA==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.17.0", + "@typescript-eslint/utils": "5.18.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", - "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz", + "integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", - "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz", + "integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/visitor-keys": "5.17.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/visitor-keys": "5.18.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -6068,15 +5951,15 @@ } }, "@typescript-eslint/utils": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", - "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.18.0.tgz", + "integrity": "sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.17.0", - "@typescript-eslint/types": "5.17.0", - "@typescript-eslint/typescript-estree": "5.17.0", + "@typescript-eslint/scope-manager": "5.18.0", + "@typescript-eslint/types": "5.18.0", + "@typescript-eslint/typescript-estree": "5.18.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -6100,12 +5983,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", - "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz", + "integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/types": "5.18.0", "eslint-visitor-keys": "^3.0.0" } }, diff --git a/package.json b/package.json index 10d2f36e..e2ea2cc0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@typescript-eslint/eslint-plugin": "^5.17.0", + "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", "eslint-config-next": "^12.1.4", From 9a349162180b32d3e50c44d21af95eac1192c9ba Mon Sep 17 00:00:00 2001 From: Huan Date: Tue, 5 Apr 2022 11:19:33 +0200 Subject: [PATCH 148/827] docs: corrected the github callback url --- docs/deploymentGuide.md | 2 +- docs/gh-oauth-new-app.png | Bin 61753 -> 44431 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploymentGuide.md b/docs/deploymentGuide.md index 2d72276b..7c711396 100644 --- a/docs/deploymentGuide.md +++ b/docs/deploymentGuide.md @@ -57,7 +57,7 @@ To enable GitHub login, we need a GitHub application. These are made by going to ![Default settings](./gh-oauth-new-app.png) -You can change the `Application name` to anything you'd like. Be sure to confirm that the `Homepage URL` is correct for your server. The `Application description` can be anything you like. The `Authorization callback URL` should be the `Homepage URL` with `/api-osoc/github/` behind it. +You can change the `Application name` to anything you'd like. Be sure to confirm that the `Homepage URL` is correct for your server. The `Application description` can be anything you like. The `Authorization callback URL` should be the `Homepage URL`. #### Configuration part In the `/backend/` folder, edit the `github.json` configuration file. There are three fields used, and most can be copied straight from the GitHub application you just created: diff --git a/docs/gh-oauth-new-app.png b/docs/gh-oauth-new-app.png index 5927d444be78a71a163b7fe5036ced6d82f518e6..659f3ae9db9a74c51b8ca6812f86d54da4b11577 100644 GIT binary patch literal 44431 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 aR!CF_dEd%hl*#t0-|P$fTf^S(fA|L(W8wb* literal 61753 zcmb@uby$?)*ENb_Qvwo7=^&tVcNjyLbc1va-K`>BA|@#q3^{Oa ze&>7t`L6em^S$4>u7PJ{W*(pCj=k4jYwZbprzB1A;K>6V92^2!8K^1_4la0pQ~%yg z@R!lE_7~uA-%&=#1qX+){rcxd0t+D(4$e~?S?FtZ&$P{H4_8vHiMz8N!}{iRGKmbw zGd^waIj3C}@c5oR3p#{#Cz(}RH~h_mc{ALXzxV9{U?hA8uTj>+w=^oh7v!L{`RKC854=jQn!r+MWp@lq0`ufS8FSIwnf%292 zEpT{`vqlUKA8!1gzxY9*YYP|KXy}cZEA$^OvMMfgkWuOtc07r%FSPw4Qcvzvci1Fg z^s}JE=V&X&VSs(MK#*EV-SZvP>O)~=U{H{kmX?Q$i@c`&+&JD#uiJmHFv`^(>>v*^ z^k;Q3wk`=hVpaW?QUhu=%Y#hPul?}+%ZpPr9wtSR~Cwp^Yl z{-_(TvZvThLbxrK8QK?U>985^$p#@Oob zed~IU3HmqHGuBBj#&ew47J82$KD3x!U!l;!s(wxCa;!ZjV#!hPoG^Wu(Awkyjw8q8 zltUS~Do4O}R0acnoA$k4Q`6ro3P zvre4HwpjS#D&vWFgT&Qhpp?|7=;%H+5u)ct-7uKyZmqsgo$KOmXnA&UP{;{#3wDLw z3m-*b3@7(b8_OOBKx`a#IC*$z$caV29a_1<%)YmHcu5{L#nVntX?f2sGbk({c-@Uo z8_kynze#p)GbfypZxQ3nmhhN0NjI&!3QNjwl=w-rN&XHqHGO;2*I^*~5u06$!?a7p zlaOX8`EMpV9W%4%FJ3TX-Iq#_qLL0ssx|f0RSnfuGtyG3FMgeJi~Kbu>bqLpp!2b# zrJgl4ffcD2@H?QdT7Q4ueR!4WKRq~IT({M^H|BQUGC1i@Q=i^|i*vHku%uQw#O)F$ z^MaR0fTwBXyq>;CLD9a=@Whw4jkCsBdW_GsTM+Mumyh&HzESDuij-Kc`C^f7Ls;mi zn~k^^joyC3tlXbN*$ulzlRRY$M^aK!Zeidq9>n)faN(G-x=%G-VgVm;b|scHk7(D{ zs)R8?x^|o*A}?A++_h^XDg(RTXd9tDp9yWv#tzwS(KPXUI8|iw`sXBtun;dTQ*4Pl z>ci|#r%2_&9Ccp?vBk-WpRC9?EtgNo!SepZU>rRXQQ9@t9_pH7+hU#&Hu{i~9v|~U z#Z+GJiJXCum`sC%4LVp@Soz-JUsKVkv~+57x!i)U!y6*OK z=b8R=6jb1bwOx2B4RIryY8!+VGdSfen937Su2mkRe7qT=ajyF?)hh$W%|{Dc4Mgdp zK6`WLE8bqU4^`f(xfuHIEpMG~tW)`DT2NZ(>nptbP!kb{n}mcHl!*AR|AxCz@I4`* zYE3N_%~gkI`d+eOMOXhlZD|p!ix1VrHm;ql;hp7)`&{wavJ42YjHN5fZBZq}#=aj8 zy$6{-*EEpDP0XhC%%*@Pj2LVSfQ z%W+JRx}28l8MvKe4l^T_zVj&5pJPR|$LeBf%MPW4@bcyD`!Jyf!P&{HOKu5~6t zgKt-qYWZOuASU^y0jHT`lH|V))he-mVhd7GVUdXl=<*U7pCp4hSl{bj0}iV!ASQU! zEv?0t)`$?3JxW*);HM1M5%id7FQ=yrURz(+gpUwn+`%I|SlWMoB!*0b@64|4?M+%E z#H**Xw!BXgZJ6b{*Pu&?ZSK8cbV7__jre=@uAcy^=C9XlE* z-S6toteh$D{zX2WdWkH7kHI`4=#TkkFuM{)SportAQ-a}NBQyajIF*nfCn0F4KWGG z;M28pQ#6qH^<04}lhaMqC#TrXZrQ>jBauC0o4r|_mgVw%!>vCfH}{W(q4ygzVL!98|GN1VrQC|L z+Q{_Ph55~Q@6@~Pm$udl0)8mRz!>;PH6g{Csj=w+Bc6?72Dg2imGVWLFZ8cs>4e=o zI+4Ad$aLh~s!K)Ldx;yF=$V1P7HMIB*TfC&fBd?MbW{x*U#*Rl>AP+zJ;DRmG_2^^0nB@$|6T?s#W#&27~Tj zHfQv+wp<-~NSFpVK*^V5IP_Xq4)|f?e!Ht{B_;O!evcoM4qW-u`M+n6>6`?cGI8rX zo|!MHEm@Sy-tc$MsY@QMkW+KH>>hUgNNqt{M5sGXt@1EV)KhdjE`~iZv{*S^aiYvT zCM!3gbKwsmo*u8m+2Z}|?(RaR^oF14pyGCK!{3WXsUBMrt`(VXYuqBTzlZmrhYK93 z%4tf&Hr4oTU{A3JSY4Kd&azEnM)XU0OwAOa^0wz77G|dF$v`pOYb^Z|%oPu#qoZc( zH=gQQ&NW(tI|B>5aLn!{TcH^xSR`8@2A+8}aMU|Y6l4mlxz-;RHLixfH*)as^KN&y z;Ka}Fj|=VU9Eu8k5Z*meI>58I_>?K@EI4n&f+u058fpf6{$(P3e9YC{jO`^udQ-dX z%g6G|{ha(Hw{D+2XUsoBzvo!f@d6JHvC1CR#U>y00B_srGzfg*iHKwIQ+B4((9lJ9 zL}_!GzL~MOvuAr>lXU6GGJ!$q?iZW{IqbrOV%W>a!o{P=A-B`F2kF#i#~f*=;{p+AgO-po6wjJ%6;~9Bl0}Txd#z;uXxUDZ_a#gFHdpj0~S)-&D zmgi4~O(W>v;1(3v3fN6bL+PcVch0=3s}O#t)4KeDT-+oInYw2Z?TL?jmXx_mzPBfyYGXLCY-+Qt7@ZZ|BP4& zHEFO?MZ$%D0A^&9h=^E2ePmP2^Hb>Nk}9J8&tHE}^`BX}M+>F?tYB?<#3rcip4Nas z+}ri-9+jFEj_eP?4vS(Qes;*(an@KKTb?rL)HB&NJZ=w|`^ zynltocH;@y;S174bX^~JJ*UQpm`DZ+s#8hghQ@CU{hqJL9L2uY;#Pt)Kko_Yk zOZne#Z-%aR?Ny2uCM0UCpG%a;6>o#-#cu=IcC_-MtzYYMqs`$gnTYtwqC6Bp)NKv_ zeb`sc!i3P`KckvrNFu5`?i%@hhWeOAw8WLWVyUwlsVT(d+eYR^o`s}b%CE+>l!-f# zd1sEynJ=%5f{m3j<+n%BYKA%n+WH-w_#(B*iLvrfL)wzf;O!D6eRct+(ibB6AG`^x zyy}QmY|i+G=-t;Af0m%n!1qiL`?t%%!fb`>> z0V_gUAu+OJf%o^WDk;qweK&B4NUbg;2!KfeTX#l^X{k3QWYxGB_s}qdK}ZA(%Z^8krX2!h*bJtuQ6ae@5m^2@hjKQeDso)-NlbLfn}( zDzkEnvbsSwU7r7@^={M8tH6gVvp*stYJbmF%tc%WX2*a)*x1=|uou^AiWb|^K%ZB} z=?gf++k=sF4^`r_g4n{s@SkPj{k0Zrm;#G?xi6Ap_w>gvf6Kmn{hIL`1=}t&RTDQB zUA6ERy%XX!R?im0f+Wq`AfN0Q_|Lu~Fwwys%QGY4pzgG+_D79}hy85Ksvq0t)!hPd z!$)J;xh@*x?v5}iM|JmUlC16&U%fT|kb~fds;lXtkNIVCml9l;uHH3m_)Ffv*_Nu= zZI(WYQlvs`x_h|oV_c)|-8TbEiCP+hun0h$b90kCj7`W5HKxo}9r1`px@G$8PUaa% zGLVsWMmxH>Q%dNH`nyId<+HG|Po8wrVjsTZpeB@lPC}vrwc43l?Qgvm;O^k+>RMq+ zJ;vugT(%b1xqkOZo!L!A*2_(?DP3LkORtDX0#>UGK~798;)1v;8{&4R*)nf7vr*(QYu~Z+~{c;!MN*2Kpqfp*LC#{rrZatmokFc?}qlcK9W;aW;y*b#Jl90_G zN*BuXUG}X2RXIS0lS2vVg!}6UG1!%Zgk&)(_roe(J9*pGt~V!}$Q{F@kRKk{kN6x0 zI-w<+0D|~(^U>E7HOdVP77VRr_{09<3}tWF*w{bIBD=yK!RFn_4?%6IkW9I;JMJbo3YV1WJ*;WJZNZW@Deqg3jG-{q>Y!h6mUPb(gN zHMakiH?37XIy%brrPI5tIN2!qw;LP4YLv9|=;0z%`LUytp_91=fym_AMhv>~$6+W| zB0j2&0uS#Qv9Z__oC}WHr8VNz)zzJJZ8be*EvX9cSUDIBRs`}cs01~b-P9_7cdLS6 zAcrjU){Hao>KoHYPEOVh{|pMzALVtq)my%*>!&%xZd4wk=O?%T7&y^qm!{AXtGL-U zb-j5}8Fwfbb2#L2rx{<*VID;ct+Lt5tE9j`lSHjOeVdh+mr>%Rq;Ej25E&IA&w@&$ zulcT>tLjrL($FCMnCkwavfAIimtW*y0Al-zPP+;lcv~2v^A6+XM*db(NOwy0rijn= zoG#jmX|M_YWBP|q_3rs+V)-yrSpmDC{ZGWsvG!VA8)%ZkxUxdsh$qSuyOH| z6rDIVg~h6PhPFr*`mIQyF;9>SGqXuiO?>gi#oAA2k9D!2`f$CcRWMI6!kIa7TDpHAKFo8kfcpH-mo9_BWp z4XiAT%)^={YU-*p$1km$V3Te?o0{6DdJY|w#eW*@kE(qrU1>Y+r_YkN9^Vvu#$aMA zKp_ng_PaDepJ|M^fie;Lh{nkan@;mL`R zxeX49we`(bb`xi(ih#XjbA9&X#pLI?s=1{-Gh#xlsF={hCTS4&&VKuk7OnB|^Ob5J z>1k=zB!wUwN+kr?**hlW+Bxx)kZ#Uj*pej{axLk|b-pEOsRy)pi#6PzbQ>O=NOX!S zf%B}yg?06J1~%cn0s(+H=+60FL!FxzFG<8JFD`AdgGO#R8;nz{N8X#i&LMAb>-5>% zI05BR+Pq7dgjDSB>E+)1K`mk735u@G_@`m0v)mP0Fp#>F6UVfIuW`St7oe!_iDCX- z%;M1T^WXJpkf!}64Q1+QBvvtZE7mNmesn+kci5yP%-=@tHnlrTK ze`QKU+$;Z0)P0zfHbxo>b{$2Azl;q_Nuh(N>*kp)KiEL-HNK8fG=+fwyk=S_acPC! zC%|Tg2yZ4Y$%B>5R&1x4NRHq3LU%&^9K(dQlB~=!ADK{$5fcp9^BY>}&zOR%-|y`$ zDAs~9NOs3Fs{kxBIJ!?YR&9QMnUvw@`5Q&7 zLj3E9APTS&i4bE@-6S8p0-k={)3rX_uPY^?PJ|4pcrvaCLV#x5}oc zLZ^}&{^)^93mYkRVlMolBC8Pz!|ZZd6}7Ra38;rEHXTPp^bk2@kKfhMPLc$#kqwLg zd*k1@c%;0#Y$3*|MTNo=Q#;#!{z98|5QIVXM3NT;icd3`A!=91K3D3?RQex)k z;#ZRUTUL&61v}%kPP%4C$oH^a2%=hr2rn+4@jY4#?#NSaF`TYx;e5$p+W&DOt|Laj z-fHygDbK`&2x^*-|7G*J6Smr(@jjr_K~PWux2u%D;4B!ilX^LjR2T{{=t0{$LDFDF zHvOk`nk5kMK>&k47HT-A+o*o94C*{PgSN<*3yo6-p+8LX*A{^vC46UgD>gr<`7d0L= zQjjjzS`*!mOUvP>=~uoYUcX;RICwBg3%yk^e7(0f6vov5{3zvAPj9T%;!T}aeYE!G zVKxRsh&|9KDg6HPaK|Blo0a=HYdd;m{MQ?+nTY`%9Rocb4WE;fznQ>>o>;lO+>^T zPdT1%zg5*MkBDn3ZVzsIym|1>Vbwc9YqZF)#(W}&i?&1wL6Um$`|IXZ)|L<9kuFiG zdcn;3V#7<)g$rNNG-@+IFai>0(w(mUhxAu#-V{)ke@lrKVdX}R56pDQe9?O63`ol# z*_MY%p2%q2YPYC`d6Uh}c71jx9+9ifAo-OzRjjWlP$!(LlGYj`z{c8`_ZqS*Dn8T6 zss%+6sPt=5FD#|&y54*<(pG(^UT=3iXFIJ#gELFph7$2HY#B{|`Y2_uwh@8B7_y8R zdRypxZ#f1sL38{J`Uy*|fTJLFSd@mm-no(G4WVp)&2eA|3Zn_3u=8A@|xy~a! zK8WPzA;e!YWUm|^6x!^p%E z?_IxuGe{2(IJhz~*9ZLn>la(8dOGAuqzE;rug@2Fj2tBmbogBEfuZ(gx&F&BK7Psn zKZ4``g;Z&4Y|V3`nc;VF3BtXo^+gw0^fuKKsrF2)sGY!o=I05aajJt9y{VUjl|%yk zFXg(K9ja&uL)th&9kwyDtaukgu~!fhSWGNYSbUYwK)2i2z;dV2cT-&+KTlPRgr z&pq**Uuzn?HMRG5l9!)Ugq_c%aui)+{8;3PiK}ZomX|5C#zqM=-g`jHg2vCA`!Xy# zxHNy4-kj?3ni8P_GHk0@gun^oo11=s@0QEu=O+0aaz|gTpVl`O>N>j%2Xq}lb2?y& z0|FXNcDJTA9uMQ{4@`h3@tV>fd_ zz?G@8lK+6nm8X&g)3dC83D2$K`P4`HSx;S+|B=Q}?a zG@6(Nyt#((l^E2&t|NN%1#?VWcx2q!_CJe(WCu1B0BQ(^Jo$ zOrE8JUy+ev_lS-hY&5lX!pYGB2KJUd#WJWfrRaSeiT;!c?_~A;!~OFl_LN*JYtnl_ zC{P(7ehuZdo%rl^KTd=BXuMsX(79nH)B7C+vE6`fs_u1IiEmERNCPSlngML*55FG6 zydP1|(m&j-bio5`1BDvTnw;fVLvPKQ2D)O;1GEtkU?#Qo?%ej=keH}HXk{|GI_V2R zue#QQ@W`VUXS-#--aY_5{No`JQ0YAFAyJ}LFiia821wnYl$kuHzFb9W7+vkkgW5eA z9u`$??r#4>70w9I+*CASpiU2IXCx;dpAc{XWOHaWU9BBka^wJUy)!a=R7)F+T=uv; z*jiy3eHdF<*`^T)cd$B}EZk|@+u?BD52p-va<6F7d=A3`x;P1avf6vx41?J$I}6&5 zIrtoBJ9y9uDnXr-7owt(|XluTFc#fX6m1QhvN1G)$iooKJ95>lZ#GP1*W zJt(gqZ_YkR#b?MjCsx_d?Rgy6860u~wwOqRO=Rd-waQSL?13EfMU?CxaB8+^S&PF8 zkOBu7b7_%#RnoXD!`MWGg2m)?v2jS+Ah&a(#PJ=yX1lQ;T=dyjm%p8*eMES8)F395 zd6`x;o958?_^-d3#WLghS-IO^vyW)WAbS|H!1{#DAC&kzb5|aW542T;+c+p|iD!;Y zjLs-OUrOLhmt!D)sXI|r%zE%ZE>z}+)iVfT8}X#}{ae0}v&(~kH4kHs%B_`a=gMs2 z6Ls(Ql>?xFg<1Xe<4K$n2xP0OcvM#>Nd73H!r{nEwMObK6o`&3b=%#yz;SXTJA*QK z&RGz+C(w`NU+|u<*!Dj-xmd{OpceJ^9zDDdlp77b)q&w9vC1ps^FqNi39XOiUdp93 zM_D6l`H~xVNMuV#yY5S`e4>xHe+=sNDn#|lGDYPT zst!8$NA z5AXp|_ynk0a{)aP>RIeDMPh0SC4lDu7lIi&O6|`e-r3mN-n8oH?D=7886V6t<~`vu zi`ow%7d<{11dfYp&&>kr<(y`4k&8J zu$9?ZHBu=efX}CwUq3y8<4`9LX@5>&|Cq~w?alP zE&cghYO4RqI8h+}eQ+Q46TQc@d!CK|o*F^wJzu^x`WB}(3|9x<2I?(y*k2B+nuAl( z@~DwN^2wRLN2dL;c~>RlFKQ~6oP~UdGEncobsjpO>F);wTpA1vJ^_r?;2>r?W`iTw zoFYl{qWs{4T&}yD6Ut*1stXDuNy%Hp0~vvxNS4C4ffqf!iarS7;yGWrYh)ycba!#8 zN}6QI&A9?%fVU#L`92oP{SZj$XQrJu)Og}*0UdQjkm-M93J}%Pw;(TnNbLYHa3(8n z{G8VBk*rf72FCY-3NqRNcXGJhoAo?Bk%V^nxGZ_i<*oM`qy6#RV$|wXbKCZs%5e2vg(KJ`R+N@trcu z$^ck`@NAHsT08YgyYzDhmUVkmc5Uygu37z4$d|MxB?~Tz4Yjt zIQAXi2kC8Ng_fyq;OJ}Ey~QIh!kNk~E29G=^S`4>{9e0fcOlQaJmyi_0+yX6+uQSl z;psj^D~n`n5>e6jGWe|{n-mJHta~|F$1D5-G!zZ42%PJtKEoG)Y`MAl{DGvciSrqd zoP~v*lCGU;l<}|GHi>qf6?)kp%k&EIEYRtSt^j5-S}a6Om51%~G7($y(7=GsJ9Rne z+_TGm@ypf$(iH*NxZCoZ@VIQ>nWDcdaqMoG)Qv{W`~pxhL2WWozT&7Oc~o0#eB*Kk zC`gSqOR;VAi4s+_m!~|YS#MIviW5d^AK%BBa{eXWx8}9vA^ZTR*0_dV?3&vF+b`3% zdL0NaZUj+$ZlLI_q)JVp(-z!VOm2@>TJo#Rdz6q^YS2=xGm!PJiqWW>nh!GAjuNC7 z+0nJ@fCfcIQC&_*Nc8n`*lSkBCurs7m`jvBea1(h4?vFA(rV~zXA&D&-k;gbPS;l8xi5yOkgtmm}8_$j*}jjb@&tC2Fb zWHF|c${#Av^vAa8MoA%?X(cR9a-6b@SlPnmkO7x&w$@$@#EO)pK8q*g7LtX{zfva7 zcKk0CfpbCN1Il;~@a^ca5@R+<4aQ@`}~K@IEq0aFsN_$=s&)) zvLCJ7_!+>Y#6(VR(3cDFtBZS;g!lZ^Lz?TdWy%(er>@`IxatIRw6iUXilPDTxv)UX z3xSqUpaBnp@fgaFT?H#42&jSDalCNgo&(hcpdPZfH=?!gy21E;{=ij zx=W9}($8@K2G}#@W?cW6DJ3J)D3mE7M4b9rY47qbE-adE&$@rA80Rn z+A2Dg#>nkJX(j34yO?Qc;gFLcHKd@Zz|F&L`)JKYnKhluoRwIt{;=6d%kONXK-tLa zk_(d!{CVipPV|FtIU~<4Y*o+HGtq0$XKm+H^r2P%rRlF5;;%1f#PFmEV&ajonDqL8 z)5UwoNBDS{om03f9A*#Hj)D|nqGP1>!!}M@`@RH?YN+dC`#6w&bo9awQ-Rb77(`%9 zgH{2vfh18&+3FmH_{HX3NI2NjB0QHySEm+jiPLhq>*RFo*8@6j-}(D8Z<(An>3DWt zh?-qGdq9q1{$!7yq9^_A;H((FCIlTG8EHCcehjce_Q1W~-Yoi0E1=%?+sGK)FDNM~ z@v6*I*BApF!Z;5ju(Dn54g~{y%wR<~u%^NHOaU_l?4<8L94{cr$&)ky*w1&s0SXDw z5CJP-V{ZNHa>1#t_TzJ$DI0ARkz>7Q)OCe0Kf?Lo6P6UwSXFVUBz*%%z!ZGU-8iA^_i%rk@y{&qNr4kD!BAF(O1#2t z*QeJXKI-9v6j)o`-*^8y5gKpf=EIbMd4O}Z0kdO$gVo=Hu_-gHz#Hm!Tm}F0?svpu zbOOn7`Y<+bI$t%ABdet(^{mas&Z2-q@;2^29^)yV|2|9n_nbqZVvI@l%}fLS^8_Xe zc0EoZRKK5$iHR(XwKE3a(nxf#wZ||sIu!pKC$q^t4-X&K46&J^-q+0KhW=NskZ{6- zC^{Oiy9=kCmGURPGj^!h*x1>t4DEk~jsrzb0@Jha5w~%0R?*-6pS;Bt#ced;-xk$1 zn0$dCXZt23mAl>D$3gS83)opO?(?>ODH&=#OEbwO9E2eQYclym)9r{5C4HE+Ek#PI zK@DRPa>R;Ppge-AVml^qdb-A!V{CpKhCbLLNJkl7$1eOIuf^1#bEEQ0UdN{Byn@oC z7hni;|Mlc#)z6@`zdWQ9ar0I1{BD`AYM_UPLa&b7Z{oQO>BLD(-SH84sml}@9!f`J z%|U9plt2<2^ba;uHL#bREvmoh78Sm2-}`l(6jsFg+yprot{dP$f%jv)C#((IWROZ` z*UM&Sd?SOM&^?GD;rg`2N+J#ut3Myo6)S1!7$`$;h?n4zc1VVtrKCKGc}$z~HMTTn zT*Y3*vik+1-Hd$ zZi=e$-~peQZ9l7QUsntr&G_g5T-D>$Q;;EdS-9&tU?4ut%lUbN|D&0b9hgiAm@_0nDzhPg5rDEv)~W1bSW5Q@@=lWL;LP7Q0aW*)P%9^ zAGpdY_4}Ouy?sV{zOELblqPS!aj`JYz|^15h0kN>+tYJ`B96Twb<@yE$F1MafnE7} z6?T3Xe*Zv@`FCJrNA$gFGCg7yDf_+Cco-x+CSr4a`|>aZ9f0^;L96I?;B5NojlmXOzWCHRkA?Ac$GQ1d`?v?!ywCpk3jCJ|?5Cv~(1w$YorKqRF)-;qRSG z(cy$Ps#ShfCT$-V!s4W)iDMfoeYL;stVj7Y>ov5!W(>Q3Y-;P>AmV?zK(|GcKGe~f zrqpxNU(=GHIUE^II4F>-USPYCN^M30sYuZNo6C>i)|Jbz7(P0Ri2itsly=pPnal5Z z&c&(iQ;POjX!3H5M^L5T2gDNl{~-HG>eKgtX3wp1R(a0#iaZAc&i`9t7E3! z`tL26d?I7B%e*LaItp$okNvo8!Kl^rkUiR+9)jyX?)?5wid~6Pg<;6)eB~|{rNS?o35h>r7l3!+%EE?c0yJRB;uSNzvW~4 z=|mt3tt-T_nfV8?Lw}z{<<*yP(dVN&+ro-7<5T3JxBrmwxkZtU95E-<7iDQa=dC2U z&%mF!^0g9(-%77_v^;7mo9y~&tgCeqEleye?J`eS$vXa2=@)BisTyXh!*BU!WahhQ z8p^&^i%^9(JHE7hdUtRDUQxwaQ^kqwVLOE_?LZ$bZX~8l#}p#5E01sG^3aCWfbMSP;(fljMnD%yu*q)Q8Kk zT(6t$^VxHN+3ZQGp38&oiza<)v#*4?%q*oj4SFt%u5xlx&FUt-4-FKMBWtcn+<5i% ze9rLR>b_T!ZhoLr@Tg&|DeFP~Jhpz6lBz@_06QpI0Jf**hHagL*1dKxOSZhav@wMP zHb+`HV?nz|u1a#f#kN+O9&U6ZF7T`D(M(jcu&_o)$4xyd7D?OlyGB#k65~u}pK^wLE3ugmB3vR1mKW=R;&k=m z08{jNnURPiElVA)%w+N0u)F+Wijas5ZsW9fb+&^SSj-EiZ5aWshKhbeVAsIjv@tUm zKOuhn@23@8X~DvQXS8!z_7uO6SZSt=_u$SOD|=zTH0A6upZ- z+A9)!pS7?^%vsp{?DIE(txD{p{!PVWjS&x1s;iAG-@%*Sjt@>W&#%~R>WS!WbS!Ja zQF2B&A3Jl(%s7N6`49Jll(92_2lk|!`xo|aLi{2vRZVZbxY#(@ z8oq=A7s%mO8IQq#*r67CvGxV0w!p@`Xe!KBirpDTp2cQGsoGk#YMDIIrB0vD18`k? zbUw34@udL!+ajv^w9`Z{4B2MfYFt!vPxA>X{H4A&Et75?6a-87-&ZPXYO5~o?XwW@xkONAbpHc&%9n%&Yh)G*T20gMtb z!)n2Y1-zf?c;zmbw0Vk_B;-1dfBLHlG1f6p@A};{H1ZL-6#ayJar0?0+}11}fWw`)S?suLetEGIy3c};5T0AK0u zQ-^w8=MKhKV(?)bIwEOE_`POP5$4uO^U)mbA9dDx()pT&3r44!Ws}0)cISkTuJKXF zckdX&3>mOVI6c;1u*8TX+N&drGCx+`~AU;?I* z&Q}h7Oh+{{I9l#>uHvTUVPIMb4+EwEX*!{F=eH^&0?dS&a{gAAIZC<0Jf$cV3(hDw zqIu0%ZnOnKL7$tU=Md?A$FViUT6QDSJ=-IAgajpa3m9inNI3rB2} zGn7p`cSao~MBS#!#?_=W$N251J>&{U>`+c-6DQxooQMHj+Nhx$u;RsLer(SA06^LAV&6ce_qUtHxZ&_{ z5~Qj5v#k)Hl1p5W!Jc=_j2|d~$x(<*A5<~_b0zP8@X1*M ziv`Jpyv~@2X_wiPv)%^O*S&1hZKE$=;=k@Kc}NgRdV0)+E2H`O_`HsaV}Shu^>wGk z6L@-C6MZq4wxAWo&%9VuPi+#|G}8gw6Bdd+G@|oYd#65eWmt6DXw=rol#Z-*yH*8p za6ivvKYORCl8|bYruDAK$~s|OHpA2NM>70xtH4!A{=(a6Ra5qZtOhKW5K+UfbY1v` z0^0b7?Gc9_>hhT>{V##cMspLB&BYh}-7hI;mql!!WV$6JJJltMZYLH5>pgx6KBJE= zR*V6@1k$`{moJkqbthF-D?w*nzA8Ly6&M0RzO|b{aQI)y0M%;oq;5DeEjPkK*6vS` zWG=dN=3kMO@qZ_SK@Ts~h=83O6s__wh1#Az{?hL!Nd%L?F&OrO6~$pZaVuFpOBL?2 zA2Dh-HG|4|3aLxq3XHH6>BbU3v-CTEWE?pxPI%{jg882KaFodneoIt;Hg?=fl#66$r z9(-d&JG*fT+CuB=>*LgTfR7av;!E61NPWKJSeMq{VgO_=BhRyE!0pD8|CAS{W%g|N?k=$2dHd8e z=3ChCrj)%8kdOTX`cu%Lqj62`8R!*@lZ%(ru(n=mt+7$}xDso4US4PSEul<4zhSKj z?5@@?l%Ec}!2L~hS2&!4YYbamIno0}-qYCww9iJ+D+vx@Hp`lKM*IdUR*vPecr)y^ zg1XiGpDKr|^TL+W6=c7d2?rqE1wlidYQgYMz*&ulkEx#sd0D1siBduefjX`xabP|&m>`*O|5zh+eR z`Nbx73`Gul6G4kCkobVZo`6{Fv?~kHlVFm0R@Ced@f%OO)y~Gk6Gwr2r+C2TZnIaC zQW9!P3Tl~pXT$>lWt_Jr_Xe{wSjIXcBmEusV3_LcrgWuzd8OXx5H~F?Ih)bX<@D5x z?q`RgG_0nbmHX^1_CSydp+}UH+&h6DVppyPce)HQD^*LN#Rar~@LE3>a#tx4n+xrjxgKi^LOYXea5CR9HcklX| zfQy+eT_R`SuEwh2gdgwySJevsZ*>udpRyI0Vk0pe%N%XxJE zn84;YjvtQEM%wl^CZLm&jlH<5r_aOr3-Q44i695j3XX&W1WXLnb>4Np=jB4MWbn7- zl&($K_!|f_GkgDEmIsH&{uZZSF@)xuWo4k0O!tX73^(hXp$AiJDkXLI%nPtnW~%pz z?a(gT9@Rp_!o|^{0B-JiPESw6=76cr8{^}LZQ}s;b6l}7Fv^aT)4yl30bL4KuYsl^ z*^@<Z1>f6PkO)8uN}A_}O8tBOvJ3{CAv?Bw#g$>EZhNZB zOh(wBLu-UIB5~i+EI>1KHCaIAK1G6@-80_fJ!iG5s>_v#EAk{w&srhNCGzLF7haa@ z9GXQ!ZrfS;4f0T{S8}=-pG@De(KK|e)PH#aW(HUyfz8PSv`}RvBUcWJq2$0*#iEHF z6^Jl#b7h_s5e5oozr4}vyxGpeQr=bP!ki(X;BW$fxKg>29zEAlb}V+b3+(UXr?_wj zCNg=uKcH;~c(b5~za)&l$};pf?QcJIaJp8*jthKRnA%?3ANTB^r>5%pPd3V1Fo6yZ z;EL!1^#abUZY_BtzlU%z6@6u#tD74Tw_obsiJu3p&4{WhA>l3))8AWLIUsHn28_Fn=?bknWiWB<#XB2Yq%$nd@==y2J zQe4!}UQ|Ey5eQ;bn{Oq1dLu(ZyqR@{og-LmsdR&xS%D)ezBnoPDU;sPT* z4TJe3LgQCpygt--ki(;7tSt1=!|wT}H$G7D|Adb#IXl@qExil635-F1*-Bi$M(q3- zDXSCF!-WQ_TY(LgZrS_Wfbb2%UXRTG_Du}4?m5zU%L;zL%;AT891QfEtCY6KpWfF} z_W^J5`0)`=P{*~6zGFn`NGt_!LYb7)&%hQ=k2$ z8=yLm3fkrzm-{nFh{>%OD!MqB4I2Dybx!S?G`3sNp8n?87Z@y4M!?2ZhR<_0F(yV~bznJ$7o*F(Je+M9 ziR&7Kr(2%>8ke1!nJGVR4NE_demDR+?{`wgmM*1@Ogt91*Q@LoFovblgfTMTgl9S? z3i3+)LBRoE8;6=18(#tgEsg{S=pjpQIza~8=@x6!Ihtv|_j^0k-(ts*6<+WJi11KX z6ln1DrqO~aw0CFnml0f+dAD_QppTCBWaN~_v>xi*)AGgiOK12OXRFCVNjp2FByTmH zXF7y+p~qbQUL^BtX5mo`dHNb^xNVK5-wm?@X9T8In4KN4xdSVtt-U`nF?NHlpGLyZ zxTnh)!)*<$(ObcGvVYgEe7AIf>wSTU!`bZ{3okiuIAK0Up#g!#E8kL`(pn*v#?+zQS8(sK~52RV4*1PhMBj$x;Wen zil@lbS$fv~Ux^_hCmaT%J_i`T z(&nQ9c%>|d*m;=(v$2G(pydQ+I>hj-^X|A;kTR=Y7iW*&j-QUM=IL~Skgsa7r*6@bF5rWh7T$^y9^oO@VyRwW2HOa}vji+Z^{2C>#BH3;$yHee79V~A zXfZWuujZ|e{&#q_Iql{eH+GJ*%d{;Ek`i83E&0ps$X^|wU&0iU{Q?fSj5%>T?wGbZ zday`_tL1KI=nm%XF?s>J-@~tn_%}Z|F}{Q=tW9jVL0+p>#dNJFt|?>0Oi?G$8nYczf%p zs=sgV_k&8Ol7ch>($b9rQqtWa-O{jW5kU};RvMA+?h@$|Hl5Pl-Ou8C&Ux-Vzj4O6 z_lzfo`~h3o@%gMZ*NoSEPx5EJ)YX5NH_*8%PXOQ=#~5RBQS*@!fFd=HLghtG8(p8v z@4NBrNXwd>xo!?Czes#`o=;cNWeI9CbK&xzya)<^J|41l_DnGHd$PzoDZym62~tu-c(1Z zuB{LvBVXilB`hDs$2XkXsLQv8C4K1C;7HJ!@-=d^_&R^^^Js?*z(wQ1TN+GP`>?4n z&}f^Vt7Xfd934O!-=<%_$Wg}bft@TxZ!u08-}{}Q0+1K|ps*keV90$3!@e53nkRzXaG&k*UeE2c!CUQ}gt|Urudwzm^ z>dFn3n69;gg=4`v1#a-iz(+O!FtzC!4X>`M6w6Su7fSP&)>|s2tJFT~oEMN)V14RB zSI9}#N2iz#&mSd*G>-?&mtWAU`1{NOL~74x4TbXKpXi;JjZXc8o$zzsmhpUbU3+6& zKBu0VvXD)1Ndc2|*jLbS-lUWzNA>FgoWn^#7z=IxsbGmcOnrA==d|JAHlCB1i)o@c zt7@RTJ7sVw%7v;eU{340!P4HfYHW|i$7ic3fKkJiZB+u=UmY%96|6QHaC+NC+UQYE z$0sn^eAQiTK47px#lvnl9T}7ZrAg@SE$oU4XTdF526%eG3cI$~69^RbI2tv7mmyj& zWLpiWDx&+#q&7Y#ooFf(J2iM_wx%BU<4&ipt7B@oV@i373BGl5_7d&>W{JaMHXgsm zA)rF=;3q=)(7Qsk7ZQPj{!GBCq%ryA{AJrOv4`?W@=Def#*&yRoOT}z!_+iOnu{YO z->6|i?w)m^^^yqs@hd=u!?9~MD?&?l6<$u;ArUw=rHQ#q&iMC;A>SG-u#k}6dH0j2 z*gR|dU;WHiWpnX)zIAYWz1PhU*H&Xbb#|k?d&)sV%@WVyY}g$0cHFj*Ic(Hj7Yo7& z4h=`cMuKWZb4RP>hy-RtX@bMhNZxOuoOUmx8@1kwA)QuMm#6Spvj@E9Awq$8y{`Md zMFxDr3K)dvl||R zYn#d9i@;2@z{ByCyR+d%1;`MXhgO6p67OFEry3rnm=HLMIskAfFY|1YAwHGQ zcV)fX3Btkq%K`SnlJwde(I)XJ49JNZt6`1%b8ip=WR=#|^4Ie6d0lirF_s3cKfqfz*Y50Y4VTaEPqcOIoer75 zt#cG|Iu3q~85TA2#imie+3aR)5{@g0wZj_Eh()e&dU60k4ti}NYQFu+FO52uOF_)Y z%#usd9QBf}czwJwk&Z6h3pVyU&+4F>@$RdXPTjXQ^myfLd8xk+d{+@PLR`kb3=-Wy zFWAEt;9wYM{4$dz)6tke-wCO#T!cXrPJbW8%6Qw&ISWT5MjKo&U7kQVkBQHx$rJas z5XHeYO_9YjcbmVm6SUTy>1Z@3H{|>oG3UL~Qz^SdxkdLdw4HMu)3$m5+oP^tQ#<%e zFE?YncZJUp%hD$b(e>{A!7+Kc3f zPh?19n6N+PaYQ3CatAMo!w!o{w02Y0Rv&e?w%(SGja4ZDENdXT*+EUxVNb%I`fWOV zVZq`fj_7*nK5v#}hXJBT86Xvrd^;b4KZju~tP+Fm^YQCb*$ABbZ~6>j;FnP-O|m36 zB7eTIGiUZvOs4=A(i^#q)F5~O8Y1}MG(KeV_*`KT{AeHl z+^j3XVl5{2(g1u>ia_I0FwbtW#WduJC_>DAF<^s>*rXp#A)jczNW@YP2 zwAj_xfq^)pcgn6~zq%+CkOe}~Ed!^xXJ<($K9Y$DT`E1!fLJAY=tvkJH?Qddi`Ja* z^Nqd|Ja|h2Zf07V`0WpJMOF@aC?YDdnOFhjE2s3v(f! zo0seA$8`SW6#4AIWd22%ng50gdGkwF>TJ!7p(tO8Pv_y!@S?w~*jQ4!@5N4l5bbn4XjOv2$5Qg64iY^b8@ ziv4-kJ7XvZb|Yj?qsQyQqDHIX+}u=M&v9XRloHLKe2XCQ0bJIb=~-Xc2qJg%OQ&Co z=E#Xtmsw)jEWO(J&iu?qQZ|0oiMqLU?n0fMat>~#d*mdd8A|vJn6Gi1`FEUUh@s;M zOe6NE+2rB_G?6_$4DD11#amVZmx1;CQ}QxIC0AXwNYWr3&#S;AS*acE^^%&g__wAU z&0l6O*tJ`J)#FF4CV%E(ma8F^0*GGni^04))}geYxBupbF2X+NN6!ZP4{cSGB0%Pn ziAn+Q#_a{zoqqlKHN07~p0

F^Oo}A$%o)bQ+4aSf+(3$LtxNVjor2&}1m%>Z9fS zEfpA~s1&zd$1K0WWFFg?n%9e43rre$;mNh?n#!izMizHn*55(Mu6A0bz9jiuO9w5# zJ?u(B_iH7CpdKmir{9{|DEMfjr5qYZp^WAV#1fgx`YAjIXZ^@#3dFZJo4~vAk@6BZ zDZz$JR^yWRQ)O^|AOEIw2^;P*Blf_|XZu^)Vm+8s=;ai3X8a-|z z6akV@*CQN~$A1?V4b6>P*8pG7D8tzP@am&7z2TQ;1rtMERU8Cy`PUcSPU8q^OO+2_ zb>^oj(zhXtzj@@@s~yYbB8L`x=`53=*pkS`J~uR1SM=KP(90@}$QK6>Y$zd5dh-a; z2Mq(deVP|*Ec{^vxU0xez*Qi+`%J9w?vw+C$8w8>xg?44%3)p|`gj@K4llVe=78-3 z0z~I&ZH~OPSkH_O9#PVoE;*aH=t;Qz*jj4KKYKXuw$1Bq)>k6TtxjcJ8K?dxUw32C z0v(zo8&IpnBrXJ7$!KG;eg3xU#7bhW096b$ILd>}`24C6otzVOY8cs&GEen za>yF?GkY7WL{A-$>PJ8kb^O-p{Yl4-f0G?q5!TKpbj;&R^TAv--#zTpqy6^|Y058> z2Ld9pk2>_|d}d2{Cg!Op25+h{uZn)IoJ61^K=y9gK1%+eETz?W;1`-&`G~Cu<)E(~{KAHllGn5KX=xngIsm<~%Ihz@6d1QCnY5PvtBT z`Jyhh>1)>bRy$_Y_H`LV&I30`m&x-IN=Be)>13`hnE}@4!Pf+}S2*Gl@(zjednUe} zxO(6Dp7%35!k^TN)-FaQpM>?xBF(1plxSj zBLc8cPu}mQ6qX%+H-`$fJg(Zn6<|S*1CNilsUXej6DgSMB_$AKTxrNo@Zx1%E3`hOhU$J)hLh5(OU+D z7G;`}%Bq^AxVJds;IbvkukG5nWfMLC7r8DvsH~RO?rRh_M)m2tM_#+RmTHYTav2(M~YBtV`UnJgzm3gUDGcKkB+%itO=i*&m zarV*Y^6_Uog*w{ysaKY9-H2XAy6JV^%nuiTYBWbicLTcjSw{9E?_t zTzsU(jZ11Mk93!;#kuj&YB(8A0{K3hr0d(SFVur$@{ccEOgbVMi#$^%7PeG`yKMd~ zQ!0&T=76c>WpqjDlkYxbt}=x~?W`=SpUh@Ue%2FesEeqU7(6^`f&`$FEI<>^F9N7psj);4N% z((#a?l9s6}Q8)ep6zMnqX9NSHTIk2(vmpq0ZEp>a40Iu7kxW+vPBvB|{?og*u z?#*0@Se!E2A0N!2J9W6gy0hXbmgk%FAMkgF>^d_i4?Ilm-1vbUsGOXjCPlWhSfbU@ zvs08jxa8b`7N{(jWdOCTIV74o1}ZaKs})>&l54FSnm@O++j-{<&e$B4MxZKC8;B2V z>Cgt}TlCfsTEsAlVh8u}Hx zOSxF>HWT(bT!v)%hki4^4$}`Ft1^rZbWm}&a5UEpS75!yd6TI|jU*A>3uyNzXs^V< z0rByXdzsdkggV-=s{SG1di@2je3&b+P^M*7sh(kLT>5P&-KRolEe`1z)s+1yI7#hI z4E2=|Aw~KaN{7dIXzgvIt-bxFOLDHUT$ogpgX8c(i44q5!qwe(gDWM@utw3`EJgZ* zp;peR9gQT3jHl#l1aX~`$W9YX+=-JS+%(neyS<*IF8tVfJl)Jp;4tr7bC|GM9dQWw z;594Icq(5^RbA3xZ$c3uUtRhnaxacHS2mkRJm^A+9?yU^{sNBcNAXSeQ$DU;>HXe9 z#x_04$%tMniXa_33tP*6it*`4qpT-?lfN)7RtJEc&F8hbstoPNRRsck30|g}Udt7~ zTXnJCPBDpiQ!;0#HNX?(hR0r!S67e(LTtv@IF1}Bklk|pu0Y(o0(D(=ju%EP;Q`&9 zQN^_3>t|!XHmu~Phu|8UB#F~5nu^-^1j}++kC7gh?zC+De(aEe)G3vqQ@txvIKH(- zlBqH5l$yvD*Z&S2%oCHEa~lloj&HBMXW|-o3#F%E%+$Uke~AurR6JtOGi#TgVqBi| zeVF;>=-3u6lg{MfP*){ay|#X#UK`c>QXqLY3vN4`$_bX`3uEMvt>>N*nH62jPx;!5 zS-&w^tLe)3TE}%uVuthitZKiOUipEj0?vRfGYr0Vb%I~Z zGY&8p(S`Bq5h-lI3#9GalPB@Waf?wKeU1^nfeYG8EGOT5yH6Iu6yKk$`v{7d>*^%h zP5Z;GDo-gf7q(WE^=Mxe>m^3jRCHEw?5q${a@o0 zB=|oLKPAUEGVmJzEzkcyLY@DePk~J`tUlsbXJW$7UD)`k`?Pk}x+1tEpkpM-W z0+WN^Z@G|(Pt0-%iy0&3`uBe@DzXUzL z--YMq#sG@&YI@9!Y3=<)dG_;9h*zcG`apAdn9dnjb;W~MihhF?pHW%lvA~(d(`>O- zBxX_zBti;umq1Nl&FW-yqyUsqL2581Rm9s+w{O!$2c#}BkQVm}JvHvmbH%3)LAI>p z%QH^v%^FY~($)$Q4Y>Y&5+-m)+B?9U#NmBi^7;n)n~7M?B>rLfNaG7I4sB+G;97G| zL<}yetA_%4tvC(E2hj2>lIq=16!ha0NBbzwyG9q*3%vV47gkr>VZsiC0FPnb!;^2- zM&BW%t^vVTZKK^j)JLzS5_N!nO+1U1RK^_HLA$wm)Z}nU>kUC)@>Dy0sZu(t2+wqn zXp9530>xjUtC5(A97VLivgJ{8u@BcWy$nw1A>NI%gw5=!GMRRB4zH$W9zec!Gg|ZX zFLxv<{@(00j#ITg09}XeE|@PPDer*3F;=2&!G6z?LSX^+^5_$xo3Y6a59iyn@EtIjwz)wN~9H-X3?V`|BtO#1|F&THkV24N>-?)SyI zPc_1k;9Vj^pzTW7Yt2-IJc6V)(AKu@+a`C1PEK8LAQlfib4wT=*^?F%_CquoAP;9{hBc!t%U&0$bKdTjFMM%G z=b)ES8T&>vxcML#2qIkX_tp)@-^k#Ir>?V+#XDsyb%YT=fEYFIZnmcSfv~EYbp^Ok zUSvGgY3_HCKlgktkK*Tfy0upXSvseBsEX6X3&$S3Qg`)N&Sl>*ANg~u%d+-srQ-hg zwpOfJ^yCQ9&c%>VxYPkoF}?a|o1f`7gx)8qAkT=s6Y$n6F%woL40#vsV;ADlT67AXOr5l$Z-?#iFsl=Jf$R91FG$a3pG+Sz_C^&c!is*d(bhJlz?Y`@>wVcuMH}F7`fD`0n~*kw ze8VQnb2dFckvjU0ay&mrHKFu@hgII}D+wC{__5ZK8}ipk1BF3h--CowN3jgmjG?AG zUyDvnYPze zZpkaCqyh&Noj08M9rV`D=5QY=0X-kXKfoR#0DR1M^zhEp=oUl79vf^kP#xQ2_AEaN z;b#$6%H7e3>KX$2T3sR;R38x1^0KND#~F+4ANd8BP(X&|-&*~7n%mRrs<5DM7%(dd zc`FI5WcjnB0~5A&Abm|8)B)sYeNNgo+xNU*6j|e;xEBqC-{*)?(Do4^gw!*5HM#HA zMg1(_B`)6Iw?zj)YWcw{)03Sp0eu^_Fd_(8=BZG=PV; zGV(pt>PTQ*mXy!IY@xiIpqcYd!p=zQD{FjjZB)YK@Gu$#l-EJ7A51+6kV%K2WQm86 zrBf&{f8Y?V-sQKM8tjI@pbq+dwnx~%c0}Y11N*Z;EyelNk*D}%p3(ieo2CKfWz1-2QNje3E zG;Nc@p~tQ4-@Q)i3PBI1DgLM@R>~iXiWD-IPWfbNh2i7Rs1N<9=>~xnqi*|f?p0(d z!pwe=J%1e)8N0T!u(`ebUg)O7yw}Ip&8r^W@_Q{fj`NEOkB$y-+nLcQYTN0A#3AI5ft?34UV}l##)p z3EF6fzf!{k-ko*0EudU9%H<-QV%MIDKYx8La6yVd$dbK^A5&Ay8yOV#1?9u}Li|bg zpx){MuMy=fDAx9>7ceuiY^}CaflGF$bzi0k8sWNVlm!35rT3C5a^CZJoAGUTx$t^X z*PhUn@A*j4O((ev6EL}daIt)&2<~0$u0FAb`1*n$2@;t-_&wnYrfUT3Pmgz#s$T#6 zBw?xU+H{V^bA9iOuOq;M6Yr}`;Kd|rYCnMmPHFPL!O322z0Q&Hi_b`m4vP)Gdj%6) zcc8G0)EMDhjU5r*d<9hu~puQvB42*VSjLoo1{NglY+X6FWTJ1Kb4pN#sxYu^l>T_0Hs<< zOp?DVyHi!2^-vkOte zUI-=dzcd0Gq{tB|!tTqQ$@0M2yj-8v;IW-`+a9S6die~L$2^CeV2ul@q{uS8jI(;l zYXb>DtjKj&;F9f=C(+`iIQ!Et3e6XpdbNsO2B#YWv7?Lq0$7|py$^uvA^)^@rRlkFO{uWA0s`4 z@Z#0!WAZ%8X-WALX0kkg$KNkjp{FovA>w#88c^E*MiM!YrAsRG?$7$;0B+mqv!VK8 zli99_Et30?L1Vtgc6_0t?-`UfWC5q8{e2epWZ$C}vEBM^YHVh>Ck@V1aN&Bvy0zB- zo#qKj4`aSY@wBpv^=W82efe4s5>@= z4MMY9B#C3Ul|n6p)*~givb6U=5x8$iQ2JL9`>~&2DMZi$qa0tjz^lBG0{q+G72WM5 zVhi6a0RLaFHf6{z)BN|J^Px6mEp3#yjCr@W=Js#Y`hS$_6Xz1zyh)Vey{u`x!$vfUhMUl`F_O zxVRSN(x3pp zccQ&J<=W|T`Q?jvWs!5|i_H8D`^Ki0_HG2oOyMmCHs-a<&T2%n<`9EML-{!(ZS?E0 zR4$hp;MRH_Uqpav`RT}S*W}PGVsIJfy-vOd4i0SMl6I^(a%$V!`IPDx?@`HP^O*C8 zot{e0C&|_F`wlEyMoakK!O5EoZ?X++#Kqllw9FOnCcsB(z8T^PlN*kh_ii7WJB?=5 zNResc_0*<{=uKCo2Z0O~0uZln_NhL^Hn2ataH1u-O6Bo93>T7=r^)<=HPviCvYq?6 z{*?S?xi?mAbTMMyGq%@gPrwT(UMz>k!)2YQp_1zIbfw(qnXy@cR+X#n!ya1sNivyD z%_iyc*>~HC24l8UTqcI|(x0gzzupzE;{ir+Dge<8_Km*w+H%?`OlIbf+&oXOxHQ>1-y`6rzIOv)gB*HWYsf%*96h@XF?uDfJ)NVRyS2Etjt8z z$_8Rv8{3(mB&3JGzXf5`T2n^HWgNkeSHAW-B>AJhcDY5lj0`UfsvXvXDG#1uCE9pi z&SZj(7kJ^L^()vP%=V~tf8JPEW!EO+x$M~PSMyC^@aOZ_IAvo4zWl_fZvl=ihRurt!uS%Z#GrN`uj;9|?JidSFgCK8n!d=hwxA+kBb;ptWoB*WixAaPhS^5(0}DC{P7qFwi(_yV<|8Q`leH|bb< ztQ1m?bntgLs4>JAsLjz>au@zxHtbv}880~havcpq3wpdrMG9Lx1i!1l{`JHn2>8peF|IzzcT;^NJomsvtG=xLHihLVf&ZOd-GGnkk@0=Dd-pLRxVLc(_g z)x#sVR6sTTG*2M)|1q>%K#B3`y(h!-6@_70pro@%(<5| z4tft?W%Z?z2)V+N3PO`UaNI4+WpkCUF6Nl3 z%kXaIhuJnS zz21#8D~^G>MLGE6K-N&19a2WR6BS_UcHx|mB9n=>~d_o4`%_HPX77tthnkLHPG|eJKt4dn{ z5>?}%zRUPk$Gh%C4&=-K3+~Bzc6lP#31sL#hoO~MmwUj*#MAwJZ--A$J+-_*W2iI9)xz(``sb&EPZ-+*d=4fK((M5W_wLmCe13crfg>2;68PK{ zwGHXZvte?2MJ=`Z?aq0Q@}n^PQ&N8XXMY7uMMa~T$C9I_>4#9 zuEvdlQ8jj_$G?HAfr{wm;JpM=qtQIeBp9EHeKl{kRa8W-3`<3GWrW=VuaOWkWp3Kt znuU%=Mv8^Cl(qEgVY6{S5*rP|lNrtK_3=2(eE~8GS+cZu)<<@5A%XkLAriW{z3Vw2 zW*!zgU2|qL7;p}pvm)JI!4&7FH9Z&dfB2(wwU8WvloU>jt@O};_ey`B`8e%30M>}y z1qEz$9N4pyj>(LA24>Ktw;dV@{2|~e&l{J*Dm<=kB}pIL|NKWTqJ8rpj^@wO!~Zi0 z`1GRn3gYxv`#Bk}P$)<80lu>y?yN2M$`A^>)x37(xc9d2S)OESrwnj>sWNzvlv03n zoEoSDjG(gfSlQU*<02y>Z#iE|lHO-c^aFz!aMdK^MGSm20h%o?he6+}75)Tcu8bC*^3I&I??2c&3;%>+Lz5GHBc>d0EvJ0|JT@xiVyY0?|fk zdYnJ&&bg{q*47#DjZEd-fJULPA5KmshX)6O?uS2g(|tx>f{1TZNJuV13Pdg-5>cn} z8LS7?5amB{(_$UC1UJb4OZe2Ej|5|9ONLIC;%9UbUkT|oXs_6Fp?^W4g4zVuZ0_wX@>7yKH7&W=ECo-QZ2dh zp}BgiXeRZ;n8pdFI~yO<{ur9_S;oM-ZZi{ftKvQ$!fb zy5_}5t?GB(A(x=pT)2|4O7DV7{BGr_*e|n}$)q-9E_lhnxj##=pPbeVdDH-y58pqL ziYKtPhuixpbKD(->!q-1u)<(7GygRkC3YpL-`mH^P)NqpV31ZU0O|I233yY^R;amc z20v=}jCw=5U0}7DplTKNvv#ycnb+05xiS&^&?Bik1DRJ~DsLoV$-HQVAP&^@=Ck04 zmo*$j6~Gw_pkuX~cJ3&;{Le_!_=YojD%ss^p6#R8Y)_A?^%H8}?Z_hq2pgF+WHh?y z1uQRLj1W5j9E^mcLcuShUN@bI(|~fRL`$UuW7%O+r2}im`&{>H>J_*H&$#m&WW4s5 z-(<=dYuC=M%MHh|tR5}#ta_I^QoqK5_7{HC8r(EIh4=@e>O*h7rF*;U%6quXrboxC zTFK4lyh4buHn$yOicW1znzZwg4YD9Ctdipa)mwyA0ngzQX3yv^(en-=kNy?s>TYQ= z`p?vu%CMO{k+!BbDIsZ$7p2hE7>tDP`|VtjGjZY1AFgMSPJl5vK~yO_>iyAnje*n_ zd)(j@&w=Q*+hCdT8}o?)wt20x%XVMouc$m%`@~;S44yjq2h@uN%z>ke9XE6=UE265 zMd7Go@0gfBq6KbFe9s3mBH->Wo;*9W6-!tA^cxKNx`JMr*>&e0IV z5`^+ma04-t`O~$-!e1j9D|-p67ZlKzsS$cq_ zyRFD|MFSC5LI^M?-79(a8nyya&ZbinuLW7^_0~9vT~2?ezZkc>-oJ8{F1MN4_kq|b zDQ}gncFPRzq?^78J1SOepqtnb5v;H5t(2L;TSrOGJnQ%*KFwS|@z9V2X@%VxH$l_m zufm+fJNgD-7mMCmVLunjBj5=pp#drKw4WjE?}&S2888$3E6S^EEY-Y@dPS1Spv@mx zLH&-dCDJm|@GZ{c=AHC2iS+mowLEXITR|UzNuqXoC#9e0@*w^Cv(lXt2 z-Gk17%MLTz0J}dVnoLL#K)Ehp{10FGMd;WW)n-Z=TH8ACF~?H`UoZA>ClAa{viunr z0x*$wMr>6Dup-THj7RSED+ICQ!6d*0ZJ=9biOpLcdk#CqJ@A^b`h72b^XRp%O#i`& zGe9jNMSVRDeKoi2iw*gTtSPz3#zK5V3a)%Vir_Cvi`>a`-M8VAlk8412|CMJ&uib{ z``U_Rsak5vX31ys47AlUeqY8gknhDjJc6JP6!HdA97GuHD_o{(?+g^;U6bp1rx->P z00)sQ?|Uf<16UVSFb$&-st@j)(3GWhrtKPB`c-KYsGO;4$)el%c-ie}3W!5sApK~_ zlP?@%dx~uui4VL9$Fc){|*3XT1)<8{4V8Q!T_m(#Z9 zuq+cR?{VX>(HnXght zgb<&r83&!nHGSLN+VEQ3=CXcE87AXxBu_O7s5%f&2}e*jo;;nOp>iv!+;m1d7nBd( zUiBveh!9STQ8_SZgc)wQfx{O}sEE4A0$TvppveasNMFe}*=x%}FE*&A^CfNS+ zb3n6!<97vL4f2!fqD#d`y>JYi(&YPB7VB|to-GcVdpd$XSkSei?Pf1c3jt}rN1ez! zp(UN5vx1fx_*K$OFZ?ulO~NDxxEY5pgZh6c(p!a9Z4K1}i~;3C3C>}6SW0;LPsN<0 zkRI%$8WafNK9jBLMT-92)NR->1D)pb9!zu;4v7R zwHcLDiFpAPsSeD&YQVTiNQWx6cp44V$QO4VKl$jDqMP1;Z<{@O?+4KxZ=R6_o_orY z3B77r>xl&kIm15!e8WR;NMp4Dzs5$x@4 z=myxfHGN_7XQS;>av6VC_)DK@`G{4a#B|5ImlVJF7JC{&^R`eFDDh8Fy&E*=Yo3H*;Q%N=1>ptBv}afb;#rWov}xECMesvHT`~ zPC1Y z$i@;9I%30c$kfjmPUDp?Gf#ZUtDm#VTSryt_jZwU@zMgcwK(TTS_K0@3a`J=puD^C zUp}B-lE4U#>yDj1qqClN1{5uD!`=+aDbbG-7O&L^Oec2yp|D(aoZ#rdp7f!(_|yFs z{se$=eIc7LN2P4ApubfccWVUwe)EO=se$?nyI4kngD;9$)m&>wy36DWwW~sDXK_$lXgRvM`yEEoz5(h|up#GmhwK!uvB|I}YnJY3e>HX->iNu?d4#oF(zE*~aNn1F zW0wK9d}-vv7ib#6|Fx4){yy@$`*3xq6xo@on`>obRSSQy6i(1b{5C^5BDK-1fe)(^ zj;=yuYiVm?Ge%-JKWxE9l<<=od)dtp#AoTd?omAB|3s#{5ABM;tNbre-6+0uBuCr#_yQ-NeC8|BtPd2*GHKYjojZzbYJ|*79hMsW? z@U~bYIRlRaM}ecsWB97@t1;C>uWP@wBCXayWi>dcu?1BzLQ&uLdN2#z1L? zLP^L2Xt%sp(gI(WTKI|mA$ufzt|NbUd9Vo&o3NbER!Wzc5JiIA4>l@-2pK@yVFdR$ z#qIHbL8|S2*3P&&yiya|TZ_OHfX#K|7SvQMUZsvlMr6y$5{Z~;rlo?pTSNV;nKCY) zWJ`bd)~aP2sHcFZz_2VylJU8-%o~A zE~gAc82B2RzJP7?-+7mqq-O*$Lj~un0e9XfxRa=gaXj%p zTr5UiAW<*dHeKVg*S#F7wD3Cmi6sBjv=!&n^^ako@0>r9klJ)Z1Z~?Eju%{eEi6We z+`TVN%!dn*`|8a989Dg_YQMA?;x?)|^2Gwq+kKzmFLqWxN@U1^{H48YjTB8mUeRpR znv{>x3&%uT%6bli)ePs1^jIB$>cwHG z48kk=zaUd4{AVzH1h53}#l(w>kO9pj7qM^L2%{Dzi2 zc`mYDkTtTlObZVx^sm_)y5IjndD)59vb5BKG_Q)s>5>~&)tCwPJ%D()9?xXMl3ztF zB6K@bQ{DtxW95^-8rqwKBnM!(48DmhsW0SA!k=6S>T9bPWd^>V+m>E>Vz;;Y5$Q@H z`CH49qnto-PJuMP=L=9$2-O(weiEp>V`zUg73xH~Z389(5GT+8#_GsSVc4o-*-F`U0f3BZK4hnv$#S((cZG*c+o}D#uD8Z(jp!r$D>fe8QnRUtv@Jt zWE;)6d;*}u)}jdzgIii!xxGzfozm1%rjL1EEKKrbSwma>yN=>E7-@MPd$CNJ=Qi&y zT%;gwt0$&rt0!yHMN&MFtw`Td1W>fK!S5pzGo)8)~sv-MRFeQs2mY<8Gvwf11n}M@9?Pn;h2jX+9N&^w%~MmLQIMTY6lixw4rtwP)nUE3g4NaYfo|`xe!gd?e_ngX zf;^_dUBQ^e{r=>$d8HZbt5#WKOyrhq6sUYMM92u@F|e92Ln27Id3U=GI6zHx*le=M zyK~)n3hYDBOHf$I1U`{&GAR&FYSz0zhP?Jq-vS(FApDdk`|WQ7K;Z%iaHY8*zOrYJ z-%b*cJAkX@xb@iVYtNt?m{Y;7b$kwWIbU88{nKH?1+rva$(0oax{y?F*LOBEO=t1_ zEZeJY&(^w#KG3RPDVknUfitw>3@R!7$@*&4MfMgC{D>XALDD|IJR_{~`6Q?qH<#2D zz~_L0#UsI=qK_=nu8yS${_ukxO>yx`BAl4JWwM(Lk{ws&cPy#ifvqPsP&qy{PHkY&E zJRsKU_yzXeb;}s(Bn6hmok*Tbvrh*KO zsS|*&NF#qR@tKzJ!ToNelaAmXC;ETrv9?Xqdsy?HvGVY!A>MMP11bwk;|N6Xyu^Ol zGA1&BZmDkef7`2gw79p$=Y2>7+6#O467kTUgWiQp7H?}KaSKBUb^T87itt_j3V${- z#$6h8n*7J4;bimHL_0Hmyr`o8FlRrM+K^Po#|q~g>g%e)3tnX?wVKchcvGgY}@4b5(-e3_wC-+L$P<&@<`IwCOeX)lB{T8`} z#gGLeBA00Osn>pJn6FREDM(j(00EE;N`r4eeuDAF*2bokPb$b0r_@$yitI-=j#&n< z0D}tsU0Ag-wM-wfAh9FW*o+C3+TTQ10AOIhz-jbaJpzRwrynR^3Ol$Cw&H%mqWh3b z`lRNHWGbhqDUt%^Ug0Se6;h$2EzrL84M0BL;Qu0kdVFTaP6VO@f=3X(TBXk%nIOxW zgk39WtT$W32UF3bxUAbT-&+@=KyfTuHC7jQk=cI{C@!vJ)qHIzQK%e_75)9j?eIW} z2Rgs<(9+U|py<}%&lm_0Tuw~IMBa?j<}Tr$>{0L|vn`4gum;6YKCkQ5^rZhpk(uIi zy^Kr;L+QbY2+*hgQV#iNKQo}F;dEhhbzA$%{NolCplAB+GofWrx9RC;MMXVC^t+La zs=J-)2IlsSsJ~31tE;m+sPjNw-HcRwSS0am6N8;XG+pG-x-&5ZhR26VSeu%@G`-H5 zMGilEZ$SROC;;Mb6$Kv6X{ zYM^OBU8L}P`FA9sNUiy0e2gr8lyaLR*2)y-3C4M}bwb<)?VR3jeUHgcmUzQr`jQC4 z7&Td5r_Ln(K*7?f7hu`-9D4V4jiuFpa55N}>;(aNIudkH8dbBZOfIaONJiI;^6;K;y`2#n}f6x2gThTQYhY!Ptd+|V+%iiXP_O$!|jSkJ%!km9)FIPU5g zHPt;ncMz9>Q~Q~K3kzdC7*+QBBQVhRad$o|u&mJXfY9)+slrifsqx-(79eIl7f)Tq2=D;DCMl7I-y&sqqJ?@!2|O z+35Eke!;(o2;c$&D(dmIwD!G2{{PvLahuwBg%yz}Ljv8(4~`tqdG4W7f`807Ph{4E z(`pEqCdL)?W`U;Ls2;wq00YAZzhiw-ddjb_V0)%f(h9U3-{*GEIs+%A5g#yaHZ=#C zpA9gtHJr@|a1^g43B0y=^zW@A5-tDM3;y4EAZ@6Xmc5mD)(P>nWDho|-0bCrM1rnfH(0vlG5Ais9^9 z&buPT618Bkc!0iD&nNtxr6jYlIww!7NOeB{8*rwp-xjH&*Izz$*8bM46J2uV6BK}x z5K=D_0p5VW-;caiPXL>t++ZDJ%Ip;OU+9+mly3WUe!!_Sb_4Td`n$85Dv$I7K`%!$ z-AQ3GDDs>MfIfWH88w)R=|J0s1PUc*(Hz1H;gS!3?D2&EjW*+a@b9fP)c@WD!}*If zlB^JPsdS47vbhR@j0SK`Oc_qd9*Jcr-yL_-b+XK7t#^Gb+`HM>Z98uV%`3(v#5Wre z3Ip=t&F~*U8;d%kvoS-A3`SwS6wtd@@uQye!gS@JroQRok8TTcbO$Or>iM#P{hSZ} z{X;{oc0USLEa<^fX=XlK)hN$4o6T#-K)RVGy@DthsNDO@zQo@iN7OUH=g0Gl(oMGy z@!ad`1xu{JWcyIvBHq+Q-}7_64-^tRVwuVsrkS1Mf!Yb^#5x}ZT*9X<rNyBF4Tllmiyv*DKWKBs4L zgLfAz=68xpT7=|;fI)-9r<=|1K1Ur|L12=*=lhcxwNf{T&v{_nG zT0C~WF4_88lHv4&vCHt!co7(5ftCqh$LBhk-xH*WW`tVCIyM(vdcllVaJmD`V4(^|pM#fGJ35 zo6nJodiN^hs&e)+Jw5+ok=bi)V`uVgDR0T^v{1F0oVnqwyi%~cLHlBXod_hhKy$uX z+>Sr0XU;aGfWE6xD|UT-=N<$;-p$Go1yDD%rt6llV{FDd26lc1n#maIxLy~MjgE3# zd0q+IN_5iq35Y>g$JesA@MAN;f_rQh9fvYSwRLsg|6x%QY=$T4#)=#Bi{s_T+@2f> zji55CjJE^_sQTJ-tW_rM^DdzqY3NmJd&nkm%H-w`DZl-@etUT+ba)V;J#qCTt&S0H z0u~7%n*yGDLZ(D<+!mSur@Oh@X>U5|Wv(CRq~pHlZ4fZHL8LM3Y_vJ@HLOQApL)`? z=it{~{~y`P?Yn9F-QXZP*(0Ju9|E<6jcpm8!!h)ytM5klll9uRmwU-^0NIs?0A9B= z6}ptf54oHXtNKz0M4veOkWt?U>6oRhr;EQw8M?;2mWJbZ&9|`~8umLC^s^?aRIHmP z@NO08snzw2i3ouFrkUn4JoyB6S=j>m+D;ZMn>92LH+qpjw2q>nkCDlexcM9taw ztF@#I8z)-H$=*@tFQfCSPBM5s)IT=m2wvr)X#eg#$i2umaMK_H01vE4b9nr4sIOh{ z#uhbD;_=aGMS1bXLVWVOM1jB_i`ktqZfeRe7Hmw)z&{_BouG+%a2m@;zrS@}D<{11 z_!VG}~L9WUQWGGRF&z`}peW$r8s5dw6duN<05j}dz z8@T5F$jLNG;`9Sw1HWLSzIaZ(u!*6SSBbCf_BYDqI7qx}i*_97| znSdV*^g4rsqx<8C8sHKQrVMbd0@C0UvQuzDjdZGPueu*dqJLR8c>MT)9dECG(jlcZ z?^LRH`Jl79g9vD@J!T~jQM<*seA6y3~cGA-2nBzHk?Dxnj5^_?}4VR_f z?f&)GB0gqKCo+q*cSdVn*PDd5?57nK1+=gK%4R6*RB7%t4PmJ$B2<(Sc$&;aVZUw~ zcR+>{J5RJYBVw4-!SU46W%u}ZSSN@r*=&8Tp4bcQm72LOZM!CydY9PkmlkC*UlX6? z<{!mAEWX4{?u17O$&~@s%_f2&ls;njYSRvOGHQehG0B`TT97Q2C}%x$m{|+4wlC5s z))!V@;Xj}Z?@-LPylSP6JVp+)Pvq=7_SzG?w!!1 zp!@&=h`s#G@z~l&Clw=Zm8>^Hg_16ajzo=bFBfx0M!5?$t3Pc$vlElSbOem&m(dTu z8k<|%6;%?PJ_52TIogP}6)xM=8>C|A_Zy$AHn;R=K_UlYv*Wntgp?BRSMOe`iXzQJP${sAFIy-jI&ON)kN%l&`^XL?@wQUZ~ zWqGs*C3S-{Czu!nxKE2#|LlY7&@MQ^^(auR4*mu?+#_%(Ns;atA#`QV>(;U6I4D8! zSs!`B-ZfI6!%adCsLi@_w`25x7jF_lQhWr{w+`1x5*t$W+fbq8v=t0vi zI51_tb4>l%+lPAk?|JJmrT!^fb|JKpgSI1U<{Y>qjd z4F||tN&N_5lMV(t1<5Y6HYU^uXRpU5S2SzY)#XabRFpq(-K>vjGc{Rc6I?WB&QPG5KJ9Q3Y3SdNmJJ6rUSr*L|Jc=6!?xtuo8a z!s8jFs-Xrx!O7tisE1q5jOijJRs_Jk=Anr?I>EwcHdAr3TKTHRK^XXJs<~%bCjLi@ z;VgbySqTTX$3}nOWExx)4U(d2?rP>-{ahbdo>Bbok}%zedin5c5zA_Lsm~Yk*Tfb3 zm$WY#SUj{#>JlBm8-mnUqrTOg;kYB04oTeJ;_~80=qIPg`z(SOk%G(wcW!q9UAZ02 z($T=}&LXMM2pk(3CxrRnRnLfFFAc0Qs|yYo~3W0ZT4Oai>>(@wWq$3CcOHkBEH7R<`@iOlGCC<=GQ zo;MI!4oFGbIN4DlK#pp6a=5;gQ5}tkqiWG@Yyv-ECM4CJt$kMlE_#4%9&DxA{HgyE zwMB0JmVgDAwrgVY{Xqn@FJ|^8Bw^#VuFjrazZDF=^gE^}*|yM26N%RE7I3q}w$se& zTh8p0{2w~2^)XONJMlCaPtj+~=P)m8c{yuE0kv#iz+$HO=;$zac|bJ~-Ww}UTyRPf zZ&@f9w)7;h0*9jUqmn4)VLF^nR2Ak5;ONI`=bF-RI)?d5Z&3Saq`dvoXD zLg+EQ6gOi}0?`xPwI$=zv*tV{ItLrINRxlpqnhM8@m2#RS)q!Z+a#~8?NdRJ+Y{RL zJlXRsnmP%LD@-yWJmR_davFglO^9k0)}G4_d4L8i=*M1gQjoe#g3=V>-w8U| z;|L*DP2~*4X2VpyTaW)Qc+O97>}rWcuNNeR`lp8~mk)PGVVY&OCKowdlF~gQCAT@% z75hULe9?y89@fVTm~y9=8Vy{%F$9Rns|8TNRS}A2|3 zmj{oMW^PU^%{Mo89(enR_sQoBi;orpL%$IRX)=phObBdX$sTp#46xdg6Gg|J6=@<6 zaZ>*K0G90401D{C)$aS=zX$2yBKs@10_JpnR7Frj z{V6Svj_DNr*>nN2nTo#P>C#(!UfA#Io6AI0=uO(d%{H*hsI?rgv8pEz?X1`xQ$h6C znh)?V8jNfn?^=zYti8>faar^YJJ=}YiMBA$r!q~bZX2~(x3+3;j^!8kIYKV<>Em1XSA=OVfM?F zpD&LP9silTB@)loX-gaDpze4Mi>b)cl$@rLb}||(=DN!v$hdU%oTUd!IK)_iWG1 zgl5LT-^dL&K+r|I78_SXf9bkJI1XyQw8mkth<&NuH{u8*6Gr3ui1dISj z((3RJcanyV++@DINhbb|w9P-fTWMIL{*|!j&d=f(<$+DKILBu`u_Rj8w!XGoR4UXp zekwi8GzF{$YSrW&qZ1-D8$U7jJ_R@mUW;(l!vM1* z*XyNYCasV%HgODTb|k-U#l( zF6II*e2miy(1v6FF1SqLXW*YePiaD190+@vD1!wZD(DsBD$RPj3jZE3uDm*_qlk2X zaZHdL`McEcg`=<924s}*ory6QWH;#ioD)C{(@nUL6W!s-A%&MEzU=Xnyvt5uDG4>2 z2~KrS@7f);u>+EL-Jm)}zJ*y^flYVgn7bjCCStB#klR?%flo*2ahY8ra&2^dr_GVs z#4WdNjr6;?GFUeq&Cj-lwGscHol9^OtdZsGdyTi;s%P!8Wj`(3R^h&o_wP#h`dp0y zy%1Z=X?I4`_o$!`C#mwZ_mS=kOTqjnnX|m!*e+<^ywdh<5Vf2WGwEM-Vk>`C{%`HN?WOtviMUxb59-vzm-~ z*%m0?VD#Nq!ub9M$Aio1b~?+KC5o~KC+0H?c#*U9PUcIol&ng7Sp+^fUTGHad+fVv zDY&gn-G|BGtu6L`>L=8A*IrU+T2^huWvDb#F6MU*?54~z%Ha=|+nxgZDi_U@aYuVR?#T*Zs=BSXAbbDanu@WIgKv(DXW22IKgbrD~};lN*LW|YoOrnWre&G59> z($@2#nYeEFz9GX({bKaa5k(UDkKBt<(Zi=_{JkU@;$p6K^<~wxyxqYfso3}DcRn|e zI}k=UTRuA`?(0>_9o8$n2Z2va@AF?Q>d7UVFdiNlhb0Dzwk0?N8vYmI^%8U{g#!p(sQMIb%)s{MdzaLr-HQ@f){s6`mFJsI zW@f7d9pMm@Uxt{!T&+wztJIjF%1c?Q4-1E|ug;=LcxELCA3@}6SjqlOH{^S0t2KV- z`K~p(h)w8E@u{Pyp+HxYZqdc(!$z+V%5YU&Ou@XF1s>GDytxjKe&*_+LByzJ$;(zn z+jS8hFzL(L&;V0f#oWoOtnJTik9nn6GstR~ZeQ*Fu+}*3HgPZ6B3lZ&fPB! zPTEuRbw}kTFtaZr4w94lWHtDZYd|o%>`goiejBbC|1#ju$*Q2D^3lVT5d+iQC)>B3 z3T8nG@DgjssZ}9jPDhJ^zwhO;6>qc)07fn^9P8cwc>feXtP?Y_^O@xL_G^4r#`ZP| zmW_J0Ha!E{_sIetlf59jx*}@js$~qDFpy3q*ABf?;pxklJ|0j?ZRYvZ^5?JQQhCm{ z%?tihD^L`^tvLP;LDKs?1V>8r!9g z&K9sF)5}v04ILa}4#GGAEK{&GJmY!bl+5<>+YY=Y&g~s`OYv6T&mj>Q!kJ~q@gNeoS)LAV)|OL&Od)Aa&$70K1!UsNjJ zQJi8W&piGLxaHH_D=XfYQ3lS;+5Le(YVdioGTyZQ*GLO^*UqR zNQnbb#T z>j5%&i?^;Ab#~L_at3s7ppUBBs1SkOLLrWF-R-!nh+JQH70VC=prO^Qwj2AkyZcgH zS^n!W77O0q$}7=Z@X%%Q?)nDNk6=u~N&~>mrZd&JIc}Y4!yWMdr4ON<99r8Joo`(> zYt)Zh>Gq=UIRf)tGhATepzOIfGSm2Cy=is*_5BDKQs~|8tUQna9eI$6bVf0h6Uf=U2^sDX>SOjvrhYwXGgsw~r z<#S5PD_9sqBr@{VIr7SK*RT5=b(r(C>S!W)sl3?yAD&LSuuR4fA$j}m9?djSWD@*R zrXtO+{se6e0a?;$3S+^0jFdtk$M4esEMCDhiY+yjh72!%`mm z*2jJt#5z6X)WdhsTWx05vtE0YCVj>arF=B`g5@!6cfD6GMD3(APIT5Ujzc&t$Rs&O zpmudL7-z*@!|WWl%60?vqnfMUzZ;CWcCG_f7oy8;Z3b%4Il}~N)-WLJ#8Z5QPZ~!% zO%r6=wmN6Tvy2h^@8+IppUJp3Cp9=T-B3jK=AWOaHKj10BWy(qkG0ODd#4f~SiO4K z=RP00pCI$XWbC*)zb@BvHLs0u`!}BY1IlPiHHC|L3H0RGHDi>ExMowgrgBPV;(hye z=Yg=_KWA=$%!JI#&3d?~Fb#IxK1=%AmdeVrlfzfO{5bx)na#jCgd3954r=^_@1_%I zHauAYOIwxDRr+ACm~mB7^oH4sIhxsv|D-jDBFQo+^hLeiTT1V<)zum^LSM6}?p{Wk z+ohVfGAE-z_rS6eE|DWo_Bxh+V@JsPB|`P`oe~f%Sxq`@1(1l}^O)w>B%yjl`of-U zdVAPhPQ}YbdZ=w=-RJFK+;=Vr?Z83EE8|@u!s+y}@<=(#S(UWCd3<&jEX`=R)5a|x!i)vzaO+%j3 zo2(%OcV&F>ja`aJPDN?)Csu+iW=$b$HbhzWC$HdxxTqW9pPH$>wks0iD#=m>V+Y6e zGdC~(M;Fm{2Ai1H8R`dSvbKehhj3zgbWiEDy_&kL=MtO&9e}ODfZNkqYV#xGbvO_D&QLd?LnF01T)UNF3`zT6XdfQsgm zTDg}koVEbJKmM*8FY#8UFpRc3-n2rfP(kMPt7*6EAHrF zFcHg(^OTP(v6OYnFq0)o2NS)#SwKpMD6elsr+%<;vMkvKBfm)M0Ft*M*A3Z+ zGR%1T>a#jW=Qs3ZPqof^SGfE+LVb0i%3KC{u?>by2dvgs#Lj|D^#1Vca+;j>N?`Ob9_U6z4hApf01n7|KMaz;z^IxV|uwB_1 zz7x1z^H1{J2X$fQ!(?yJ4{5Mboc)6hoOmNz#zKJkWvph-z=r-}Pb!yyj?b46jIJu; z3iY&}g!nmQBZ1eiD1`!mgo9NkLqd8b6J%#C^bfy`!zFNDSva;qCsR?-pyoOK0Z5nu zdpfbP2aVA}vp4flNVt>U&J({L^$@~n9_pJ_J-i3*K|YDcpy0IP`ueRS1^sMFPUTCx zK>%g*lAvpyr}I)^<6;RV)#CTJ^0E7*+KhCAl z`D#ogA#VvIb>H+8Z6P+fc`>jg6G?u5^5K=jr?f~+{4#D7*I}QR(5F4v$inTC5uFY7 z&{U9tWg+hDS$IZRn;Gd5$5yiga!O3;~_2+B_%RxOMVbnf6p*WSUBO6VmKrxukXi7GBGXHZ}HaK@b zJtY^A&*8j}v^3VBoHw*_uv1cV!I^TqmVo#tYK5OJa$|mDuQe!5B9fQk?<8W#WeUF=FxbxBNmb|+(y88cE_DVs z2mq!vUXLUM!8J{;$_(O*fmWpeM;YnQK&c8KbuVHuRckBLlM-@8<;a_jQQY!H>b1$! ziOVt~Hjfcq8!N9`t;ZO8{%R*of+!qGIwIU|e`N4pN|R%bM_Wn|a3u$%Ha7S{YUH7~ z)`M706Pv@Q@x?JwxD?ga`nDM+VT^M&9cJ&o5b=BqjD+t?8gEHcn$)f&)YcykVlEhp zNUg?bJfL>c8gF(uNV7RR_liThj|2s=kB*6TQx8$-DYsM{61xRG3sg5vzs9KNjO=Aw z8;1TkV1_u2n)sor;@%Rx(N!BWOZOlghlZw-kGJL;O!#&}~EG|O4 zzocd!H4dx1B?&y7DM_%|&9e!Jdu2FuDe>gCSN?T(!ETMbhY z5a3Z>T8#F1FW<}*6>yWE%`HBHSm$rTiMw=5fhDqLDv&Ha9B6Dq?SJ{T4#w~RgBP6h z-dQXLT84o|Csyh^Pcji6!Rr%N=en8x{y|ndSA1WQhX;r6{n3?j%7M~l%jV&cLnX&5 zQ5UzNIrI@RMfxA{jv=MkDAz;k+p4J|HpV~gJ)c-;B1}>A-c_p&kz=4Am6XkaL3XH+ z84I>M_9%%AkW>SlUU`SDwm=>{V3#IG)BB2i`1SY+)?>+ZHq3ZR1)5{my7CMc?_eUp zy4&4i$Fuej&;q@H?seTIcQmMf1RXPy{6|y7$s_n#`SYv0qOzxn2~~sM z%Sz>u?^B{D5Cxw(SmSB`qb@RO`)q%Lk^gsSLRZbLT)O3|F*m^a@B&j=hvPI*&zkXwE>@{uD&-?1vQZHONua3QT7HMD zW>yFWI{nlj7dZIX?HU&-Kp|SvI7IJicQBJT=IJi;^hD$NxT6J~50e zeGtD*I?W$A?8&ai42|E5j_^aFv$LeeAFQ&vrH!e*8)vkNq1YtdTzavwtk3k`n(i!R zaZp{PgTr}d&9vw9ic#FQTeNp**Mw6(Gih=%I#o%mMi&9Wg&%XuvwC$lx5-|rWVVRw z_evXE?2?ie#wQL>3ISEsQFmL$pstJXhwTtWE@mDS$du5R9+wKN|G2u*`#+~Df_A7! z4cPFHejm6l_J`%@jjOP)Qq|U)L~5p9oUe(3R{ciPasJV(axn$Ou(qOhgR^#1g;x7! z{3N$k_r=*Hkz2GyWxkRBpfD;(r)+0R6w>?12Bh8wLols6 z=XNqYqR7MR&mP_p+%NE1{hnS@T#o$bPY6eU`?jkZ2~>O4BBAH$e5L6* zJJ;Vff)2tW+CQvtJ;wZS&R)#N8o2A3LnW7tmRffNeg2RTD*t8Jjq z!d)43)&mrMV|GXGZ3$;C9jst-08TtuNuP7}?DU@W36}hl$l&0RJ>loU{+`QB=9GA; z`sce>ael(cLP65iwU$c_(jTtR_3LR{8;vpsvZW4V2|)=~0&Lnp+%{{Kxn2)jFAjW3 zt){Qfn=QDUU+&nrXFPWOeK)r<>zY!0Ms3!`1Qo(Y@z{1^c4fW|H(Ei@y*ZiK)n-}a z0B@}p6(mVw!oH6ha1X1vb-P(kfw=g6MlSw>?M>TY-`F5yuq&|IRvS^{nj#rjo+Uhjh&#_wG4 zQr)(>S^jO}A4xVf{_FYu!#rc14AiR#!&^juj{2TNI%ZOq$)6 z#->&A{@};F_~yy#6iJ%#s*ej#RjNK1{)gZ50%PcihLd&&;A>*rncVnXg6A8FD1gp& zh7baM-k0u|b+Y0X>NPLfElz$}Id0WNI9)q}GP>9ScrgRgMc(H6HZyIZpgq4v*3hhp zB2WkQSx#7Iq%3u9*X$mtDliL_vI1kN;Z6`Vd3tm*8-8#Hxo{K!I_cl3wM~m;>5@J& zQV-jy9ZI+e?Zy^sJqJF9)3c7cL4*(uX;{fP1MsKCGeV^nuK~m*n=IeI%bgc(qdPP8 zZcpW9ts36ZvDf1SP@bmWxo@ILFBJ@?)eQ6xUkqf;P8Uv4+tOXipZMn8(de%>0%YHb zf2)G?KAhy+%?_z-G@y-`@IFip8QjK6A$T$Hn7(fn z7aQdz7oB1j0uX!ve~2Hlb}Z4U;cp&ljugwN2&ncFbS^0<_T~2%NzLz;ko6BHjq2!2 zsBxb^D3@ym`Ws)94)z7Te1>jclEj+RWWXAU=MPg(&7N~L0mL0}B>_LoMsn=vZ`^wE zlSwDist^qb0z5ox=l6fICLK5Id50Pc!BgvpVL&j1(i*0)YTJ>Rg+2IA#OFGiAz^!A ziJTxRG77OJWUCGvt-Zb|-6@dq@;qHsPW|!guz!CGV3B|qs{;t4;A#LuYMig40Jae9YrK_cZ`ke?Kh%`tCQXMPv}*w0M!p1}bYPtC^|4H&`8GZVt))&^KCXb|AGLUYUOkDw&d0)2Xd z+sbNOqs}|;&u7(Eb>jR?)PMTA&Pqya5LHt`-CW;U;b#a(40HeV zOTWZQ1_6L%*Vmj#$wn3qddjbTMb78Kx1|}ITB@?!Nwp)nsA(IDx>X4SCA3(aG9_T= zmwr0AiW?xB8qf(1yJe?u7H>KzB08Q!AQUeq`&*)05QvINfyf9WH`eD(4cqAGV2XL1 znZ67OAWuZ2;Vd;Zhm&LMcD;SH_1$;i{s?|9&_IB{C0mu#wR5Qeyj~=4GDouN2q$1? zSbMpO2MpQy1vEjf@X#uyn_Ja$P3F?da#GSLq9F>O#*bPEj&u0k55Mi&(Qs8%mt!B8 zO52zav(0p@$)21=gg2&Obu9r!hNQH=+p%vuh-Iw;X@J0Nz}D-h;^@=_CIk|H?h9A( z@)2?)c>@tqcn9RoTv=I88GdlGhO)H*2;29r)Q;Bkar#Ic-yZG@KVxNNMmgs9Ldpsr`Hlfz~s=xrDsa*Cm10Q z5MB1XiIrJIvCp--Yob_%Mg|&3I|d3eU~h92rw@+q^gy?24Fz!VSID~V{(~F$Tg=V>;;NB!<87#%{>Ir z1quo^?YygvsHT+j`=VsPpODF7W+Y=R0iFPW{u!9EBl^CwWxfQ-TP(x03^K z=~NFCBYIA{w7IOUrckIDxEA>W?RQ>QaAV4)YYp-nV1&0ycC!qYr7YaRDqh|iS{#XY zz+syO|EP%E3j)Mj z8Z6ne!#{J_Ku2*da6QJWV}(TrvaIb1d#aiNUCP|p5*>37$$K%2IfhgcyCugC4p=)b z+pW}uaT<%CUpn*mx{-NsT8bVT_JHU}c6L2DFCVZd{yo@PaUui7WCNo)FtH_jE0`Dx zh&ARqO7Y0NoR3Ro&9n1OfOurtdJN&;wM&!U0RjMbGJ1mZbxL2o{)6ag4eeW19_8RD zN4xXF);(X~5DcE2nvlVM7yof-cQZ%Xj4<9;A^UFozL?!Ni;a5>nOBUz14yYTYH(s= zbR39du)WZitD^=WxKsF&3X9H0VuulDxszI}NgLmHXz z8m2XT3^n;=sQO?s@*OmJM*we`P{m|q7?}UNq{)IaIjJQK%15Z5abSx zs71x^pB`iFOu3ci|2_#4iPIEyxb{mn*QvF3)K{G=lJcZlJpdAxz(A001JWfFB>AAS zGKaHE*HJEx&10u4zPY=F_!j419)+j<;5@ob}0m+OOHp_zLgOMVS3l$y`uQEA8HGUSL`8vs&!=`POy0Jw-j>uP!$Q@coZ5dQaQddybE<|i*DIaVR$o-SI z)GBr#w2`?hen?f;*SlzW+LSkbimAn>cH0y+`3cUQt)eJZug4u7v9gB=7w-^5W#%L$ zt=Y_vxm0wGqZ2P*P*tgo8JJlV>x!!Ds!cC(TF%5o;^4WKXJ2s1?BjCEz`x1;VuZh> z01m=8!X4XW@%Thf=N3n&MqLWOp6|kYKEAzo*Fkl?Y+amD2@eQ(daXUpWPjt* z@;nv&cIF07GB1#l5LS`gQ?%5m@ol3I8>$^iyGPe8L!DjVe2hA;54&5Nkg09g?bV$_ zPW3j~>d)8jp9g|)LS&DrNj|r?R|35{w29f+?6^bYjVq3N6_uF-#_EX-56ap9!v&aW zZcWYb@IUV%8FAK-lb#3*2So0FQ2Xn3gY>)PM8-iKg-U|l3?2}LA;ps$cTtfXtOY^z z=r>=7tMVUwa0}M1>e1_4#+5WU$Qg0FSUCQ=5SK#&cr0r{Qx>~V0H;}q^2G1j$f)lJ zU7n>P5P)JY?2b=!zb23Bjlqr6(F6gI^3Ny$1_xkPJD#nmlbx~%Re*yV>PCmx9is;q znp&g;jT-cH^fEFPngCl4!a!x34FlHLd139yazladqKQAvntmyW-3&Xp72=vHe|rt_47t+Jei& z#+#(KPshReHdlB-Po&xn6~NuSn4?SZZz!%AEq>H(-@B!QfOF9k#}hiGjc)iukggSL z_smcRtF*G%+;nqzb_+!>zW5jCC3!|8Ey1Por*i@t8PfKG6gyRNOKB6)KA3%+gu zmxBumfK)bkm_?grjs3|~EbEv09TB-;ksPWnp!ESPor;nI-z{{_XwK-y6uvQam#Wts zl@97?&Ry(bce$tL+2}tLvXb5p>dT&BQPAt0u%!MRG_-2m$hhRj3`Jqxl%(AUuxD8Xe|9 zTYV+kh0`fqJUlaM+Owc$XVm6g&0FDk1e!ZY$u%!2C>agng8dX`Q}k6cSzz2_eQ((q zfOaCjfTMmuOiMUX`_@Yq*27#dN`zq+-5rFs&sb;)dL)G)jyyeAE^R>OAP6Ya?mG#c z()1(hQbk{db`olc0^n(J=h;7YT?fm-?e$|ez-ok*t))_QY7s{a@LY4p@PzSO6up^|K@;!ssnF{C`> zPgO@#Li?&6ooT$Z4{bsIslDB+Xs|MM=ElYOCK?(tpz`I}_y8&ds?3Ui^(9uq-vr8be7d#?NYjC^2shXQ093gv9hhX3KNMg*-kU1E z?T)gz?Cy@CefVT?)Yzo4FCpA|PJhQwXIqJ83L&3**Ckx16sxY^h9HZ`GBgF_?PxdVX3RkvPk zx`br5$58EbVy5^5%UQPrfG%ngTLfldKijE1{N$+QVFvmn^u9{FUMJxnhuPVgx>X}& zvaHOXoV!#51#*YAT}57j6}zSMe4s>iA5-`!UrNUw+4^~=MBsy7VTxGV<~Ckcmy=Q zQRFU@!MUOVF6&!Qa!kziSjD}*z-z{^x}IPAgeBDr!$p>tcbwVJZtjrA#t=8y)L??u zi#en>9}M(vQ{JVGB=*riK5!tbtcI*AK1AJeO=EXcYy>AzUoN2a+@IlssRHh zV3cRiD{aN{t4>}_Z#S>#O=0b3B*?_IHm)kj5pe#RKKs5valdM-7}ofye=}~P0}W8h z0CVD(UllN#Q``WT2QJv`syG!xp&i!4sGzOSsvNv5Z z&#W|scAlH#I)WynhT2~1+vbH-T|Od7z*C%Uxz7eFSN2Oa5z})*qdYJikb>EBiIIZSMvM(?K}4$`LBM%;vl{d# z1(^0IIa-FUC*j~5xpsQ1<_>H?pQgsEfW7GnR}{0A`eTTJilSvUJmG{M&Vypaf*qzy zklfCyMH_{2*xvF11l&Gd56ldwSGf1BQgXPZd2yCyuE(D#} zHdQ$N19v7k!<>zQ?E{Ddz2=_BRJnShJ@CBF1tGujlT`74rLY%;h(Y=xfYWTzJ1z3N zqZt#{cellQ(UH7g{I&?*MNQy&0XP`TKRuCT6rC8F=wYmZ> z3oAwiL4ZhVjG!al((3&G6-69Z?dnm${)3;UK6te8ClEayXOBmZXllAu5Rfy$0^SjA z3(u`6-d-EeTY~fPjddR$e zoMyq2oz<%rK)%%fA?so1XRms5j>X;E+t_PrZR{N8Dh+Nh;21_Tmw^QwRI$b!>fV)k zU>k2#h_)(~0yCG)NyWzuOcV#{?Y8LS9+JizQaz_U6JyCAn1=DiV9N!AgELSB0+L-( z-*X51VPq;c7A9{;irz(Zw zXAy<{jQH7((}E^aKKp7nP{v4x_EexO6oHt>YS ztWZ{@Gf@7lZxP;qw3z?^-W(3oz^K;GVty9`K!OJPdY;-$U@JllwZ?prwtXbv;e!Jm z=!2T2M-^iciz9}OT@O&G?a!o=_VKVcGJ7x1p}PNF~p|1vkZhn`!uF}%q93WFz#-VW(~fTo6&Bz zHa%v%sAJ##i)xLy!*;=5u`Fq4VKc&SrAGhe;obl|2Hg8++a^qN28=NSQ={WTGz68L zAaPJZWA$p-B$A544C(Y?2kAICrnp}5pC+1=$2%J{i z9IF8-M@dJiEFpSQ#n<6y1%Kzidx)Uoj6OH6gws|vHa$lV2g%L>MfB8#Ek|$k0>SC zmw(S~yt3%}+Y|ei@v~~n9FB+qBkKQzj?MM2^ABCSUAF3JuwB{?ER+!x&)-OtuWYq8 zy~r_tPgh!k!(Z+AB2lK$?5TFW;P1`Q{{jiudV|5w12d*ii;1-SX4rQV%yHAe%z~V{ z>{`$L+HV#$K}3}mPA5!~$$%H+u$XA#XekGd9IH1)APYvbUYXVLo^ndz| zYKURa75Nv?d%xwy3NZ^GmcIx<(R+81H?rlJ z1cCwi*w4G6=u7xJ+c9g8xX^0=tphId5VFnicc<$gop4{=c;Gd8u^8bMhylVo@a~LC zcAYR9>{7pIgB1x?T!y~)AJEFrPWZ09sPh8!fGipM>|+fT3rQ-%sS2S(Jvrd7Ol zIww6Y{n~HS<|g84T>}qJuwF>JZQe@+r}Y6U*AeT+cv>5;9!7VgFWQu#ozixoc~z3D zGZtC7x)V@MM2u=xv(`Pjs6jQ`>4bi)eqlO3TCR}YG&||w*;xk89FG}Eoba{;=C+K( z1Wq~=?A&CuJL+Hv&@%%PNz zG{W^eUAVoVV?i7?;{$Y=3B=~${HGvTd*1J+bvrxb(Jn4$0hCbelTM{IwDC#it}h-u zb+t-;RR#?{kV68@H?>(ThEE>A2AxJi?wx457{)g$Y&6D)-@b*+xXngvJFJ|xx7C_I zjr)1uef4rbczhhbniM--)b6(1t1a-JB}RFt@djP!{@3jKgD&KXo(o4tgW+qqrM4vvli+H;q+x;-=|L19JJ)-3(0QbOigdEda0&roh;sk-Iw-(qwNG;wwZ*KC-5gb zR~jLEq-Q+mJS_;k(ahDvoE-UJJ9{7e^5guSC~*tpxZZC1SXPs&e*akp71t^ru|Oh* zxkCwhv&CLYP_R*jKjE~r_73I?Z=|8i3*vDYH|B7u=94B6`qsB-xE#&)J|MP!;O!-x zZ?Z3epKWE>0J1JBB^6{*Wx!d&jZCf%c_&x)o+gIXrm8dz#IP^I=__+h39Dtg^%Wzg znD`G;XPbg^3rV3kM^bpd!O`aw6SWI?Sh&VCMU)-o;1%*Ne%W6$3+fU+zSZ<>z(B2;erU{0l@VE0{ccDYo& zs&<6D^Ad8ZQH(}Q$&rPmH@TmuGyLVvrN7;*zX$3$|M3Y-Hf>VfCO^@A)vCnyv7)j* z!3g8TcQ$B_@BE_I#cy>DmcvX7-Z+=dkNktllDglQA=r_4_F#7m6 zm+4wD+V&oVVpXkbbEAP^mFy1PRFJ)7y!c^Yuz7n#?ILuWJmyq{_=SKwTB2m+zjiFG zeOCo{|)Sl>yWV`q(85zt96rbYX znV#w`EX+Bj%6T=;*C&YyFCk~Mo~E%*i?39V#h`NP$C7(~@`}ft1 zB~L-#J6Bg^yNr&7shl-@%dI_h%%8W`Wb&Z6{E75W{OZfqXFg5$NGQ2_F)KcYDuJeV z2l`L0&+|WoUtpl*46ZRzX9Saw;*xHmL+j(BtA$g)O{zWcn3)3W!?kcG+BFKf6Z~a2 z$ToI2UeUtxLAu$Sp|a?j!=++mi+oYK$d&F4B7;bW(ljw(-a4)zrX}0KM*Rl+-73?H z43WlUKMv_ump+^AqfQy^_ZyxaAerK}%hi1QiBIDi-cs5SQSPQbKa-L0(OEcNSj86a z)Jv6~U&KfdX!tS7gcW+7Z3pZIsRzLBd8BJ%y4@Vv>>~!j%hCsoK|tgrs?_^hEp08Y zt?o#GGD2wEi*>h%!`Ul*qOINa^_BaOT~;71Q{l~Nkk5yl@x!d(MeDKC_l58|6v&ujI`VXV8&T&WJu^+iVy7S(!O?lo@h?OgN^v(+8G)5O~AO{-d=ffUn|xN~L6 zdFd|!LLCbSBz0dB9!b}-+kvxT zvqTDZ+aHsSZr`%R1idv3sBN)jk-%t6P|*SADHoW3%axM>X39D57-QzQslSwgS zr79Hdn962l8V!88A5AYFoI`)=7C+*QoJ&t;+@yL^_fmT9Z$Cl#fVoEWQC-e$+tAQd zOju-LyWXP~9OOX?VDVy>DszsV^ADX>MUxbv+m$zt!sAV`Pol#I8z$VkTL*fZd!9gs zQlv@F^p`{1m(1F-@vmLI#qt7p9N;CHFIJY8D`s{_MH|SIr!Qr<&S)!wlZS_<*DX5$ z0_%!B%^2*?59&ybTHWb|$D2t(LBuYvcESdXA00(1k8ngNjSg@mI@5Rcbp`eq8sJ52 z3)qXn25iR6(*}Tqy*9nK{IT@-c?OOs^{Z)C{9mVQH1vgvX(J|O)r0=(Aj^vaocz_O zGYp3~5;asUSqKF*Np>P>qzQ(5%tPz0y>rSt+K(8^sSq za3-o4M+<&^PcC0pr|8C>-GU~1(jMEDm0_Eo#uR9nVs#Gxa6p1kvA_(|Z zzFGpQZMelNcxI~EaV=-DE7XUB`g(=hmt@FkxfYls1cv_>XLxg&S8Pk;Md#2OB@>l; zH=hu_8VN_V=%cPg*^nO;s<|z!zmRjUk1{rdle^;IuS&-a+H~cdkw2{?5-9Nj2~q-!shL#~mUJL@&pt(Z$}$@1OY0d-<^6o#ec zv;^&6m3|Xd(@2(_9RF2NtnYN5iC1Z~w}XB|J&b#CHT{^GX>>#)UV=M>6o~lJf3NdS zXvW!K0xS8f$)}s_p@{rgtXp1U%QVq?Gh1)%KEFF~55y;&bs2Q@g^)hz!4*o3#lbl> zjdl42{Dgr0#VbN=<2DvmwqfD?1~ez6uBV|-%gu?Ec)K1PsU;iGJO3%3BJ!!pZ$?Ax z`!W$T{4ZY!B2|(?3&5D zjb9r!kQ(HS&*;I4S_-o?-;nv+hb(kFeGzj5@0~Gvv`85*?oRHoq0r-ax?9$nl`Xj9 zi^;dX|Fw)G&pyX0bbX5f^SRZmMDIoQkNx^VKHZSF`TJRQxe}k1GKqkYAGRSFyM5ph zbR@WCWk(HHeM%TGam=|(I%-SU4fY(gtbs26j!J3(UP?PmiZ@=z_BGS&-#1mC85vce zT8eYV)(%^kR=E;zb9Ju>3^Ng(7Bwz1o=sVSzRZyHp~c%@nJ>&_z7GxA34rm$U3m>hA-g-2*_pUXVQ%{ISq!x+=r!T_xsUJL z_f%;*|3w1#p1+S4eJZ%+-3zlOefN23#&+6jrbO~Z&(yaS8j)wrUNEp`DnW{17rJ%DfT&p z0TnqBOEc;A=ZI&cV)5eK--SI3M!G=>Z~G-BI>R+0`kM0r%TRXtQfwx&_z}#or`Til zWT!!ZdYG495)+=UmdAt>n3!s+0I`fTP1&YZw~Xc?At)^dDWQgvO-iW8A|?bBr`W91 zRfcukKjkqCX!8;v>{=?B{#N|O?ij5fhay^N@`1bQ>x;5)BGnw znu~6M*MtCf*Je@PI^zdYEP*L+8wxq?jeVf|8kcZ&_7HZ!=2A z>uCXUdu{le*PwvtlbM^3zFb3fh?RVPlfv`NB|h5c`t`TaG|k(Y(o{*KMyAEv9a&D$ zuTv4~CTd@Pj#&pZa$EYB<;Zy+`KgIbKHldwGpz_ z#`r1HIWV_Rbc)-I1hezhx=ovRNk_K+o~Zs%Fy0+iHMQ}4_vL*~e7x`~Wr$J`yKx)e zeY(7Z{tD_f=|wtDc+dQ&$u@c3?yBt>EWZr;_2jltBs&v32IQxp7y|Y5!LLgYJiBFl zjpcgJoNJZQ0XX3sC$XfSj_IGftHv2^03HNFw=c>8lNGvhNA#X)|K?epgq$@o~9zEsdtJ-Za-L-p2Y_(sZ1 zo2cU1rlgpruL9ICEKM=-fZEyv0Jt8j{PeMwc%Z7p(&@GVvg;4Q#i<(BNEc~|uE_2J z`{2$}gP4M$%el<%A9o4VF8k*1LMGCH;}KZ-CkV@FO=9&M^NF{qh4~4&qjc)+Tv#zt zJi52JJ%U#&5no2V=Ao0CUv9H6A_B~)s}Cwp5#<%69(m~eR_*TxS9_GRrFvZ#em*3+ z6*+Vyy9ov=K`y-4m_SmT$jYPjcTMX4I-sL&hC1(WuWc}b)D!etw!i;f(4Z-jz5L>O zNx;ItLn~iM`2OimzvVOa0;3?P&`r>Nd5(?zK84n%wDiW**jn#{_}Nc^aHSq@>S6$o5=9-t(X94^b**Amad ze!0!VE}67G-A4p=#3~Z}b?o59J(q#yt)QRd2Xdt}mupepKiqR=S&!KJvd@s$MLXNm z_ranxC9urDKdj)dHKbe)r62QUzG!my|Fwm8kowzLe5+@BPQD|x+SlxiYH`ko2iTT5 zY6g`STIQ3bz)X3c8s+iH1p;>EAyKvA`U+_~wUMg?*ws?_MXA&31JcgF{eL#7M-%Rm z2i@ZiAq?2U_|iO8h>yiENQ$}0v9_2N7k zJ44i>&yhGNxhKnZnXcg2s_xla?N!cm3xYKm<$h{E1QsLj3Lsb}pCtlF2QCuF$MY3+R{~RXi zE~*vjR7RTUC3FQ~Jq|aPv@6KXcT+YVKY-N=ca;DEy%uOu_k}EZHu~F?WQ8qtQftwl z7_7;wg67t!WOc4Kr)&{8;XAlZ%ak(Qk!14hJrK$4l$F^3JekrUb=*-&3q zP?l`H+&!!V?^I8a=e`Qu+;wu0`4gs8s4wjlkyf7ek?Y#-D(*)NSmJcDa~85p49Yw9 zqQc9Sf3g-&aVyy^w(1Tx#zCQ;HE}TPeBlcR*K-^d+Xgw!6({0`VDKSMwqK?yT=|i2A;rX) zogaoE5nH)KiTW!F8sFK*{?3qtc$A!gMOoU|xwq3z;2Je!iPLHh%iN(Lu3^#0`xK&q zUb9-d3fTWJ*>>IbMQ$A%TchDj)p>3S)HDq+Bi$PBB?Z5#?cJQt?o(r5?51CSQN>^Q zL&FAexDp2rLM@Xy7pL@&dTRWoqn+SoyuMx6TjWR(XhKnVCIl>xu0>;rj5*W-q?H7LIfG=x>T}v$~cv zv2diq;dEh8xAJ1*DAoh5qX#Qn7}t0!o2oTi@v4u(dbqFfbTNGjy@j+|Z_-4~tW|fi zFdml~=}7H2OHFv(`Y5PoJdWpD4^~WW*q^ICVC~ExS;HlHznU#Y8_Czb#!;-K$02(C zKwSfc!yl)%z7WCuyrwKCTQm%&YZUe7Ht>eZ(-7|CHUO!bQvGwxJpNv#JykU`ctWAHwHyTV^t_r?2;j(Zu3!3--`Sh&l&(v()QC z;oyz*o3A6$gpYjN7F6SO0aO2`)XrBoe5v5++Hy0zLu``b0^aY|o%)BAkiR0R?V`+p zM=)K!Fr(&Tmmo6$aD=(9#BDqp9@X`CVYrZOZyQhfGX4RQ;gpnH!1K1tfm4Iqc}-DB zuaAx9C&KVYUJq#ixHd(G2&p_^7jU}uZxFYYB^1Aj?+kaoP2vL%dp>^}p+H2={X&3Dox*aY&aW7(MS@- z!Hhlv1%-r4QJ+uY#@z{v=gjcix=eg$RQI==zGY0$Hap0up7>_xreD~AV zpa5#%LIP;&z?oSog}1oK&4z<0*-ozI+U2p5H?^0 zhQ~%xyaYput^cEwB4E2`>>cP1)esd7;h(<)gCI!lExirKx<*d;XNR>~9Gyi3phE>e zvvqOjM1mrd-MSIt~}gzCd~BP%;*hoi6kYt2IYcTz974e!moCgG%n9 z%>gSrvm@YM$p|M4xNIjpR_F6}Xe3&wN=NonnOMh+{^Rb-wFOGf{Fn20tfo6@WcG|t z6i=X+iMg{NdwyQT0B4s?YbjwsGd5gKjL&r^1Ca8yNP*BWXA{89$V%~RzGYFSWC=>zh%PwJB7OXaMcbTO!}4Bqp?8<8KkVd^X3zXN9|N2Unc;{oOFU zx+R5iTstkkgSjG@n(^xJb&+-OMktQNW#OITpSR3|AnFWvR|H%PrB9P7J@CrRZOLcE9f&_`Rh_{68L-UO$* zC?CFtF*r|d$g0B1Omv!U>k13Y+2QO%3e3}b&6m10=SyLfu5^Ge-C=L{Fvh{~+)LSb zh5FA4{M(+<$)aodY;$um|M%^l*@wG^BRjB4Ka}!kl7QV6+SqVIi`F~7{;%-7`1)$6 z)m4NJaxl;CXlI^kmv&)ODSz14Z#~rVR*V3>m|(rA+gu<@LO}0gv#v7S1my_rHjkQ{ z{(j;ABuU$Bx;LMzL0qCbChF$^ccs8dkEt+Ywck4SX90pR32yv%IuMnFdb8NtaVLG> z60zkEFVJDEavkX&bOWd8lp$^KC^OHChC>kfz=`DMS1TUT5H`fA=q{WB$9mbYF6C}< z{;@_2A=p(|W$VWBV(XT^nw0Jk+FOUdn$z@;HL-Gz{Z(O)JR6?6D?l`@|8>ceeIHA- zi3uFJ5DU`a^a#N&)t6f|4=x&U(}M_-!pSS5qs}KUj;av^DW{VH_vjkjreE!}^1qXC zZNUHSPzR~Uzy(#)X!Dka1P!W`(25rJT+V4VprfVEVpp9^wS_3u`APJOy`NrX=`()xHy5dcwoVge#Z+iUti|hT_^94+ zsDkfhzR1+1eTx?bsSx6S1TvM&()Hiw{c^;w|1#Ythh1!|=O0qIuKeRkniAb?Dry90 zS^hN4XWBMFFU^r)y~y1QWrwf~flWIg?_%BCuRqOo1WDDvT#DgBM8Q)H9-9 zD-TIN?;!r-Ie5+TRzYoan6#o|r>#(gWcR_(A6JATz9#5c+j#$nH5Ly{;hw~cmd(D8 zpX;>3i73kxp|2diO$~SnJN70DXSi&yqxWLLc+E{YzlKZpw!B3E@%hJ*`A!v>m>)c^ T({$tnz?bqf^``|-OoRRhZ)z$y From 19a2efd268b2cd2797187fb0d12277259d1379b8 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 5 Apr 2022 11:59:18 +0200 Subject: [PATCH 149/827] feat: password reset email design --- backend/public/img/logo-osoc-color.png | Bin 0 -> 12930 bytes backend/routes/reset.ts | 288 +++++++++++++++---------- 2 files changed, 180 insertions(+), 108 deletions(-) create mode 100644 backend/public/img/logo-osoc-color.png diff --git a/backend/public/img/logo-osoc-color.png b/backend/public/img/logo-osoc-color.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d2a3119694455cb0c7c9b3722539dbd7cf59c0 GIT binary patch literal 12930 zcma)Dg;!MF*S>@(iZs$lNC*-GNSA>~i*z?bcMYu|0@6Kn4h_N(5<@7>kV8n0O7{yx z=QsTRf^TNcnzhb7b?@1GpJzYkxse~#6dpgMdk6r)W5xGxKLP;G2=-_3fB;((EGEW| z{UfkYR(K2C-hJ~BB}o7vD607Om6q?^ZuTQzt*M5-`|(H~C@pD2dv1fyq)AMoIQTQc zVJB&ALsMCU_I_2Pfs0{PQ`M4v#gTP`K`z3nwqY7cWIK5QBr$UzUzAqqRXj;S;`iK_ zNl43!4{LVd#FM9ECKU1*Sz156Y`+DsP*{yDfv>^4h?6;)r`Q?&|2}eV(40&e_rn6i zldLS&IWOqS`nKREW?QIiXbWD3p0xhhGzFeGow3%$tjsVa1)JtQlf9L{HZbuStXV7# zQg`AwM{kYVZa9{eLpImq4m@!HIS1GaM@GX;RVLG1MU!VXX-|?aGp{_VxOAT(YE&9U zl5U&=!G~>g@&l366;-d^;R1k>qAEv&ZXozV)(J0;?99o%~Nl zIXwYeP2s~(wU@C_g*D`u+Cqi@M>khf;mTL9?6Uilg8M5yn}M|Q0Kmo9ybvJ+-$5q@ zXp+De%0-U$b738Yb=Cyb0PrxJdt+c<+uPnb)lf5zl4&@BAD>g3FCCOYb{_~a&#zO~ z-pxSsAR%vE-gEdVTEe`jZLkm0F|8dj)LzJ1Rw8AUinocUoj?EGc8vvzH3^zAf#R_e zeWGPZ9Q|7A7FB~q9sqog3`ZjEXx2_nUtPw|c{c3)ipBx{D>t`6rC2;5F^_sPVW9u> z+U{RZCU^QP0AR|mu6W5!@L$Kd^?Ri2nf%)l!At;985OkF*TKE1pfT;r;Jzwr+)fRJ z>H|P{eyr}KH|!>2oJ!u|E&AhQ)^t0p0Wvqmt0Z*ZND$~+DN4$=km3NgDwvqIuHQ( z6f$aqdn^&w##M8UTkLjOu3SPCK&MkFPlw>ZW_k)wL%B$%RF2|9;1BtvNe2G9A&5)Y zA={JTf*ei-behu%&0H3{pYTO1#uenZI3Cg?4FKS4ss3*RwHp1$JkF9y>mYv|KnYo2 zEcE)L=b^y6qT(f&{*k#%eInoktCl6+rs#Y)&gN{6IwQ5k6FeX}qWyKAqdhS}QL&Dw zCoInTu8b5kY-%az$2U4{gGx&80s!{6sbK!DkRxGLaZAHrL<9g=m0gY!+?%(dPDBW0 zL4a*DAuNfyP3^#tu}vuRymhk?0OSavb>F=z@zFi$@j!rtod8M zp4&vsRZoJDWbbN{nA!3D$;ojCaWB97)zj{PtmXfpt)rQ7Ikxe|77gV>Cd0}Nwmg{Xl}94)FU z{N+9no72&gyPm8Xp^CYow04D}s|HPPpbY>(Jf5y7uO-kMe4!cbXyU|h70@%$xyN9ClQhSa2hpIJql|#Mo+nWdQ^K&e#kYn zT=NTl1HRA$O)2;I-fF#N3)J7071G2Gn&jjMYcPo`H5MF1**|GarM?iK=F4d|G)>J= zl-4fo@Ng12PBeH2PQ{K8W~U4e)3S8^^;mOvu~EejbRPLpdS+m@s7laaoZ#*W6xOL0 zw&Ir&VS=?eHWo11URF*V*M9^~jNo3zyq5xRumgg#%1VZXhmVq+Tl$+>ggguaIgYu9 z-~d2ZSe++0COo4|wc``eyzrn&dOsu0IaX1AFMA$zC7(pSSjP#+x+SzIf+OQ0H^??zEy097$e;r_4~30JAX1& z0stQbwtV&>GZIQfwlS%_voGNpWcWZxVgaA~G`!ek z%lsV)wt6hh*t#&n5QB|C)o`qj1+M(TLFMh+T|S;nb#WF z5jvR@bi8WpZL%EnVE66Vi+Pi>=cCG?yd1`EBwL&o(AT zv_MS$<@*_=m_HX0F5vq7Q)k$9D9(1*Fp)>n=b%d?3Yo%ic=_}pI5Eo zL2XR-@PfGS0 zQ|WK0#>BV75b5TA7<5#k%HbtZJCCr?C>S~}gHColWZq4kg_mX0HovA_^+fmY#eyhW z=PhR<6|IfUlIJz!Hb%MeOnlJkzi40iu=UB8cTeh2&kJJ=_D|Kt^n{iJbl^bSCtQ{W z6`q(!N3G<0A6X}eCl+O327*KMR_DiFkCE;biHHg67D)?PVnn-JVhyV&*c^RKA{B}Mgn=vB=BC<}l57RHiSuH!Gk z+7!`T`Ep#~{#f~5V}C>WVcIyq^m0JtL(up2EP>&G=`z1^`*DNHnvODW<)E^?xgH@O zjB6t#Jlrz1;tpVvWfQ;|=E*XjaR{Q>Y?>Rc$EJ@@<>PEblB3BijG82EGHzc`UK9|M zRItno>i01l<*EkGS5O$~5Hd}>B9~Z%okj(wZmj)}UXjqR$z^=~^yI1Cr&G%kRiPT) z!1lrT`P{TYTu))OsisDVxdU`&&F2kRIR&wW(Rh#%gXAw*43I}oQhG%hIBLB zFFxKni{VbSIH3%eGy8edv-MDThp7?AL0inIA(No%j7b`vZq*3#K%jX%+S)5tZM_&2 zQ2gSitV&MU(3#uO#Os&O9o|LsHNSBq)16_R(q@8K=;L1RRZ^x3Hik{}8@pu9PWEK* z-Ihs8@Sps|*FDu6Tn{vwar?N-NT?Y&R5xDODp@dU8FKG~LOrjKX>{bvoj}#?`_;n3 zL0)NT?7gjXVv;DYIl2-fZS9$X+E(8t%cMJ~)?~_lK&~U| zG4Z(YrHiEgzQwYZ{yM@rC>)hK@?fBD{VU!5HR6;6s6?NGU>RaE#fUC$w2H|zgI87y z_bMsfNMkCel^+Qyc{RR4a(_p@T4cCw5HC`Z54|4nez~(UgEe60Jt|8!nDg8*&Q@B) zeQy%ZqU8MOJOlrjM4<5_S7ZHU`|nzsW$!dw{_!s1OZdncI*`vf>H`+B6hT}~YGUov zMf!S#YbFDX+_^1JOj5RI8vQv`9|7$n{BHM2x1Mx zNalUuzH4zkEChX-&uIF_W=(j?;KSv?hRSuf<|n&A|CRKcW%Bg2M@KUYcz+ka54G8a zSB~1+WXVKeTUj34#PCx^cDO#1O~iJa}&%E1i#!Kf= zS;)~E$G|(D{{&UPxUt_*dFl}1#x0GG&u(aPhPSp0L87h}XJ`cpXi1u$1?`$F8tT*^Swd>!^%gAG1h>d2o%+sp9nPe4@RP#~k znY*-TEz`z-1SPI#Bb_UE)_y!upuRVuZ1=LOO+-2gX+U5h%?8O^d{^=<)Xd? z3vfiRIFSA*?eV6b7CNOoMf%_7dEh@1sk18sr0}r#IEdT|P7{-mVK*1>n4scR!^eQm zIjiO@V0mcaWQYC14Knmou(H=_!@*F|)5@Wu{nCAv>sNmLBk`sk8k5|z=n@I4NS(vV zkJ4L1;SiYkqUcBGrS#CQbRKOMC#}Zkv*}&1=2tRk7aneI<5{NalP`wesb)Xu-pGY& z&GkEKj@X+-HKzwxDES!fh>3oo_zAET6GsuyWZCK;k&Paqx63?J#~8Kl1avRB7ynu5eBxTeq!=2oEaS zw0c>tbnqi#Y&H!(oxuE#%1x&nDmnm%;=oybeqmwb#!m;u@h@C^B*=7KjCHC|H65RB zgMeVzN zxWwLczuxYw=v5kA~1T-b=q5wfu={!AxB3`aq zNNhKBTt748r`xWGN~hEK)3+{giO^X~q5hXsvoVaq)0jD3>RMHK~6zesCn zmv0J${i2!7z0rfX_wTtQMr(P^`=(rg2rsY*^t!t0>I37N%WoIsH@Ety6Y);7+Mm}A zX&xxouLZDVwHoR%hV*fb0sh&i_8)1E!q$4B+SD$WL@<%et7!c3$25_r<+ctcE#fu{ zVS7$-LDZpghQ@U%2$!^dm%V4b@OKW&M}6u?5J0@Zo&DXv=T#M0yhk)FQs`n%YD=H7 zy=6Toxa~R&AMWVyqxpMpd8gyQmi>ztDn0ZH>LVa`*3(9L{%)H15&aUaf*hC+yHnf9 zI?JJgmPwyhwCJ#5bXI4xNN%ppecO2Z5`j{#i?6zM!^3xwm%Zya5ZR+A?TnbC6LvpT z5|E<6+TB#k2}t3G8`ID*3yE!86C`%DaMGu^~#q!X0cH@V9&fc+yIRAf|V8 zS6jnrknRiEp5C+n3K>O5HXiNui!FD|2rd)##Ql77-yw(h@;w^G3F1>|$d}1%DZr+h zgB}zga7~0>J*84=hFCI~RwoebH*G;Edn6txGf2fix33v=-Bfsr&9*&qvIEF>f)qLx z?r9nxRmAE}5meU4_RvXDs4oau)pygji#>Nc(-joI4bS*n}sab2fdkL-p@ao z>z3+Jm~sLoBmLF_)|kufWR>$2?xEMS5SW};I`CM#$x!*+_*5<{H~;_tOgFd!6g$8Hak>p)~3*w#!lQF+UYmq-}b;w7#8={;w#t zUH+>e^u{aLgXt9$)s>!-!p>J>{;8Yys7I9@@3gpI++i*(RZAU3R@qB*zlwcJ`Rm1D z7ZF{**nEKt`Gy9WXGtL~SRkk!hOl>G=BD}@zEg?lA$9(!97nUP!>-CcvF_h~2F~Q6 zby{sD{iBgU$MVAcx7bSrV^2_6zm}2KvD3{CSoB?U*?;T1?caX28WbYqcXwSHdt%x8 zvD7H1D4N=JM2y!EWztZAh0^11OUSMAuvAoTN3~;d8jrPuBx=2U)zPNgEX_{R!jz-4 zut0ly>FN0ok6!(Tv}$Vo`?!&jZ%Zcb_$_;(f!il+1HLd-6OO=OES9R@Y~Eyp;=CCa zo3}&}{#WXI@AbIEx{VKp3zYAyI*d_x%UN>r3YBthy>P19#tGktdleIdi+s42qZeL> zD99%l>RLtO@Q&B*nsoYo+E{sEU!z`Ehk8bV&`lMRot%BKEvXqy`pdE4PsxYg0S!v6 zx5th0d~VZXy9%Dh)sMgK2D80x?tAM1-{e1{{lIY?zCA3M>8F9b*_s=%lD5&u_meD8 zr}(3B!0li;BcA0pGB%BM#FuZms9D$PN?s@hDN#*M`fz|FH4y(i_w~kKH{cuQDrRpr ze=A}f6f`NBsGq9Y;KhS@Qi^&eBSPMIxmesqa}b)(s^ zP3hO+*tV{nGO8ZS$@H3)^O1Uhv%m-zz~K96SuF<)ZbD2mH{PWhZ_?>WV84BS-xqxGXd z0^|!VSo4X<*bawOJJW-@#ioKvT9~uwc>xCDk){WG*h`zSP!o6(&4{}tQpnbn|EWDb z8Br%r%X}d}>4L`ipDxM@e16b{3Hm}MN<9}Tk1Hg%sNBNF_|Wg;Op zjjMMlsuTl&Qmd9fCk_+UzL?TVhmr82Ld0yFRSsnCH10tAU8D`bZde{;q#B~oqiI#1 zxz=iBD=RW8c6n5ng{hCU1=#bN*u0srE33RM(HrZ73OK#2km2KicYVMZQ4<^7vh``d zqnHJvnziQ1E`_nfC^w(C{@AOQ)1M3I{;<68o9pkanLrM63l3%E!a~#=>?*$Zn~L!; zNflNXoD;h(gXDkEX4fFj8|$6jqG!i(=EX)AlUi}U>=U+MI29tM(wh123EsxC?8R%{ zqG?%z1L$VCd>xt_vA_yfhCAM5!s$l&PcS9ki-;wvYYB971kb2ae-vyBPe-NUHc-1T zuxqfl`z22%d6~8*MU@gy3CBc1h5S0di&B91`N6@puf-RGYNu~}EE|Q49ic)O4!hA< z7nU%k3_YhhS=cE4`yc2jTg#K2smCYP6|gzeCzfq*hz_JRoZs84w6&I(U7V!EOqJAR z=m^DfN~qj{L+pl3EGn}R!BDe&mG(NHN1&DUWiE+c)zK&~QAKP`^$0j&`>SKBq3-Bl z*@@$T!M1xPCDFK;WUtEIzC7vwvDg^4PH%Sa7mh#KJ6);BPcjS-e)o|(eQlsLNgKr$lUNYoLx= zEpZ|drptnU4ajIJ7?+7BqtTkCZ6?5zGckl+_XlCON+wI5+3{3af;$&JB+lxu%rXef zq4`aBU&W(vDUE%>L%)5IczZ2YoCr_u7vi1DOVrpcuNdnKV-J(=-%2o&qOPCnlOCM7 zr24a+=YI|vkdyNWULQcnSHeOp5p`% zzFYfnLR(*be4e2|OYgwLqfle7f}H6W7Ttp^y=vH%lYi~bL1HO4FWX9cUa-0U8Ety( zM)=Rg+=}?Q?V{5bB5%U*#C=i4LkM&AP_21Yl;!ye^Y+_$lY$t&!Qs>0l`p3(Xa@7g z@79b^VYc;t(TX1SkU}r3PloF$20Z9Y9R!JUv-R{W(={W(r$!CRqkH@a5|)m|$>3Tc z?9_gRb=>jkY~)F6YZ_Y-UwdIt-D3vqEp<7??Q-HAz9GWB-d?gi)5I9eEU?L@yj@MHCfXOnzQC)Zeq$gUr=p^2M8eI`DcTQmQNd$#w63qYl=B)lIfiM~g{haUbRn@5N zV@79{5t#U>j`MG-p`0j7J+x)UE1GfCa2%WyL*6}OVMWZSePB4VVV4!zrz)g2?({Z{ z7e3O?(`ON9PWPVMu4X0XjC1vqZw&rH3Jp{U+mmb!DnMVF9ke8e(lY31=~_yO_ReepMnh z{+8gYq_1r}yz`X7y|r)Co{;PfPxFW;rcG}uIo-C}Zq4gw?Nd%%$1h7Kzt7VneadZT zn&kf>FY@>N=k6;p2NMbLluf6m&3jbj@1hN+p_rHryKlq2T2f;y zrEGpULP2NBO@H$O^YM?o(hUZVNSYr={u#Mey);;kfrH%0_OBa}m})&0-oNI2?@kL4 zhJlI}jr)HloS!6J)d*5Vh$OMCg&IURkT^=Xw1Z`G_t(xS=EZ4T7U+~vnx4(rO_ z=$99RtNg!xF$gSt_TN(M`9?sW%~31T>g&UZt7%GolK>hxkFRCedG&K*`||ch-(^P? zqtSUOPt1!XU?G;!&NicNVT8y#$LxE#1ZDmH-}X^lZmYmvD&+>%NO~#PSd4#L&o9tu zbL*E?juZtDTb19~O)_-5owXnjEunsamiczR`@OEbeD6u|aBxzfZqZAx41x zu?n2cPh4nvSKn5OrR$ahG)|TNMrAzp@2iHa1r&;aN8g=8lxw}MjvB4UY9*_y6<$5qi=gFW{v9+O( zzoUfBDqR)!+(=kZXc+kpgK%4ql<9oYlzbg4EYIaLEml!NZ%8w#75HS|{8Iw#Kk)l8 zldo>#!$WhTJEff2;m{*&(u)m^UcDWgE2Wj`A_rV=V&LH6$Vh45{ix21 z=5Zy$C4Nh@*p2*!g+pWLm!+b%7a*D37@4gD#CQ+4q>pd2H_lMKKz;H2EYkIh?!{f?2W1u$(5}BOw^MHRoF{`a zf8Cb^{V3Q_OIZ{eG7|{4Q}D3d&Q^)a{C31sxg z!{-bwuW$qTZFETyC={7^4D{ZwxktRYKa_)LPXmAPI5O_0rc_ii@WSBzA!@IJ2hM-& zEOqS&`6Ua}20vV$XK7fQ8}u<3>53&MGVmWvwE5PxcAkNfe_fIB!Vp=aYq`8*vSiCR zF#pf)8Nt_w|JAc%0KT|8ZeT-789=BTTmRnsBr4%C*$Q0zI$+9)b&d|K z`Hy^wRFu|M!K#vy#8}~A%=@@HU|cLNN}r2^ii*PfNuiz4m=sv-+7a{RwYr0^`SBb3 zW@yu|7y>`jp01Cy8I++LUC(3-PW>cVWj|d0LXvZlu~~1PfWv9m4b}K(xKq^|PN{_a zl-ZU8AYRLF8^erT@;rEm$?#MmGI%NMJ4+}1#aO1H0Qu0>YO}E;@-t{npDAMI$KU2^ zdnFahHwJ~D?Aj52N}u;E{doU|dynSc8m)m)JBi<5{K!SBZ=gq=cCSC3$*=8r80faH zHlIMjnidV;R58+1g7Knqy4W!Wmtr?&xS zic0J1<4UX1CiA3$Gk1QapND_14n&eoFs{>qGG>UH^)4V~iPt83H*Ufz1k{@15)9v& zMDl50a|yfoh)V0*#k%HM<=ncC>ks!W$=2G7uC+z#ms(jbI#EU59lUKlAsvoQk}5pm zYi`E(WTla^aRKF|%<<0E{c0GCBvr|jtGR1;;B(K3|()5wTNlM(ffk`Pe1 z<;JPC?kj#Y$~t8vXFSlbh?{e=VR(qfp01A+9%7shF{7)|tD-BT85x=Mi?V5j zam)X~X}1%3KQH)gTELkZqD?t@|7K{LYUd4t9GX1_(8RaP z(7$BnIaUXR_~j(=V07UwV>VW#V4REC`UkHY$YAwPL?G@1GuJ5Ssx<5y zh)>;^o}XW>a%#=IxK()!{)wYfSWw_@^y#xj)5gHf>f+fzml;)$9&t74Skn_A?|nZL z%($DmS?lwa7y5XwK+CNB8#Xp^IlqYK|dgm{k*+Ig70`3d_)XvvfTuOfIQBYJvSxO`Ab@a%)7N350qn^kj%GyJb-t+fm^fTkL6L( z==b`klSki1hq1yMTSdrux3Tccku(}<&Ja-3I5swMSz z_0?fKARC@4yWPA%-H}iWPqnqnx+(X&dyc8Doh_;;+@#91zN!MG#9Nfx&^dOSX@dZH zyc4dM?#6ho%mU`Yqd|WYBza#liay_=GwsDL%pB{b6?oe|>#x}rE=djLRW6LGq2|A7 zoRB4)-!7sX_E{6~lY9KJP_)a_(&Z|ybDkcie^qz@LP*Vce`SFtA~bcki(j&1%s1ta z#F7LshL*yco}VN57xEHiSg1{#IEqj~^GlxSLY4tJf2#ouP7Hj+g#YE6u^(Jq9k1gf zFugb-H@(=?nqm(D;`3!6U-K-B_KftUS*-ZX8rkLsU(Z$Iyz4P^fG_qEo_BE@d@RAzpBV01uE14ne^Lf5&+k{}?! zzdhM_yBU&E;ZYkeky-9x8O5v3^j_uPV>|#8jpeRvd_!uhNT3o zZ0R5zC&aDPpL|$wq}`I6)e7WMZY3eYJ_&} zPK+gK-Ay~}REjmNFwuuVw8E|;)F~@-l8X3uNy>y=F?>8{OAt(PDtQ0w^Wg&*3c&XZ znFZH3DT|!IOV@&@_d>P(srDfT4OwBG19%0jqBBRYnwdq zNBkM)qBbp5mhNUe7fWCD(&Ga;g#{KrN5~)OS`yesw1hR?2c*6+%>Q_ZJwo~cwK=CT zwN4hrmSuVA#A+(Lq~p@9kR|V9rh$<41YGF8`2$;0zu|RBRwO+hz@zI=}cZ_b9@ ziTNhCjRycS>qgFk&4_L3{WH}sy*CzkfDlOZ1A>1f=H#|{y<}SfJT(u1&L>)D6uI%t z=Pd6&HHtMs@LcW#ojUPy5Zb%M_+thmR`yK6!&S9m(kbu(onjl*8tH=znEC62xq^~K zt9xE!sep#yo_PQe*kCujyiEUEl9FPnwAw}p0HH8QGX7|v0IB-cuMhn);XVLh*00=1 z;iKEbLKC?@;+QkMb047LSpC%ftOhN;Su3f8LizzAjL)!JZ%COhD=J`cy@Cj|i~~HZ zb)^69(rXkwz1{T((6BXxTQCD5ta%ds@8gXl#%YR=g~hQ7aMrH8 zh7$aFKBB(lwcY9GNP8fpAQOBnL+)wX>nrS1Lj8TzLIf)Wj<{0gb}^HiJITA|w*dii zk%aghJEL_~-dkkZU07kSYeaxgez4hlzjPZhv)tawJISo;4|`FgmDn=!~Ct zI@-rCKe;l?JPLIOdxU=Imq--HYNBP{5nLZs2yp9e4t(IMy|0PYjP`_uPE)g+b;Q|; zFMUV!tiwMe-(z1XDCL3^=MTkuoZ1mwPeYKe0brO0o7IQmO{+v#7Ww5|t6)nK8l8v9 z$I{I0i}f<3S&`9rfWS|wij%}YGSs0wQA;WOoSdK`03doBTN$8;du^C*{E4!DPbcQ| zPBBhE5vgZc+;!$Mwabu>{+>d3z8r((000sV6CE#GF47JX3Wmh#!L-=T^1t{(SkovB zyP77fTRD3H`=RRf&3p+<{#^=M0)QOobt+Ryr1*t$FB>8AU%a{Zm(YVpgF~EqKZyXW+8)%aOPj*J z0PogfdO7(*+v`ATitFdP15-}~9w67n#jhN#t6gj%Jh~ZFR=;B`!*$uRUV5oQi@hN( ziSmTDh3-Q}(8T3S{Sjjhs_v$trBz+MD6Dw&Ywc(Aj+gStO@+Xr92|!f%saadwm!$T z2n~}LL*FU%!m)LzQBvh?NSPNj^&QL{-cXv~h>vCS&t+?PdJ_^{J58R=u4G#z$`WJa z+9h^l!;1HT^v-^>|I!h~37xfDKX+C&7hL?}WGB~$B+*VT({eFJ# zQmuPgu~n@+z^qOds#3akY?b)`dl>@&-W}XpME?vU&x^dcYoaKx_O|-9+2{WQBTP9+ literal 0 HcmV?d00001 diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 0ea6102c..1b0ed62e 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -11,134 +11,206 @@ import * as ormSK from '../orm_functions/session_key'; import * as rq from '../request'; import {Email, Responses} from '../types'; import * as util from '../utility'; -import github from "../github.json" +import github from "../github.json"; export async function sendMail(mail: Email) { - const oauthclient = new gapi.Auth.OAuth2Client( - google['google-client-id'], google['google-client-secret']); - oauthclient.setCredentials({refresh_token : google['google-refresh-token']}); - const accesstoken = - await oauthclient.getAccessToken().then(token => token.token!); - const transp = nodemailer.createTransport({ - service : 'gmail', - auth : { - type : 'OAUTH2', - user : 'osoc2.be@gmail.com', - clientId : google['google-client-id'], - clientSecret : google['google-client-secret'], - refreshToken : google['google-refresh-token'], - accessToken : accesstoken - } - }); + const oauthclient = new gapi.Auth.OAuth2Client( + google['google-client-id'], google['google-client-secret']); + oauthclient.setCredentials({refresh_token: google['google-refresh-token']}); + const accesstoken = + await oauthclient.getAccessToken().then(token => token.token!); + const transp = nodemailer.createTransport({ + service: 'gmail', + auth: { + type: 'OAUTH2', + user: 'osoc2.be@gmail.com', + clientId: google['google-client-id'], + clientSecret: google['google-client-secret'], + refreshToken: google['google-refresh-token'], + accessToken: accesstoken + } + }); - return transp - .sendMail({ - from : config.email.from, - to : mail.to, - subject : mail.subject, - html : mail.html - }) - .then(res => { - transp.close(); - return Promise.resolve(res); - }) - .catch(e => { - console.log('Email error: ' + JSON.stringify(e)); - return Promise.reject(e); - }); + return transp + .sendMail({ + from: config.email.from, + to: mail.to, + subject: mail.subject, + html: mail.html + }) + .then(res => { + transp.close(); + return Promise.resolve(res); + }) + .catch(e => { + console.log('Email error: ' + JSON.stringify(e)); + return Promise.reject(e); + }); } /** - * Returns the html body for the email with the reset code applied. - * @param resetID + * Handles password reset requests + * If the email in the 'GET' body is correct, an email with a reset link will be sent. + * If not returns an error. + * Route: `/reset` + * @param req Request body should be of form { email: string } */ -function createEmail(resetID: string) { - return `${github.frontend}/reset/${resetID}` -} - - async function requestReset(req: express.Request): Promise { - return rq.parseRequestResetRequest(req).then( - (parsed) => - ormP.getPasswordPersonByEmail(parsed.email).then(async (person) => { - if (person == null || person.login_user == null) { - return Promise.reject(config.apiErrors.invalidEmailReset); - } - const date: Date = new Date(Date.now()); - date.setHours(date.getHours() + 24); - return ormPR - .createOrUpdateReset(person.login_user.login_user_id, - util.generateKey(), date) - .then(async (code) => { - return sendMail({ - to : parsed.email, - subject : config.email.header, - html : createEmail(code.reset_id) - }) - .then(data => { - console.log(data); - console.log(nodemailer.getTestMessageUrl(data)); - return Promise.resolve({}); - }); - }); - })); + return rq.parseRequestResetRequest(req).then( + (parsed) => + ormP.getPasswordPersonByEmail(parsed.email).then(async (person) => { + if (person == null || person.login_user == null) { + return Promise.reject(config.apiErrors.invalidEmailReset); + } + const date: Date = new Date(Date.now()); + date.setHours(date.getHours() + 24); + return ormPR + .createOrUpdateReset(person.login_user.login_user_id, + util.generateKey(), date) + .then(async (code) => { + return sendMail({ + to: parsed.email, + subject: config.email.header, + html: createEmail(code.reset_id) + }) + .then(data => { + console.log(data); + nodemailer.getTestMessageUrl(data); + return Promise.resolve({}); + }); + }); + })); } +/** + * Route used to check if a reset code is valid. + * Use route `/reset/:id` with a 'GET' request. + * @param req + */ async function checkCode(req: express.Request): Promise { - return rq.parseCheckResetCodeRequest(req) - .then(parsed => ormPR.findResetByCode(parsed.code)) - .then(res => { - if (res == null || res.valid_until < new Date(Date.now())) - return Promise.reject(); + return rq.parseCheckResetCodeRequest(req) + .then(parsed => ormPR.findResetByCode(parsed.code)) + .then(res => { + if (res == null || res.valid_until < new Date(Date.now())) + return Promise.reject(); - return Promise.resolve({}); - }) - .catch(() => Promise.reject(util.errors.cookArgumentError())); + return Promise.resolve({}); + }) + .catch(() => Promise.reject(util.errors.cookArgumentError())); } +/** + * Route that will reset the password when the code and password are valid. + * Use route `/reset/:id` with a 'POST' request with body of form + * { password: string } + * @param req + */ async function resetPassword(req: express.Request): Promise { - return rq.parseResetPasswordRequest(req).then( - parsed => ormPR.findResetByCode(parsed.code).then(async code => { - if (code == null || code.valid_until < new Date(Date.now())) - return Promise.reject(util.errors.cookArgumentError()); + return rq.parseResetPasswordRequest(req).then( + parsed => ormPR.findResetByCode(parsed.code).then(async code => { + if (code == null || code.valid_until < new Date(Date.now())) + return Promise.reject(util.errors.cookArgumentError()); - return ormLU.getLoginUserById(code.login_user_id) - .then(user => { - if (user == null) - return Promise.reject(); - console.log("Updating user " + JSON.stringify(user) + - "'s password to " + parsed.password); - return ormLU.updateLoginUser({ - loginUserId : user.login_user_id, - isAdmin : user.is_admin, - isCoach : user.is_coach, - accountStatus : user?.account_status, - password : parsed.password - }); - }) - .then(user => { - console.log(JSON.stringify(user)); - return ormSK.addSessionKey(user.login_user_id, - util.generateKey()); - }) - .then(async key => { - return ormPR.deleteResetWithResetId(code.reset_id) - .then(() => Promise.resolve({sessionkey : key.session_key})); - }); - })); + return ormLU.getLoginUserById(code.login_user_id) + .then(user => { + if (user == null) + return Promise.reject(); + console.log("Updating user " + JSON.stringify(user) + + "'s password to " + parsed.password); + return ormLU.updateLoginUser({ + loginUserId: user.login_user_id, + isAdmin: user.is_admin, + isCoach: user.is_coach, + accountStatus: user?.account_status, + password: parsed.password + }); + }) + .then(user => { + console.log(JSON.stringify(user)); + return ormSK.addSessionKey(user.login_user_id, + util.generateKey()); + }) + .then(async key => { + return ormPR.deleteResetWithResetId(code.reset_id) + .then(() => Promise.resolve({sessionkey: key.session_key})); + }); + })); } export function getRouter(): express.Router { - const router: express.Router = express.Router(); + const router: express.Router = express.Router(); + + router.post('/', + (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); + router.get('/:id', + (req, res) => util.respOrErrorNoReinject(res, checkCode(req))); + router.post("/:id", (req, res) => + util.respOrErrorNoReinject(res, resetPassword(req))); + + util.addAllInvalidVerbs(router, ["/", "/:id"]); - router.post('/', - (req, res) => util.respOrErrorNoReinject(res, requestReset(req))); - router.get('/:id', - (req, res) => util.respOrErrorNoReinject(res, checkCode(req))); - router.post("/:id", (req, res) => - util.respOrErrorNoReinject(res, resetPassword(req))); + return router; +} - util.addAllInvalidVerbs(router, [ "/", "/:id" ]); +/** + * Returns the html body for the email with the reset code applied. + * @param resetID The ID that validates the password reset. + */ +function createEmail(resetID: string) { + return ` + + + Selections - Password Reset + + + + + + - return router; + + + + + + + + + + + + + + + + + + +
+ + Selections
You have requested a password reset for your OSOC Selections account. + Please click the link below to reset your password.
Note: This link is only valid for 24 hours.
Reset Password
If you believe that the password reset was not requested by you, please contact us as soon as possibe at osoc2.be@gmail.com
+ + + ` } From d3f6a816f0451e93adad032742f6db2d321dc1f0 Mon Sep 17 00:00:00 2001 From: Mouwrice Date: Tue, 5 Apr 2022 12:23:00 +0200 Subject: [PATCH 150/827] fix: some little styling improvements --- backend/routes/reset.ts | 14 +++++++------- frontend/components/Header/Header.module.css | 4 ++++ frontend/pages/reset/[pid].tsx | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/backend/routes/reset.ts b/backend/routes/reset.ts index 1b0ed62e..e78c2630 100644 --- a/backend/routes/reset.ts +++ b/backend/routes/reset.ts @@ -184,29 +184,29 @@ function createEmail(resetID: string) { - Selections + Selections - You have requested a password reset for your OSOC Selections account. + You have requested a password reset for your OSOC Selections account. Please click the link below to reset your password. - Note: This link is only valid for 24 hours. + Note: This link is only valid for 24 hours. - Reset Password - If you believe that the password reset was not requested by you, please contact us as soon as possibe at If you believe that the password reset was not requested by you, please contact us as soon as possibe at osoc2.be@gmail.com diff --git a/frontend/components/Header/Header.module.css b/frontend/components/Header/Header.module.css index 671557fd..a5bf2eaa 100644 --- a/frontend/components/Header/Header.module.css +++ b/frontend/components/Header/Header.module.css @@ -58,6 +58,10 @@ background-color: var(--neutral-200); } +.links button { + margin-left: auto; +} + .displayNone { display: none !important; } diff --git a/frontend/pages/reset/[pid].tsx b/frontend/pages/reset/[pid].tsx index 1ed6618c..49047bcb 100644 --- a/frontend/pages/reset/[pid].tsx +++ b/frontend/pages/reset/[pid].tsx @@ -86,7 +86,7 @@ const Pid: NextPage = () => { return <>

-
+

Reset Password