diff --git a/leaky-ships/components/Gamefield/EventBar.tsx b/leaky-ships/components/Gamefield/EventBar.tsx index cc5f092..3805910 100644 --- a/leaky-ships/components/Gamefield/EventBar.tsx +++ b/leaky-ships/components/Gamefield/EventBar.tsx @@ -14,7 +14,7 @@ function EventBar({ { icon: "burger-menu", text: "Menu" }, { icon: "radar", text: "Radar scan", mode: 0, amount: 1 }, { icon: "torpedo", text: "Fire torpedo", mode: 1, amount: 1 }, - { icon: "scope", text: "Fire missile", mode: 2 }, + { icon: "scope", text: "Fire missile", mode: 3 }, { icon: "gear", text: "Settings" }, ] return ( diff --git a/leaky-ships/components/Lobby/LobbyFrame.tsx b/leaky-ships/components/Lobby/LobbyFrame.tsx index bc71f1d..04eb11c 100644 --- a/leaky-ships/components/Lobby/LobbyFrame.tsx +++ b/leaky-ships/components/Lobby/LobbyFrame.tsx @@ -5,7 +5,7 @@ import { Fragment, useContext, useEffect, useState } from "react" function LobbyFrame({ openSettings }: { openSettings: () => void }) { const [gameProps, setGameProps] = useContext(gameContext) - const [enemy, setEnemy] = useState(false) + const { enemy } = gameProps const [dots, setDots] = useState(1) useEffect(() => { @@ -28,18 +28,13 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
-

setEnemy((e) => !e)} - > - VS -

+

VS

{enemy ? ( - + ) : (

Warte auf Spieler 2 {Array.from(Array(dots), () => ".").join("")} diff --git a/leaky-ships/lib/backend/components/createTokenDB.ts b/leaky-ships/lib/backend/components/createTokenDB.ts index f81d194..d6dd7c7 100644 --- a/leaky-ships/lib/backend/components/createTokenDB.ts +++ b/leaky-ships/lib/backend/components/createTokenDB.ts @@ -11,7 +11,7 @@ export interface IdToken { type: TokenType } -const tokenLifetime = { +export const tokenLifetime = { REFRESH: 172800, ACCESS: 15, } diff --git a/leaky-ships/lib/backend/components/getPinFromBody.ts b/leaky-ships/lib/backend/components/getPinFromBody.ts index a674f45..dc75de3 100644 --- a/leaky-ships/lib/backend/components/getPinFromBody.ts +++ b/leaky-ships/lib/backend/components/getPinFromBody.ts @@ -1,4 +1,3 @@ -import { rejectionErrors } from "../errors" import sendError, { API } from "./sendError" async function getPinFromBody(context: API, next: (pin: string) => void) { @@ -9,7 +8,13 @@ async function getPinFromBody(context: API, next: (pin: string) => void) { !("pin" in body) || typeof body.pin !== "string" ) - return sendError(context, rejectionErrors.noUsername) + return sendError(context, { + rejected: true, + message: "No pin in request body!", + statusCode: 401, + solved: true, + type: "warn", + }) const { pin } = body return next(pin) diff --git a/leaky-ships/lib/backend/components/getTokenDB.ts b/leaky-ships/lib/backend/components/getTokenDB.ts index 17e4fc2..6303dfc 100644 --- a/leaky-ships/lib/backend/components/getTokenDB.ts +++ b/leaky-ships/lib/backend/components/getTokenDB.ts @@ -3,6 +3,7 @@ import { rejectionErrorFns, rejectionErrors } from "../errors" import type { IdToken } from "./createTokenDB" import sendError, { API } from "./sendError" import type { Token } from "@prisma/client" +import { deleteCookie } from "cookies-next" async function getTokenDB( context: API, @@ -17,7 +18,10 @@ async function getTokenDB( id, }, }) - if (!tokenDB) return sendError(context, rejectionErrorFns.tokenNotFound(type)) + if (!tokenDB) { + deleteCookie("token", { ...context, path: "/api" }) + return sendError(context, rejectionErrorFns.tokenNotFound(type)) + } if (tokenDB.used && !ignoreChecks) return sendError(context, rejectionErrors.tokenUsed) diff --git a/leaky-ships/lib/backend/components/getTokenFromBody.ts b/leaky-ships/lib/backend/components/getTokenFromBody.ts index 4ad43f2..a39a968 100644 --- a/leaky-ships/lib/backend/components/getTokenFromBody.ts +++ b/leaky-ships/lib/backend/components/getTokenFromBody.ts @@ -18,6 +18,7 @@ async function getTokenFromBody( const value = body.token return next({ value, type }) } + console.log(body) return sendError(context, rejectionErrorFns.noToken(type)) } diff --git a/leaky-ships/lib/backend/components/getTokenFromCookie.ts b/leaky-ships/lib/backend/components/getTokenFromCookie.ts index 9ccbe45..3ba7ce7 100644 --- a/leaky-ships/lib/backend/components/getTokenFromCookie.ts +++ b/leaky-ships/lib/backend/components/getTokenFromCookie.ts @@ -1,32 +1,24 @@ import createPlayerDB from "./createPlayerDB" import createTokenDB, { RawToken } from "./createTokenDB" import type { API } from "./sendError" -import { setCookie } from "cookies-next" +import { Player, Token } from "@prisma/client" async function getTokenFromCookie( context: API, - next: (refreshToken: RawToken) => void + next: ( + refreshToken: RawToken, + newPlayer?: { player: Player; newToken: RawToken; newTokenDB: Token } + ) => void ) { const type = "REFRESH" - const { req, res } = context - const value = req.cookies.token + const value = context.req.cookies.token // Checking for cookie presens, because it is necessary if (!value) { return createPlayerDB((player) => - createTokenDB(player, type, (newToken) => { - // Set login cookie - setCookie("token", newToken.value, { - req, - res, - maxAge: 172800, - httpOnly: true, - sameSite: true, - secure: true, - path: "/api", - }) - return next(newToken) - }) + createTokenDB(player, type, (newToken, newTokenDB) => + next(newToken, { player, newToken, newTokenDB }) + ) ) } return next({ value, type }) diff --git a/leaky-ships/lib/backend/components/sendResponse.ts b/leaky-ships/lib/backend/components/sendResponse.ts index a884de3..5525f4e 100644 --- a/leaky-ships/lib/backend/components/sendResponse.ts +++ b/leaky-ships/lib/backend/components/sendResponse.ts @@ -1,15 +1,39 @@ import logging, { Logging } from "../logging" +import { RawToken, tokenLifetime } from "./createTokenDB" import type { API } from "./sendError" +import { deleteCookie, setCookie } from "cookies-next" export interface Result { message: string statusCode?: number body?: T type?: Logging[] + cookie?: string } export default function sendResponse(context: API, result: Result) { const { req, res } = context + if (typeof result.cookie === "string") { + if (result.cookie) { + console.log(1) + setCookie("token", result.cookie, { + req, + res, + maxAge: tokenLifetime.REFRESH, + httpOnly: true, + sameSite: true, + secure: true, + path: "/api", + }) + } else { + console.log(2) + deleteCookie("token", { + req, + res, + path: "/api", + }) + } + } res.status(result.statusCode ?? 200) result.body ? res.json(result.body) : res.end() logging(result.message, result.type ?? ["debug"], req) diff --git a/leaky-ships/lib/backend/errors.ts b/leaky-ships/lib/backend/errors.ts index 6c101bf..641ac35 100644 --- a/leaky-ships/lib/backend/errors.ts +++ b/leaky-ships/lib/backend/errors.ts @@ -69,6 +69,12 @@ export const rejectionErrors: rejectionErrors = { solved: true, type: "warn", }, + gameNotFound: { + rejected: true, + message: "Game not found!", + statusCode: 403, + solved: true, + }, } export const rejectionErrorFns: rejectionErrorFns = { diff --git a/leaky-ships/lib/frontend/getAccessToken.ts b/leaky-ships/lib/frontend/getAccessToken.ts index 9e941b5..571b2bf 100644 --- a/leaky-ships/lib/frontend/getAccessToken.ts +++ b/leaky-ships/lib/frontend/getAccessToken.ts @@ -1,18 +1,42 @@ +import status from "http-status" +import { toast } from "react-toastify" +import { ZodError, z } from "zod" + +const tokenSchema = z.object({ + token: z.string(), +}) + +export function successfulResponse(res: Response) { + if (status[`${res.status}_CLASS`] === status.classes.SUCCESSFUL) + return res.json() + + const resStatus = status[`${res.status}_CLASS`] + if (typeof resStatus !== "string") return + + toast(status.classes[resStatus] + ": " + status[res.status], { + position: "top-center", + type: "error", + theme: "colored", + }) + return Promise.reject() +} + async function getAccessToken() { - const response = await fetch("/api/user/auth", { + return fetch("/api/user/auth", { method: "GET", }) - const res = await response.json() + .then(successfulResponse) + .then((response) => { + try { + const token = tokenSchema.parse(response) - if ( - typeof res === "object" && - res && - "token" in res && - typeof res.token === "string" - ) - return res.token - - throw new Error("Access token not found") + return token + } catch (err: any) { + const error = err as ZodError + toast(JSON.stringify(error)) + return Promise.reject() + } + }) } export default getAccessToken diff --git a/leaky-ships/package.json b/leaky-ships/package.json index 3949f52..7fe186a 100644 --- a/leaky-ships/package.json +++ b/leaky-ships/package.json @@ -25,11 +25,14 @@ "cookies-next": "^2.1.1", "eslint": "8.31.0", "eslint-config-next": "13.1.1", + "http-status": "^1.6.2", "jsonwebtoken": "^9.0.0", "next": "13.1.1", "prisma": "^4.12.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-otp-input": "^3.0.0", + "react-toastify": "^9.1.2", "socket.io": "^4.6.1", "socket.io-client": "^4.6.1", "typescript": "4.9.4", diff --git a/leaky-ships/pages/_app.tsx b/leaky-ships/pages/_app.tsx index 821c654..1bd28e7 100644 --- a/leaky-ships/pages/_app.tsx +++ b/leaky-ships/pages/_app.tsx @@ -4,11 +4,18 @@ import "../styles/grid2.scss" import "../styles/grid.scss" import type { AppProps } from "next/app" import { Dispatch, SetStateAction, createContext, useState } from "react" +import { ToastContainer } from "react-toastify" +import "react-toastify/dist/ReactToastify.css" interface gameContext { pin?: string game?: { - id: "" + id: string + } + player?: { + id: string + username?: string + isOwner?: boolean } enemy?: { id: string @@ -25,6 +32,7 @@ export default function App({ Component, pageProps }: AppProps) { return ( + ) } diff --git a/leaky-ships/pages/api/game/create.ts b/leaky-ships/pages/api/game/create.ts index f03b900..c024d73 100644 --- a/leaky-ships/pages/api/game/create.ts +++ b/leaky-ships/pages/api/game/create.ts @@ -2,18 +2,33 @@ import sendError from "@backend/components/sendError" import sendResponse from "@backend/components/sendResponse" import getPlayer from "@lib/backend/components/getPlayer" import prisma from "@lib/prisma" +import { Game, Gamepin, Player_Game } from "@prisma/client" import type { NextApiRequest, NextApiResponse } from "next" import { z } from "zod" -const returnSchema = z.object({ +export const createSchema = z.object({ game: z.object({ id: z.string(), createdAt: z.date(), updatedAt: z.date(), running: z.boolean(), }), + pin: z.string().optional(), + player: z.object({ + id: z.string(), + username: z.string().optional(), + isOwner: z.boolean().optional(), + }), + enemy: z + .object({ + id: z.string(), + username: z.string().optional(), + isOwner: z.boolean().optional(), + }) + .optional(), }) -type Data = z.infer + +type Data = z.infer export default async function create( req: NextApiRequest, @@ -26,16 +41,20 @@ export default async function create( const pin = Math.floor(Math.random() * 10000) .toString() .padStart(4, "0") - const game = await prisma.game.create({ - data: { - pin: { - create: { - pin, - }, - }, + + let created = false + let game: + | (Game & { + pin: Gamepin | null + players: Player_Game[] + }) + | null + + game = await prisma.game.findFirst({ + where: { + running: true, players: { - create: { - isOwner: true, + some: { playerId: player.id, }, }, @@ -45,10 +64,41 @@ export default async function create( players: true, }, }) + if (!game) { + created = true + game = await prisma.game.create({ + data: { + pin: { + create: { + pin, + }, + }, + players: { + create: { + isOwner: true, + playerId: player.id, + }, + }, + }, + include: { + pin: true, + players: true, + }, + }) + } return sendResponse(context, { message: `Player: ${player.id} created game: ${game.id}`, - body: { game }, + statusCode: created ? 201 : 200, + body: { + game, + pin: game.pin?.pin, + player: { + id: player.id, + username: player.username ?? undefined, + isOwner: true, + }, + }, type: ["debug", "infoCyan"], }) }).catch((err) => sendError(context, err)) diff --git a/leaky-ships/pages/api/game/join.ts b/leaky-ships/pages/api/game/join.ts index c2c63d0..2a8f431 100644 --- a/leaky-ships/pages/api/game/join.ts +++ b/leaky-ships/pages/api/game/join.ts @@ -2,6 +2,7 @@ import sendError from "@backend/components/sendError" import sendResponse from "@backend/components/sendResponse" import getPinFromBody from "@lib/backend/components/getPinFromBody" import getPlayer from "@lib/backend/components/getPlayer" +import { rejectionErrors } from "@lib/backend/errors" import prisma from "@lib/prisma" import type { Game } from "@prisma/client" import type { NextApiRequest, NextApiResponse } from "next" @@ -18,30 +19,61 @@ export default async function join( return getPinFromBody(context, (pin) => getPlayer(context, async (player) => { - const { game } = await prisma.gamepin.update({ - where: { - pin, - }, - data: { - game: { - update: { - players: { - create: { - isOwner: false, - playerId: player.id, + try { + const pinDB = await prisma.gamepin.update({ + where: { + pin, + }, + data: { + game: { + update: { + players: { + create: { + isOwner: false, + playerId: player.id, + }, }, }, }, }, - }, - include: { game: true }, - }) + include: { + game: { + include: { + players: { + include: { + player: true, + }, + }, + }, + }, + }, + }) - sendResponse(context, { - message: `Player: ${player.id} joined game: ${game.id}`, - body: { game }, - type: ["debug", "infoCyan"], - }) + const enemy = pinDB.game.players.find( + (enemy) => enemy.player.id !== player.id + ) + return sendResponse(context, { + message: `Player: ${player.id} joined game: ${pinDB.game.id}`, + body: { + game: pinDB.game, + pin: pinDB.pin, + player: { + id: player.id, + username: player.username ?? undefined, + isOwner: true, + }, + enemy: { + id: enemy?.player.id, + username: enemy?.player.username ?? undefined, + isOwner: false, + }, + }, + type: ["debug", "infoCyan"], + }) + } catch (err: any) { + console.log("HERE".red, err.code, err.meta, err.message) + return sendError(context, rejectionErrors.gameNotFound) + } }) ).catch((err) => sendError(context, err)) } diff --git a/leaky-ships/pages/api/user/auth.ts b/leaky-ships/pages/api/user/auth.ts index af98864..0e3c4ad 100644 --- a/leaky-ships/pages/api/user/auth.ts +++ b/leaky-ships/pages/api/user/auth.ts @@ -5,6 +5,7 @@ import getTokenDB from "@backend/components/getTokenDB" import getTokenFromCookie from "@backend/components/getTokenFromCookie" import sendError from "@backend/components/sendError" import sendResponse from "@backend/components/sendResponse" +import { Player, Token } from "@prisma/client" import type { NextApiRequest, NextApiResponse } from "next" interface Data { @@ -18,19 +19,24 @@ export default async function auth( const context = { req, res } const type = "ACCESS" - return getTokenFromCookie(context, (refreshToken) => - checkTokenIsValid(context, refreshToken, (token) => - getTokenDB(context, token, (tokenDB) => - getPlayerByIdDB(context, tokenDB, (player) => - createTokenDB(player, type, (newToken, newTokenDB) => - sendResponse(context, { - message: `Access-Token generated: ${newTokenDB.id} with Refreshtoken-Token: ${tokenDB.id}`, - body: { token: newToken.value }, - type: ["debug", "infoCyan"], - }) - ) + return getTokenFromCookie(context, (refreshToken, newPlayer) => { + const next = (player: Player, tokenDB: Token, cookie?: string) => + createTokenDB(player, type, (newToken, newTokenDB) => + sendResponse(context, { + message: `Access-Token generated: ${newTokenDB.id} with Refreshtoken-Token: ${tokenDB.id}`, + body: { token: newToken.value }, + type: ["debug", "infoCyan"], + cookie, + }) + ) + if (!newPlayer) { + return checkTokenIsValid(context, refreshToken, (token) => + getTokenDB(context, token, (tokenDB) => + getPlayerByIdDB(context, tokenDB, (player) => next(player, tokenDB)) ) ) - ) - ).catch((err) => sendError(context, err)) + } + const { player, newToken, newTokenDB } = newPlayer + return next(player, newTokenDB, newToken.value) + }).catch((err) => sendError(context, err)) } diff --git a/leaky-ships/pages/api/user/login.ts b/leaky-ships/pages/api/user/login.ts index 7546478..d1e1c7c 100644 --- a/leaky-ships/pages/api/user/login.ts +++ b/leaky-ships/pages/api/user/login.ts @@ -26,17 +26,6 @@ export default async function login( getPlayerByNameDB(context, username, (player) => { checkPasswordIsValid(context, player, password, () => { createTokenDB(player, "REFRESH", (newToken, newTokenDB) => { - // Set login cookie - setCookie("token", newToken.value, { - req, - res, - maxAge: 172800, - httpOnly: true, - sameSite: true, - secure: true, - path: "/api", - }) - sendResponse(context, { message: "User " + @@ -45,6 +34,7 @@ export default async function login( newTokenDB.id, body: { loggedIn: true }, type: ["debug", "infoCyan"], + cookie: newToken.value, }) }) }) diff --git a/leaky-ships/pages/api/user/logout.ts b/leaky-ships/pages/api/user/logout.ts index d479186..11cde18 100644 --- a/leaky-ships/pages/api/user/logout.ts +++ b/leaky-ships/pages/api/user/logout.ts @@ -3,7 +3,6 @@ import getTokenFromCookie from "@backend/components/getTokenFromCookie" import sendError from "@backend/components/sendError" import sendResponse from "@backend/components/sendResponse" import decodeToken from "@lib/backend/components/decodeToken" -import { deleteCookie } from "cookies-next" import type { NextApiRequest, NextApiResponse } from "next" interface Data { @@ -12,7 +11,7 @@ interface Data { export default async function logout( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse ) { const context = { req, res } @@ -22,13 +21,11 @@ export default async function logout( context, token, (tokenDB) => { - // Set login cookie - deleteCookie("token", { req, res }) - return sendResponse(context, { message: "User of Token " + tokenDB.id + " logged out.", body: { loggedOut: true }, type: ["debug", "infoCyan"], + cookie: "", }) }, true diff --git a/leaky-ships/pages/dev/index.tsx b/leaky-ships/pages/dev/index.tsx index 0519f32..49388c7 100644 --- a/leaky-ships/pages/dev/index.tsx +++ b/leaky-ships/pages/dev/index.tsx @@ -1,104 +1,222 @@ import BurgerMenu from "../../components/BurgerMenu" import Logo from "../../components/Logo" import OptionButton from "../../components/OptionButton" +import { gameContext } from "../_app" import { faEye, faLeftLong } from "@fortawesome/pro-regular-svg-icons" import { faPlus, faUserPlus } from "@fortawesome/pro-solid-svg-icons" import { faCirclePlay } from "@fortawesome/pro-thin-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import getAccessToken from "@lib/frontend/getAccessToken" +import getAccessToken, { + successfulResponse, +} from "@lib/frontend/getAccessToken" import { GetServerSideProps } from "next" import { useRouter } from "next/router" -import { useState } from "react" +import { useCallback, useContext, useEffect, useState } from "react" +import OtpInput from "react-otp-input" +import { toast } from "react-toastify" import { z } from "zod" interface Props { - start: boolean + q: string | string[] | undefined +} +function isInputOnlyNumbers(input: string) { + return /^\d+$/.test(input) } -export default function Home({ start }: Props) { +export default function Home({ q }: Props) { + const [otp, setOtp] = useState("") + const [gameProps, setGameProps] = useContext(gameContext) const router = useRouter() + + const gameFetch = useCallback( + async (pin?: string) => { + const createSchema = z.object({ + game: z.object({ + id: z.string(), + }), + pin: z.string().optional(), + player: z.object({ + id: z.string(), + username: z.string().optional(), + isOwner: z.boolean().optional(), + }), + enemy: z + .object({ + id: z.string(), + username: z.string().optional(), + isOwner: z.boolean().optional(), + }) + .optional(), + }) + const gamePromise = getAccessToken().then((token) => { + console.log(otp, { ...token, pin }) + return fetch("/api/game/" + (!pin ? "create" : "join"), { + method: "POST", + body: JSON.stringify({ ...token, pin: pin }), + }) + .then(successfulResponse) + .then((game) => createSchema.parse(game)) + }) + + const res = await toast.promise(gamePromise, { + pending: { + render: "Raum wird " + (!pin ? "erstellt" : "angefragt"), + }, + success: { + render: "Raum " + (!pin ? "erstellt" : "angefragt") + " 👌", + type: "info", + theme: "colored", + }, + error: { + render: "Es ist ein Fehler aufgetreten 🤯", + type: "error", + theme: "colored", + }, + }) + + setGameProps(res) + + await toast.promise(router.push("/dev/lobby"), { + pending: { + render: "Raum wird beigetreten", + }, + success: { + render: "Raum begetreten 👌", + type: "info", + }, + error: { + render: "Es ist ein Fehler aufgetreten 🤯", + type: "error", + }, + }) + }, + [router, setGameProps] + ) + + useEffect(() => { + if (otp.length !== 4) return + if (!isInputOnlyNumbers(otp)) { + toast("Der Code darf nur Zahlen beinhalten!", { + type: "warning", + theme: "dark", + }) + return + } + gameFetch(otp) + }, [otp]) + return (

- {!start ? ( - <> -
- -
- - - ) : ( -
- -
- { - const token = await getAccessToken() - const game = await fetch("/api/game/create", { - method: "POST", - body: JSON.stringify({ token }), - }) - const gameSchema = z.object({ - game: z.object({ - id: z.string(), - createdAt: z.date(), - updatedAt: z.date(), - running: z.boolean(), - }), - }) - - const check = gameSchema.safeParse(game) - - if (check.success) { - const gameData = check.data - console.log(gameData) - } else { - console.error(check.error) - } - - // const warst = result.game - - router.push("/dev/lobby") - }} - icon={faPlus} - > - Raum erstellen - - Raum beitreten - Zuschauen -
-
- )} + {(() => { + switch (q) { + case "join": + return ( +
+ +
+ Raum erstellen + { + router.push({ + pathname: router.pathname, + query: { q: "join" }, + }) + }} + icon={faUserPlus} + > + -} + renderInput={(props) => } + /> + + Zuschauen +
+
+ ) + case "start": + return ( +
+ +
+ gameFetch()} icon={faPlus}> + Raum erstellen + + { + router.push({ + pathname: router.pathname, + query: { q: "join" }, + }) + }} + icon={faUserPlus} + > + Raum beitreten + + Zuschauen +
+
+ ) + default: + return ( + <> +
+ +
+ + + ) + } + })()}
) @@ -107,10 +225,7 @@ export default function Home({ start }: Props) { export const getServerSideProps: GetServerSideProps = async ( context ) => { - const { start } = context.query + const { q } = context.query - // Convert the `start` query parameter to a boolean - const isStart = start === "true" - - return { props: { start: isStart } } + return { props: { q: q ? q : "" } } } diff --git a/leaky-ships/pnpm-lock.yaml b/leaky-ships/pnpm-lock.yaml index eea794c..e9c81c5 100644 --- a/leaky-ships/pnpm-lock.yaml +++ b/leaky-ships/pnpm-lock.yaml @@ -49,6 +49,9 @@ dependencies: eslint-config-next: specifier: 13.1.1 version: 13.1.1(eslint@8.31.0)(typescript@4.9.4) + http-status: + specifier: ^1.6.2 + version: 1.6.2 jsonwebtoken: specifier: ^9.0.0 version: 9.0.0 @@ -64,6 +67,12 @@ dependencies: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-otp-input: + specifier: ^3.0.0 + version: 3.0.0(react-dom@18.2.0)(react@18.2.0) + react-toastify: + specifier: ^9.1.2 + version: 9.1.2(react-dom@18.2.0)(react@18.2.0) socket.io: specifier: ^4.6.1 version: 4.6.1 @@ -1010,6 +1019,11 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false + /clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1920,6 +1934,11 @@ packages: dependencies: function-bind: 1.1.1 + /http-status@1.6.2: + resolution: {integrity: sha512-oUExvfNckrpTpDazph7kNG8sQi5au3BeTo0idaZFXEhTaJKu7GNJCLHI0rYY2wljm548MSTM+Ljj/c6anqu2zQ==} + engines: {node: '>= 0.4.0'} + dev: false + /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -2785,6 +2804,27 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: false + /react-otp-input@3.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-TgTE3PbHhu4VxsAA9JUACzhtxwuZ8OSle9kiwK0Ne7fCv7wOXTtIRWQWPoNlfOZ/sxGsXjMexqwrQdB9yy0qEQ==} + peerDependencies: + react: '>=16.8.6 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8.6 || ^17.0.0 || ^18.0.0' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-toastify@9.1.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PBfzXO5jMGEtdYR5jxrORlNZZe/EuOkwvwKijMatsZZm8IZwLj01YvobeJYNjFcA6uy6CVrx2fzL9GWbhWPTDA==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + clsx: 1.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'}