diff --git a/client/config/webpack.dev.js b/client/config/webpack.dev.js index c79eeaa..3bfa20a 100644 --- a/client/config/webpack.dev.js +++ b/client/config/webpack.dev.js @@ -8,9 +8,18 @@ const common = require('./webpack.common.js') module.exports = merge(common, { // Set the mode to development or production mode: 'development', - watch: true, - watchOptions: { - ignored: '**/node_modules/', + // watch: true, + // watchOptions: { + // ignored: '**/node_modules/', + // }, + + devServer: { + // watchOptions: { + // ignored: '**/node_modules/' + // }, + client: { + webSocketURL: 'auto://0.0.0.0:0/ws' + }, }, // Control how source maps are generated diff --git a/client/package.json b/client/package.json index 9c9e788..4212c53 100644 --- a/client/package.json +++ b/client/package.json @@ -3,6 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@mui/base": "^5.0.0-beta.8", + "@mui/material": "^5.14.2", "@types/node": "^16.18.39", "@types/react": "^18.2.16", "@types/react-dom": "^18.2.7", @@ -10,6 +14,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "sass": "^1.64.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, @@ -26,7 +31,10 @@ ] }, "devDependencies": { + "css-loader": "^6.8.1", "html-webpack-plugin": "^5.5.3", + "sass-loader": "^13.3.2", + "style-loader": "^3.3.3", "ts-loader": "^9.4.4", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", diff --git a/client/src/App.tsx b/client/src/App.tsx index a53698a..ebeb59d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,26 +1,31 @@ import React from 'react'; import logo from './logo.svg'; -import './App.css'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import { yellow} from "@mui/material/colors"; +import './sass/index.scss'; + +const theme = createTheme({ + palette:{ + primary: yellow, + mode: "dark" + } +}) + +import ModePanel from "./components/modePanel"; function App() { return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
+ +
+
+
+ ChatBridge +
+ +
+
+
); } -export default App; +export default App; \ No newline at end of file diff --git a/client/src/components/groups/groupPanel.tsx b/client/src/components/groups/groupPanel.tsx new file mode 100644 index 0000000..c13230a --- /dev/null +++ b/client/src/components/groups/groupPanel.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import {useState, useEffect} from "react"; + +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableRow from "@mui/material/TableRow"; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import Typography from "@mui/material/Typography"; + +import GroupRow from "./groupRow"; + +export default function GroupPanel({ + groups +}){ + + return( +
+ + + + + Group + Created At + Invite Token + + + + {groups ? groups.map((group) => ( + + )) : undefined} + +
+
+
+ ) +} \ No newline at end of file diff --git a/client/src/components/groups/groupRow.tsx b/client/src/components/groups/groupRow.tsx new file mode 100644 index 0000000..faf5ac2 --- /dev/null +++ b/client/src/components/groups/groupRow.tsx @@ -0,0 +1,29 @@ +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; + +export interface GroupRowProps { + name: string; + id: string; + invite_token: string; + created_at: string; +} + +export default function GroupRow( + {name, id, invite_token, created_at}: GroupRowProps +){ + return( + + + {name} + + {created_at} + {invite_token} + + ) + +} \ No newline at end of file diff --git a/client/src/components/modePanel.tsx b/client/src/components/modePanel.tsx new file mode 100644 index 0000000..05e9fba --- /dev/null +++ b/client/src/components/modePanel.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; + +import Tab from '@mui/material/Tab'; +import TabsList from '@mui/base/TabsList'; +import Tabs from '@mui/material/Tabs'; +import Box from "@mui/material/Box"; +import { styled } from '@mui/material/styles'; + +import ManagePanel from "./panels/managePanel"; +import JoinPanel from "./panels/joinPanel"; +import TabPanel from "./tabPanel"; + +const StyledTabs = styled(Tabs)({ + '& .MuiTabs-indicator': { + backgroundColor: "#DED03A" + } +}) + +export default function ModePanel() { + const [value, setValue] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + + return ( + + + {/**/} + + + {/**/} + + + + + + + ); +} diff --git a/client/src/components/panels/joinPanel.tsx b/client/src/components/panels/joinPanel.tsx new file mode 100644 index 0000000..14d438e --- /dev/null +++ b/client/src/components/panels/joinPanel.tsx @@ -0,0 +1,9 @@ + + +export default function JoinPanel(){ + return( +
+ +
+ ) +} \ No newline at end of file diff --git a/client/src/components/panels/managePanel.tsx b/client/src/components/panels/managePanel.tsx new file mode 100644 index 0000000..9020e8c --- /dev/null +++ b/client/src/components/panels/managePanel.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import {useState, useEffect} from "react"; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; + +import GroupPanel from "../groups/groupPanel"; + +export default function ManagePanel(){ + const [loggedIn, setLoggedIn] = useState(false); + const [authError, setAuthError] = useState(false); + const [errorText, setErrorText] = useState(''); + const [text, setText] = useState(''); + const [groups, setGroups] = useState([]); + + + const submitToken = (login_type) => { + fetch("api/auth", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + token: text + }) + }).then( + result => result.json() + ).then( + response => { + + if (response.status !== "success"){ + if (login_type === 'initial') { + // Do nothing, we're just checking + return + } + setAuthError(true) + setLoggedIn(false) + setErrorText(response.message) + console.log('failed') + } else if (response.status === "success") { + setLoggedIn(true) + setAuthError(false) + setErrorText('') + console.log('succeeded') + } + console.log(response) + } + ) + } + + const fetchGroups = () => { + fetch('api/groups') + .then(res => res.json()) + .then(res => { + console.log('fetched groups', res) + if (res.data !== undefined) { + setGroups(res.data) + } + }) + } + + const createGroup = () => { + fetch("api/groups", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + name: text, + enable: true + }) + }) + .then(req => req.json()) + .then(req => { + console.log(req) + if (req.status === "success"){ + setAuthError(false); + setErrorText(''); + setGroups([...groups, req.data]) + } else { + setAuthError(true); + setErrorText(req.message); + } + }) + } + + const handleClick = () => { + if (!loggedIn){ + submitToken(''); + } else { + createGroup(); + } + } + + + const textChanged = (event: React.ChangeEvent) => { + setText(event.target.value) + } + + // Call auth once when loaded to check session cookie + useEffect(() => { + submitToken('initial') + }, []) + + // Fetch groups if we're logged in + useEffect(() => { + if (loggedIn) { + console.log('fetching groups') + fetchGroups() + } + }, [loggedIn]) + + return( +
+
+ + +
+ { + loggedIn ? + + : + null + } +
+ ) +} \ No newline at end of file diff --git a/client/src/components/tabPanel.tsx b/client/src/components/tabPanel.tsx new file mode 100644 index 0000000..af3c6aa --- /dev/null +++ b/client/src/components/tabPanel.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; + +export default function TabPanel(props) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.number.isRequired, + value: PropTypes.number.isRequired, +}; \ No newline at end of file diff --git a/client/src/index.tsx b/client/src/index.tsx index 032464f..5d6db87 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,8 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; +import './sass/index.scss'; import App from './App'; -import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement @@ -13,7 +12,3 @@ root.render( ); -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/client/src/react-app-env.d.ts b/client/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/client/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/client/src/reportWebVitals.ts b/client/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16..0000000 --- a/client/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/client/src/App.css b/client/src/sass/App.scss similarity index 55% rename from client/src/App.css rename to client/src/sass/App.scss index 74b5e05..e7d8353 100644 --- a/client/src/App.css +++ b/client/src/sass/App.scss @@ -1,7 +1,31 @@ .App { - text-align: center; + //text-align: center; + background-color: $color-background; + //width: 100%; + //height: 100%; + //margin: { + // top: 5%; + // left: 10%; + // right: 10%; + // bottom: 5%; + //} + border: 2px solid $color-primary; + height:100%; } +.App-Container { + padding: { + top: 5%; + left: 10%; + right: 10%; + bottom: 5%; + } + width:100%; + height:100%; + box-sizing: border-box; +} + + .App-logo { height: 40vmin; pointer-events: none; @@ -14,8 +38,10 @@ } .App-header { - background-color: #282c34; - min-height: 100vh; + text-align: center; + margin: { + top: 1em; + } display: flex; flex-direction: column; align-items: center; diff --git a/client/src/index.css b/client/src/sass/base.scss similarity index 57% rename from client/src/index.css rename to client/src/sass/base.scss index ec2585e..3388e7e 100644 --- a/client/src/index.css +++ b/client/src/sass/base.scss @@ -1,13 +1,20 @@ body { + background-color: $color-background; margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } +#root { + width:100%; + height:100%; + position:absolute; +} + code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + monospace; } diff --git a/client/src/sass/index.scss b/client/src/sass/index.scss new file mode 100644 index 0000000..d766cdb --- /dev/null +++ b/client/src/sass/index.scss @@ -0,0 +1,7 @@ +@charset "utf-8"; + +@import './variables.scss'; +@import './base.scss'; +@import './App.scss'; +@import './tabs.scss'; +@import './input.scss'; \ No newline at end of file diff --git a/client/src/sass/input.scss b/client/src/sass/input.scss new file mode 100644 index 0000000..22501ef --- /dev/null +++ b/client/src/sass/input.scss @@ -0,0 +1,50 @@ +.InputSlot { + min-width: 320px; + width: 100%; + padding: 8px 12px; + box-sizing: border-box; +} + +.Panel .Input { + width: 100%; +} + +.InputGroup { + display: flex; + flex-direction: row; + gap: 2em; + align-items: flex-start; + + + + .Input { + flex-basis: 70%; + } + + .MuiButton-root { + flex-basis: 30%; + flex-grow:0; + height: 56px; + } + +} + +//.MuiButton-root { +// font-family: IBM Plex Sans, sans-serif; +// font-weight: 600; +// font-size: 0.875rem; +// line-height: 1.5; +// background-color: $color-primary; +// padding: 8px 16px; +// border-radius: 8px; +// color: black; +// transition: all 150ms ease; +// cursor: pointer; +// border: none; +// box-shadow: 0px 4px 30px black; +// +// &:hover{ +// background-color: $color-primary-darker; +// } +// +//} \ No newline at end of file diff --git a/client/src/sass/tabs.scss b/client/src/sass/tabs.scss new file mode 100644 index 0000000..b0f9052 --- /dev/null +++ b/client/src/sass/tabs.scss @@ -0,0 +1,45 @@ + +.ModePanel { + max-width: 80%; + margin: auto; + + .TabsList { + min-width: 400px; + background-color: transparent; + border-radius: 12px; + margin: { + top: 12px; + bottom: 12px; + left: auto; + right: auto; + } + border: 5px solid $color-primary; + display: flex; + align-items: center; + justify-content: center; + align-content: space-between; + + .Tab { + font-family: 'IBM Plex Sans', sans-serif; + color: white; + cursor: pointer; + font-size: 1.5rem; + font-weight: bold; + background-color: transparent; + width: 100%; + line-height: 1.5; + padding: 8px 12px; + margin: 6px; + border-radius: 8px; + display: flex; + justify-content: center; + border: none; + } + + .Tab.Mui-selected { + background-color: $color-primary; + color: $color-background; + } + + } +} \ No newline at end of file diff --git a/client/src/sass/variables.scss b/client/src/sass/variables.scss new file mode 100644 index 0000000..53cc0bf --- /dev/null +++ b/client/src/sass/variables.scss @@ -0,0 +1,4 @@ +$color-primary: #DED03A; +$color-primary-darker: scale-color($color-primary, $lightness: -10%); +$color-secondary: #A167A5; +$color-background: #1C1B22; \ No newline at end of file diff --git a/client/src/setupTests.ts b/client/src/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/client/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/client/src/types.d.ts b/client/src/types.d.ts new file mode 100644 index 0000000..b623f68 --- /dev/null +++ b/client/src/types.d.ts @@ -0,0 +1,5 @@ + +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/client/tsconfig.json b/client/tsconfig.json index 84cef4f..6e118eb 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -17,6 +17,6 @@ "esModuleInterop": true }, "include": [ - "src" + "src", "src/types.d.ts" ] } diff --git a/server/config/custom-environment-variables.ts b/server/config/custom-environment-variables.ts index 13c0a50..66809a3 100755 --- a/server/config/custom-environment-variables.ts +++ b/server/config/custom-environment-variables.ts @@ -12,5 +12,9 @@ export default { client_secret: 'SLACK_CLIENT_SECRET', signing_secret: 'SLACK_SIGNING_SECRET' }, - admin_token: 'ADMIN_TOKEN' + admin_token: 'ADMIN_TOKEN', + cookies:{ + key1: 'COOKIE_KEY_1', + key2: 'COOKIE_KEY_2' + } } \ No newline at end of file diff --git a/server/package.json b/server/package.json index 65e3ae4..dcc5a6c 100644 --- a/server/package.json +++ b/server/package.json @@ -18,6 +18,7 @@ "dependencies": { "@slack/oauth": "^2.6.1", "config": "^3.3.9", + "cookie-session": "^2.0.0", "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", @@ -34,6 +35,7 @@ }, "devDependencies": { "@types/config": "^3.3.0", + "@types/cookie-session": "^2.0.44", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.2", diff --git a/server/src/app.ts b/server/src/app.ts index 727a282..509b646 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -7,8 +7,10 @@ import cors from 'cors'; import { AppDataSource } from './db/data-source'; import AppError from './errors/appError'; +import {cookieMiddleware} from "./middleware/cookies"; import groupRoutes from "./routes/group.routes"; import slackRoutes from "./routes/slack.routes"; +import authRoutes from "./routes/auth.routes"; @@ -17,6 +19,7 @@ AppDataSource.initialize() const app = express(); app.use(express.json({limit: "10kb"})); + app.use(cookieMiddleware); app.get('/healthcheck', async (_, res: Response) => { res.status(200).json({ @@ -25,6 +28,7 @@ AppDataSource.initialize() }) app.use('/groups', groupRoutes); + app.use('/auth', authRoutes) // app.use('/slack', async (req:Request, res:Response) => { // console.log(req) diff --git a/server/src/auth.ts b/server/src/auth.ts new file mode 100644 index 0000000..f1056c7 --- /dev/null +++ b/server/src/auth.ts @@ -0,0 +1,20 @@ +import config from 'config'; +import {createHash} from "crypto"; +import AppError from "./errors/appError"; + +export const tokenHasher = (token:string) => { + if (token === undefined){ + return '' + } + + let hash = createHash('blake2b512'); + hash.update(token); + return hash.digest('hex'); +} + +const admin_token = config.get('admin_token'); +export const hashed_token = tokenHasher(admin_token) + +if (hashed_token === ""){ + throw new Error('Hashed admin token cannot be empty') +} \ No newline at end of file diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts new file mode 100644 index 0000000..b03f519 --- /dev/null +++ b/server/src/controllers/auth.controller.ts @@ -0,0 +1,32 @@ +import {NextFunction, Request, Response} from 'express'; +import {CheckAdminInput} from "../schemas/auth.schema"; +import { hashed_token, tokenHasher} from "../auth"; + + + + +export const checkAdminToken = async( + req: Request<{},{},CheckAdminInput>, + res: Response +) => { + let hashed_user_token = tokenHasher(req.body?.token) + + if (hashed_user_token === hashed_token || + req.session?.hashed_token === hashed_token + ){ + req.session.hashed_token = hashed_token; + console.log("Logged in with admin token") + res.status(200).json({ + status: 'success', + message: "Correct admin token!" + }); + } else { + req.session.logged_in = false; + + console.log("Login with admin token failed") + return res.status(403).json({ + status:'fail', + message: "Incorrect admin token!" + }) + } +} diff --git a/server/src/controllers/group.controller.ts b/server/src/controllers/group.controller.ts index 5bc30fe..99ac368 100644 --- a/server/src/controllers/group.controller.ts +++ b/server/src/controllers/group.controller.ts @@ -3,27 +3,37 @@ import {CreateGroupInput, GetGroupInput, getGroupSchema} from "../schemas/group. import config from 'config'; import {Group} from "../entities/group.entity"; import AppError from "../errors/appError"; +import {randomBytes} from "crypto"; import {AppDataSource} from "../db/data-source"; +import {hashed_token} from "../auth"; + const groupRepository = AppDataSource.getRepository(Group) export const createGroupHandler = async( req: Request<{}, {}, CreateGroupInput>, res: Response ) => { - console.log(req.body); - const admin_token = config.get('admin_token'); - if (req.body.token !== admin_token){ - return res.status(403).json({ - status: 'fail', - message: 'Not authorized to create group without correct admin token' - }) - } + // console.log(req.body); + // const admin_token = config.get('admin_token'); + // if (req.body?.token !== admin_token && req.session.hashed_token !== hashed_token){ + // return res.status(403).json({ + // status: 'fail', + // message: 'Not authorized to create group without correct admin token' + // }) + // } - let group = await groupRepository.create({...req.body}) + // Make new invite token as a random UUID + // let invite_token = randomUUID(); + // doesn't need to be as long as that... + let invite_token = randomBytes(8).toString('hex'); + + let group = await groupRepository.create({...req.body, invite_token}) let result = await groupRepository.save(group) - return res.send(result) + console.log("Group successfully created") + console.log(result) + return res.send({"status": "success", "data": result}) } @@ -33,20 +43,39 @@ export const getGroupHandler = async( next: NextFunction ) => { try { - let group = await groupRepository.findOneBy({name: req.params.name}) - if (!group) { - return next(new AppError(404, "No group with matching name exists")) + let query = req.body.name ?? req.query.name ?? undefined + let group; + if (query){ + group = await groupRepository.findBy({name: query}) + } else { + group = await groupRepository.find() } + console.log(group); + if (group.length === 0) { + return res.status(404).json({ + status: 'failed', + message: `No group with matching name exists. given name: ${query}` + }) + } + + console.log("Groups retrieved", group); return res.status(200).json({ status: 'success', - data: { - group - } + data: [ + ...group + ] }) } catch (err: any) { next(err); } -}; \ No newline at end of file +}; + +export const getGroupsHandler = async( + req: Request, + res: Response +) => { + +} \ No newline at end of file diff --git a/server/src/db/data-source.ts b/server/src/db/data-source.ts index 3d56e21..340eca0 100644 --- a/server/src/db/data-source.ts +++ b/server/src/db/data-source.ts @@ -16,7 +16,8 @@ export const AppDataSource = new DataSource({ type: 'postgres', synchronize: true, logging: false, - entities: ['server/entities/**/*.entity{.ts,.js}'], - migrations: ['server/migrations/**/*{.ts,.js}'], + // dropSchema: true, + entities: ['src/entities/**/*.entity{.ts,.js}'], + // migrations: ['src/migrations/**/*{.ts,.js}'], // subscribers: ['server/subscribers/**/*{.ts,.js}'], }); diff --git a/server/src/entities/group.entity.ts b/server/src/entities/group.entity.ts index efaf032..32c4304 100644 --- a/server/src/entities/group.entity.ts +++ b/server/src/entities/group.entity.ts @@ -14,6 +14,9 @@ export class Group extends Model { }) enable: boolean; + @Column() + invite_token: string; + @OneToMany(() => Channel, (channel) => channel.bridge) channels: Channel[] diff --git a/server/src/index.d.ts b/server/src/index.d.ts new file mode 100644 index 0000000..aeda724 --- /dev/null +++ b/server/src/index.d.ts @@ -0,0 +1,7 @@ +declare global { + namespace Express { + interface Session { + logged_in?: boolean + } + } +} \ No newline at end of file diff --git a/server/src/middleware/cookies.ts b/server/src/middleware/cookies.ts new file mode 100644 index 0000000..f4d67b4 --- /dev/null +++ b/server/src/middleware/cookies.ts @@ -0,0 +1,35 @@ +import cookieSession from 'cookie-session'; +import config from 'config'; +import {Request, Response, NextFunction} from "express"; +import { tokenHasher, hashed_token } from "../auth"; + +const cookieConfig = config.get<{ + 'key1': string, + "key2": string +}>('cookies') + + + +export const cookieMiddleware = cookieSession({ + name: 'session', + keys: [cookieConfig.key1, cookieConfig.key2], + signed: true, + +}) + +export const requireAdmin = (req: Request, res: Response, next: NextFunction) => { + if (req.session.hashed_token === hashed_token){ + next(); + } else { + let token_input = req.params.token ?? req.body.token ?? req.query.token + let hashed_user_token = tokenHasher(token_input); + if (hashed_user_token === hashed_token) { + next(); + } else{ + return res.status(403).json({ + status: 'fail', + message: 'this action requires you to be logged in as an administrator' + }); + } + } +}; diff --git a/server/src/routes/auth.routes.ts b/server/src/routes/auth.routes.ts new file mode 100644 index 0000000..ca31080 --- /dev/null +++ b/server/src/routes/auth.routes.ts @@ -0,0 +1,13 @@ +import express from 'express'; + +import { + checkAdminToken +} from "../controllers/auth.controller"; + +const router = express.Router(); + +router + .route('/') + .post(checkAdminToken) + +export default router diff --git a/server/src/routes/group.routes.ts b/server/src/routes/group.routes.ts index 9c77e33..0b03476 100644 --- a/server/src/routes/group.routes.ts +++ b/server/src/routes/group.routes.ts @@ -10,12 +10,13 @@ import { } from "../schemas/group.schema"; import { validate } from "../middleware/validate"; +import { requireAdmin } from "../middleware/cookies"; const router = express.Router(); router .route('/') - .post(createGroupHandler) - .get(getGroupHandler) + .post(validate(createGroupSchema), requireAdmin, createGroupHandler) + .get(requireAdmin, getGroupHandler) export default router diff --git a/server/src/schemas/auth.schema.ts b/server/src/schemas/auth.schema.ts new file mode 100644 index 0000000..0b01e6f --- /dev/null +++ b/server/src/schemas/auth.schema.ts @@ -0,0 +1,13 @@ +import { object, string, TypeOf } from 'zod'; + +export const checkAdminSchema = object({ + body: object({ + token: string({ + required_error: "Administration token required to log in!" + }) + }) +}) + + + +export type CheckAdminInput = TypeOf['body']; \ No newline at end of file diff --git a/server/src/schemas/group.schema.ts b/server/src/schemas/group.schema.ts index 5f59b1b..2384738 100644 --- a/server/src/schemas/group.schema.ts +++ b/server/src/schemas/group.schema.ts @@ -5,9 +5,9 @@ export const createGroupSchema = object({ name: string({ required_error: "Name for group required" }), - token: string({ - required_error: "Administration token required for creating groups" - }) + // token: string({ + // required_error: "Administration token required for creating groups" + // }) }) }) diff --git a/server/tsconfig.json b/server/tsconfig.json index 158a58a..774daf9 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -8,12 +8,13 @@ "forceConsistentCasingInFileNames": true, "strict": true, "strictPropertyInitialization": false, + "strictNullChecks": false, "skipLibCheck": true, "outDir": "./build", "rootDir": ".", }, - "include": ["server/**/*"], + "include": ["src/**/*"], "exclude": [ "../node_modules", "roles"] } diff --git a/yarn.lock b/yarn.lock index eab8fac..75eb701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -168,7 +168,7 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.22.5": +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== @@ -1110,7 +1110,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== @@ -1279,6 +1279,113 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" + integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + +"@emotion/is-prop-valid@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" + integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== + dependencies: + "@emotion/memoize" "^0.8.1" + +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/react@^11.11.1": + version "11.11.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" + integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" + integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" + integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== + +"@emotion/styled@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346" + integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== + +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1619,6 +1726,92 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@mui/base@5.0.0-beta.8", "@mui/base@^5.0.0-beta.8": + version "5.0.0-beta.8" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.8.tgz#a0a9531ae9147be92d17e4f0e3b9accc57916841" + integrity sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g== + dependencies: + "@babel/runtime" "^7.22.6" + "@emotion/is-prop-valid" "^1.2.1" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.14.1" + "@popperjs/core" "^2.11.8" + clsx "^1.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/core-downloads-tracker@^5.14.2": + version "5.14.2" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.2.tgz#d8fcacdb1d37e621fce33ea808180fa5a590f908" + integrity sha512-x+c/MgDL1t/IIy5lDbMlrDouFG5DYZbl3DP4dbbuhlpPFBnE9glYwmJEee/orVHQpOPwLxCAIWQs+2DKSaBVWQ== + +"@mui/material@^5.14.2": + version "5.14.2" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.2.tgz#13b113489a61021145d62e0383912ca487a46375" + integrity sha512-TgNR4/YRL11RifsnMWNhITNCkGJYVz20SCvVJBBoU5Y/KhUNSSJxjDpEB8VrnY+sUsV0NigLCkHZJglfsiS3Pw== + dependencies: + "@babel/runtime" "^7.22.6" + "@mui/base" "5.0.0-beta.8" + "@mui/core-downloads-tracker" "^5.14.2" + "@mui/system" "^5.14.1" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.14.1" + "@types/react-transition-group" "^4.4.6" + clsx "^1.2.1" + csstype "^3.1.2" + prop-types "^15.8.1" + react-is "^18.2.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.13.7": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099" + integrity sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA== + dependencies: + "@babel/runtime" "^7.22.5" + "@mui/utils" "^5.13.7" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.13.2": + version "5.13.2" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.13.2.tgz#c87bd61c0ab8086d34828b6defe97c02bcd642ef" + integrity sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw== + dependencies: + "@babel/runtime" "^7.21.0" + "@emotion/cache" "^11.11.0" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/system@^5.14.1": + version "5.14.1" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.1.tgz#ec8ae69f63963b5916dad4bca2f8a86a001a2392" + integrity sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA== + dependencies: + "@babel/runtime" "^7.22.6" + "@mui/private-theming" "^5.13.7" + "@mui/styled-engine" "^5.13.2" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.14.1" + clsx "^1.2.1" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/types@^7.2.4": + version "7.2.4" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" + integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== + +"@mui/utils@^5.13.7", "@mui/utils@^5.14.1": + version "5.14.1" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.1.tgz#29696371016552a6eb3af975bc7af429ec88b29a" + integrity sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw== + dependencies: + "@babel/runtime" "^7.22.6" + "@types/prop-types" "^15.7.5" + "@types/react-is" "^18.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -1662,6 +1855,11 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@redis/bloom@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" @@ -2014,6 +2212,14 @@ dependencies: "@types/node" "*" +"@types/cookie-session@^2.0.44": + version "2.0.44" + resolved "https://registry.yarnpkg.com/@types/cookie-session/-/cookie-session-2.0.44.tgz#86f3810bf19716ff716e08f877ce40cee9062267" + integrity sha512-3DheOZ41pql6raSIkqEPphJdhA2dX2bkS+s2Qacv8YMKkoCbAIEXbsDil7351ARzMqvfyDUGNeHGiRZveIzhqQ== + dependencies: + "@types/express" "*" + "@types/keygrip" "*" + "@types/cors@^2.8.13": version "2.8.13" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" @@ -2141,6 +2347,11 @@ dependencies: "@types/node" "*" +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -2171,7 +2382,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== -"@types/prop-types@*": +"@types/prop-types@*", "@types/prop-types@^15.7.5": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== @@ -2203,6 +2414,20 @@ dependencies: "@types/react" "*" +"@types/react-is@^18.2.1": + version "18.2.1" + resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.1.tgz#61d01c2a6fc089a53520c0b66996d458fdc46863" + integrity sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw== + dependencies: + "@types/react" "*" + +"@types/react-transition-group@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e" + integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.2.16": version "18.2.16" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.16.tgz#403dda0e933caccac9efde569923239ac426786c" @@ -3294,7 +3519,7 @@ check-types@^11.1.1: resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.2.tgz#7afc0b6a860d686885062f2dba888ba5710335b4" integrity sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA== -chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.3: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3370,6 +3595,11 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + cluster-key-slot@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" @@ -3552,11 +3782,21 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +cookie-session@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cookie-session/-/cookie-session-2.0.0.tgz#d07aa27822f43619e4342df1342268c849833089" + integrity sha512-hKvgoThbw00zQOleSlUr2qpvuNweoqBtxrmx0UFosx6AGi9lYtLoA+RbsvknrEX8Pr6MDbdWAb2j6SnMn+lPsg== + dependencies: + cookies "0.8.0" + debug "3.2.7" + on-headers "~1.0.2" + safe-buffer "5.2.1" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -3567,6 +3807,14 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookies@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + core-js-compat@^3.31.0: version "3.31.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" @@ -3668,7 +3916,7 @@ css-has-pseudo@^3.0.4: dependencies: postcss-selector-parser "^6.0.9" -css-loader@^6.5.1: +css-loader@^6.5.1, css-loader@^6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== @@ -3834,7 +4082,7 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@^3.0.2: +csstype@^3.0.2, csstype@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== @@ -3867,6 +4115,13 @@ debug@2.6.9, debug@^2.6.0: dependencies: ms "2.0.0" +debug@3.2.7, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -3874,13 +4129,6 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - decimal.js@^10.2.1: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" @@ -3926,7 +4174,7 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -4029,6 +4277,14 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -4779,6 +5035,11 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -5186,6 +5447,13 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -5376,6 +5644,11 @@ immer@^9.0.7: resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== +immutable@^4.0.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.1.tgz#17988b356097ab0719e2f741d56f3ec6c317f9dc" + integrity sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A== + import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -6396,6 +6669,13 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -7938,7 +8218,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8174,7 +8454,7 @@ react-error-overlay@^6.0.11: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-is@^16.13.1: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8184,7 +8464,7 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -8249,6 +8529,16 @@ react-scripts@5.0.1: optionalDependencies: fsevents "^2.3.2" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -8573,6 +8863,22 @@ sass-loader@^12.3.0: klona "^2.0.4" neo-async "^2.6.2" +sass-loader@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.3.2.tgz#460022de27aec772480f03de17f5ba88fa7e18c6" + integrity sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg== + dependencies: + neo-async "^2.6.2" + +sass@^1.64.1: + version "1.64.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.64.1.tgz#6a46f6d68e0fa5ad90aa59ce025673ddaa8441cf" + integrity sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -8811,7 +9117,7 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.1, source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -8838,6 +9144,11 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + source-map@^0.7.3: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" @@ -9093,7 +9404,7 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -style-loader@^3.3.1: +style-loader@^3.3.1, style-loader@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== @@ -9106,6 +9417,11 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + sucrase@^3.32.0: version "3.34.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" @@ -9456,6 +9772,11 @@ tslib@^2.0.3, tslib@^2.1.0, tslib@^2.5.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"