Using next-auth
This commit is contained in:
parent
b9e72b2a32
commit
dab3abdda2
50 changed files with 656 additions and 1525 deletions
|
@ -28,15 +28,15 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
<div className="flex items-center justify-around">
|
||||
<Player
|
||||
src="player_blue.png"
|
||||
text={gameProps.player?.username ?? "Spieler 1 (Du)"}
|
||||
text={gameProps.player?.name ?? "Spieler 1 (Du)"}
|
||||
primary={true}
|
||||
edit={true}
|
||||
/>
|
||||
<p className="font-farro m-4 text-6xl font-semibold">VS</p>
|
||||
{enemy ? (
|
||||
<Player src="player_red.png" text={enemy.username ?? "Spieler 2"} />
|
||||
<Player src="player_red.png" text={enemy.name ?? "Spieler 2"} />
|
||||
) : (
|
||||
<p className="font-farro w-96 text-center text-5xl font-medium">
|
||||
<p className="font-farro w-96 text-center text-4xl font-medium">
|
||||
Warte auf Spieler 2 {Array.from(Array(dots), () => ".").join("")}
|
||||
{Array.from(Array(3 - dots), (_, i) => (
|
||||
<Fragment key={i}> </Fragment>
|
||||
|
|
|
@ -2,21 +2,32 @@ import {
|
|||
FontAwesomeIcon,
|
||||
FontAwesomeIconProps,
|
||||
} from "@fortawesome/react-fontawesome"
|
||||
import classNames from "classnames"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
function OptionButton({
|
||||
icon,
|
||||
action,
|
||||
children,
|
||||
disabled,
|
||||
}: {
|
||||
icon: FontAwesomeIconProps["icon"]
|
||||
action?: () => void
|
||||
children: ReactNode
|
||||
disabled?: boolean
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
className="flex w-full flex-row items-center justify-between rounded-xl border-b-4 border-shield-gray bg-voidDark py-2 pl-8 pr-4 text-lg text-grayish duration-100 first:mt-4 last:mt-4 active:border-b-0 active:border-t-4 sm:py-4 sm:pl-16 sm:pr-8 sm:text-4xl sm:first:mt-8 sm:last:mt-8"
|
||||
className={classNames(
|
||||
"flex w-full flex-row items-center justify-between rounded-xl py-2 pl-8 pr-4 text-lg text-grayish duration-100 first:mt-4 last:mt-4 sm:py-4 sm:pl-16 sm:pr-8 sm:text-4xl sm:first:mt-8 sm:last:mt-8",
|
||||
{
|
||||
"bg-voidDark border-shield-gray border-b-4 active:border-b-0 active:border-t-4":
|
||||
!disabled,
|
||||
"bg-red-950 border-slate-600 border-4 border-dashed": disabled,
|
||||
}
|
||||
)}
|
||||
onClick={() => action && setTimeout(action, 200)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<span className="mx-auto">{children}</span>
|
||||
<FontAwesomeIcon
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { rejectionErrors } from "../errors"
|
||||
import sendError, { API } from "./sendError"
|
||||
import type { Player } from "@prisma/client"
|
||||
import bcrypt from "bcrypt"
|
||||
|
||||
export default async function checkPasswordIsValid<T>(
|
||||
context: API<T>,
|
||||
player: Player,
|
||||
password: string
|
||||
) {
|
||||
// Validate for correct password
|
||||
const result = await bcrypt.compare(password, player.passwordHash ?? "")
|
||||
if (!result) throw sendError(context, rejectionErrors.wrongPassword)
|
||||
|
||||
return
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import { rejectionErrorFns } from "../errors"
|
||||
import jwtVerifyCatch from "../jwtVerifyCatch"
|
||||
import type { IdToken, RawToken } from "./createTokenDB"
|
||||
import sendError, { API } from "./sendError"
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
async function checkTokenIsValid<T>(context: API<T>, rawToken: RawToken) {
|
||||
const { value, type } = rawToken
|
||||
|
||||
// Verify the token and get the payload
|
||||
let data: string | jwt.JwtPayload
|
||||
try {
|
||||
data = jwt.verify(value, process.env.TOKEN_SECRET as string)
|
||||
} catch (err: any) {
|
||||
// Deal with the problem in more detail
|
||||
throw sendError(context, jwtVerifyCatch(type, err))
|
||||
}
|
||||
// Making sure the token data is not a string (because it should be an object)
|
||||
if (typeof data === "string")
|
||||
throw sendError(context, rejectionErrorFns.tokenWasString(type, value))
|
||||
|
||||
return { id: data.id, type }
|
||||
}
|
||||
|
||||
export default checkTokenIsValid
|
|
@ -1,12 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import logging from "../logging"
|
||||
import type { Player } from "@prisma/client"
|
||||
|
||||
async function createPlayerDB() {
|
||||
const player = await prisma.player.create({ data: {} })
|
||||
logging("Anonymous player created: " + player.id, ["debug"])
|
||||
|
||||
return player
|
||||
}
|
||||
|
||||
export default createPlayerDB
|
|
@ -1,44 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import type { Player, Token, TokenType } from "@prisma/client"
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
export interface RawToken {
|
||||
value: string
|
||||
type: TokenType
|
||||
}
|
||||
export interface IdToken {
|
||||
id: string
|
||||
type: TokenType
|
||||
}
|
||||
|
||||
export const tokenLifetime = {
|
||||
REFRESH: 172800,
|
||||
ACCESS: 15,
|
||||
}
|
||||
|
||||
export default async function createTokenDB(
|
||||
player: Player,
|
||||
newTokenType: TokenType
|
||||
) {
|
||||
// Create token entry in DB
|
||||
const newTokenDB = await prisma.token.create({
|
||||
data: {
|
||||
type: newTokenType,
|
||||
// expires: new Date(Date.now() + tokenLifetime[newTokenType] + "000"),
|
||||
owner: {
|
||||
connect: {
|
||||
id: player.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Sign a new access token
|
||||
const newToken = jwt.sign(
|
||||
{ id: newTokenDB.id },
|
||||
process.env.TOKEN_SECRET as string,
|
||||
{ expiresIn: tokenLifetime[newTokenType] }
|
||||
)
|
||||
|
||||
return { newToken: { value: newToken, type: newTokenType }, newTokenDB }
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import { rejectionErrorFns } from "../errors"
|
||||
import jwtVerifyCatch from "../jwtVerifyCatch"
|
||||
import logging from "../logging"
|
||||
import type { IdToken, RawToken } from "./createTokenDB"
|
||||
import sendError, { API } from "./sendError"
|
||||
import jwt from "jsonwebtoken"
|
||||
|
||||
async function decodeToken<T>(context: API<T>, rawToken: RawToken) {
|
||||
const { value, type } = rawToken
|
||||
|
||||
// Verify the token and get the payload
|
||||
let data: string | jwt.JwtPayload
|
||||
try {
|
||||
data = jwt.verify(value, process.env.TOKEN_SECRET as string)
|
||||
} catch (err: any) {
|
||||
// Deal with the problem in more detail
|
||||
logging(jwtVerifyCatch(type, err).message, ["error"])
|
||||
const fallbackData = jwt.decode(value)
|
||||
// Making sure the token data is not a string (because it should be an object)
|
||||
if (typeof fallbackData === "string")
|
||||
throw sendError(context, rejectionErrorFns.tokenWasString(type, value))
|
||||
return { id: fallbackData?.id, type }
|
||||
}
|
||||
// Making sure the token data is not a string (because it should be an object)
|
||||
if (typeof data === "string")
|
||||
throw sendError(context, rejectionErrorFns.tokenWasString(type, value))
|
||||
|
||||
return { id: data.id, type }
|
||||
}
|
||||
|
||||
export default decodeToken
|
|
@ -1,15 +0,0 @@
|
|||
import checkTokenIsValid from "./checkTokenIsValid"
|
||||
import getPlayerByIdDB from "./getPlayerByIdDB"
|
||||
import getTokenDB from "./getTokenDB"
|
||||
import getTokenFromBody from "./getTokenFromBody"
|
||||
import { API } from "./sendError"
|
||||
|
||||
async function getPlayer<T>(context: API<T>) {
|
||||
const accessToken = await getTokenFromBody(context)
|
||||
const token = await checkTokenIsValid(context, accessToken)
|
||||
const tokenDB = await getTokenDB(context, token)
|
||||
const player = await getPlayerByIdDB(context, tokenDB)
|
||||
return { player, tokenDB }
|
||||
}
|
||||
|
||||
export default getPlayer
|
|
@ -1,21 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import { rejectionErrors } from "../errors"
|
||||
import sendError, { API } from "./sendError"
|
||||
import type { Player, Token } from "@prisma/client"
|
||||
|
||||
export default async function getPlayerByIdDB<T>(
|
||||
context: API<T>,
|
||||
tokenDB: Token
|
||||
) {
|
||||
// Find Host in DB if it still exists (just to make sure)
|
||||
const player = await prisma.player.findUnique({
|
||||
where: {
|
||||
id: tokenDB.ownerId,
|
||||
},
|
||||
})
|
||||
if (!player) {
|
||||
throw sendError(context, rejectionErrors.playerNotFound)
|
||||
}
|
||||
|
||||
return player
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import { rejectionErrors } from "../errors"
|
||||
import sendError, { API } from "./sendError"
|
||||
import type { Player } from "@prisma/client"
|
||||
|
||||
export default async function getPlayerByNameDB<T>(
|
||||
context: API<T>,
|
||||
username: string
|
||||
) {
|
||||
// Find Player in DB if it still exists (just to make sure)
|
||||
const player = await Promise.any([
|
||||
prisma.player.findUniqueOrThrow({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
}),
|
||||
prisma.player.findUniqueOrThrow({
|
||||
where: {
|
||||
email: username,
|
||||
},
|
||||
}),
|
||||
]).catch(() => null)
|
||||
if (player === null) throw sendError(context, rejectionErrors.playerNotFound)
|
||||
|
||||
return player
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import { rejectionErrorFns, rejectionErrors } from "../errors"
|
||||
import type { IdToken } from "./createTokenDB"
|
||||
import sendError, { API } from "./sendError"
|
||||
import { deleteCookie } from "cookies-next"
|
||||
|
||||
async function getTokenDB<T>(
|
||||
context: API<T>,
|
||||
token: IdToken,
|
||||
|
||||
ignoreChecks?: boolean
|
||||
) {
|
||||
const { id, type } = token
|
||||
// Find refresh token in DB
|
||||
const tokenDB = await prisma.token.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
if (!tokenDB) {
|
||||
deleteCookie("token", { ...context, path: "/api" })
|
||||
throw sendError(context, rejectionErrorFns.tokenNotFound(type))
|
||||
}
|
||||
|
||||
if (tokenDB.used && !ignoreChecks)
|
||||
throw sendError(context, rejectionErrors.tokenUsed)
|
||||
|
||||
await prisma.token.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
used: type === "ACCESS",
|
||||
},
|
||||
})
|
||||
|
||||
// await logging('Old token has been invalidated.', ['debug'], req)
|
||||
|
||||
return tokenDB
|
||||
}
|
||||
|
||||
export default getTokenDB
|
|
@ -1,23 +0,0 @@
|
|||
import { rejectionErrorFns } from "../errors"
|
||||
import type { RawToken } from "./createTokenDB"
|
||||
import sendError, { API } from "./sendError"
|
||||
|
||||
async function getTokenFromBody<T>(context: API<T>): Promise<RawToken> {
|
||||
const type = "ACCESS"
|
||||
const body = JSON.parse(context.req.body)
|
||||
// Checking for cookie presens, because it is necessary
|
||||
if (
|
||||
typeof body === "object" &&
|
||||
body &&
|
||||
"token" in body &&
|
||||
typeof body.token === "string"
|
||||
) {
|
||||
const value = body.token
|
||||
return { value, type }
|
||||
}
|
||||
console.log(body)
|
||||
|
||||
throw sendError(context, rejectionErrorFns.noToken(type))
|
||||
}
|
||||
|
||||
export default getTokenFromBody
|
|
@ -1,31 +0,0 @@
|
|||
import createPlayerDB from "./createPlayerDB"
|
||||
import createTokenDB, { RawToken } from "./createTokenDB"
|
||||
import type { API } from "./sendError"
|
||||
import { Player, Token } from "@prisma/client"
|
||||
|
||||
interface Returning {
|
||||
refreshToken: RawToken
|
||||
newPlayer?: { player: Player; newToken: RawToken; newTokenDB: Token }
|
||||
}
|
||||
|
||||
async function getTokenFromCookie<T>(context: API<T>): Promise<Returning> {
|
||||
const type = "REFRESH"
|
||||
const value = context.req.cookies.token
|
||||
|
||||
// Checking for cookie presens, because it is necessary
|
||||
if (!value) {
|
||||
const player = await createPlayerDB()
|
||||
const { newToken, newTokenDB } = await createTokenDB(player, type)
|
||||
return {
|
||||
refreshToken: newToken,
|
||||
newPlayer: {
|
||||
player,
|
||||
newToken,
|
||||
newTokenDB,
|
||||
},
|
||||
}
|
||||
}
|
||||
return { refreshToken: { value, type } }
|
||||
}
|
||||
|
||||
export default getTokenFromCookie
|
|
@ -1,21 +0,0 @@
|
|||
import { rejectionErrors } from "../errors"
|
||||
import sendError, { API } from "./sendError"
|
||||
|
||||
async function getUserFromBody<T>(context: API<T>) {
|
||||
const body = JSON.parse(context.req.body)
|
||||
if (
|
||||
typeof body !== "object" ||
|
||||
!body ||
|
||||
!("username" in body) ||
|
||||
typeof body.username !== "string"
|
||||
)
|
||||
throw sendError(context, rejectionErrors.noUsername)
|
||||
const { username } = body
|
||||
if (!("password" in body) || typeof body.password !== "string")
|
||||
throw sendError(context, rejectionErrors.noPassword)
|
||||
const { password } = body
|
||||
|
||||
return { username, password }
|
||||
}
|
||||
|
||||
export default getUserFromBody
|
|
@ -1,41 +0,0 @@
|
|||
import logging, { Logging } from "../logging"
|
||||
import { RawToken, tokenLifetime } from "./createTokenDB"
|
||||
import type { API } from "./sendError"
|
||||
import { deleteCookie, setCookie } from "cookies-next"
|
||||
|
||||
export interface Result<T> {
|
||||
message: string
|
||||
statusCode?: number
|
||||
body?: T
|
||||
type?: Logging[]
|
||||
cookie?: string
|
||||
}
|
||||
|
||||
export default function sendResponse<T>(context: API<T>, result: Result<T>) {
|
||||
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)
|
||||
return "done" as const
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import prisma from "../../prisma"
|
||||
import { rejectionErrors } from "../errors"
|
||||
import sendError, { API } from "./sendError"
|
||||
import type { Player, Prisma } from "@prisma/client"
|
||||
|
||||
async function updatePlayerDB<T>(
|
||||
context: API<T>,
|
||||
player: Player,
|
||||
data: Prisma.PlayerUpdateInput
|
||||
) {
|
||||
if (!player.anonymous) throw sendError(context, rejectionErrors.registered)
|
||||
const updatedPlayer = await prisma.player.update({
|
||||
where: { id: player.id },
|
||||
data,
|
||||
})
|
||||
|
||||
return updatedPlayer
|
||||
}
|
||||
|
||||
export default updatePlayerDB
|
|
@ -1,106 +1,71 @@
|
|||
import { Logging } from "./logging"
|
||||
import { TokenType } from "@prisma/client"
|
||||
|
||||
export interface rejectionError {
|
||||
rejected?: boolean
|
||||
message: string
|
||||
statusCode: number
|
||||
solved: boolean
|
||||
type?: Logging
|
||||
type?: Logging[]
|
||||
}
|
||||
interface rejectionErrors {
|
||||
[key: string]: rejectionError
|
||||
}
|
||||
interface rejectionErrorFns {
|
||||
[key: string]: (...args: any[]) => rejectionError
|
||||
}
|
||||
|
||||
export const rejectionErrors: rejectionErrors = {
|
||||
noCookie: {
|
||||
rejected: true,
|
||||
message: "Unauthorized. No cookie.",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
},
|
||||
noBody: {
|
||||
rejected: true,
|
||||
message: "Unauthorized. No Body.",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
noUsername: {
|
||||
rejected: true,
|
||||
message: "No username in request body!",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
noPassword: {
|
||||
rejected: true,
|
||||
message: "No password in request body!",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
wrongPassword: {
|
||||
message: "Passwords do not match!",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
playerNotFound: {
|
||||
message: "Player name not found in DB!",
|
||||
statusCode: 401,
|
||||
solved: false,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
tokenUsed: {
|
||||
rejected: true,
|
||||
message: "DBToken was already used!",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
registered: {
|
||||
rejected: true,
|
||||
message: "Player is already registered!",
|
||||
statusCode: 403,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
type: ["warn"],
|
||||
},
|
||||
gameNotFound: {
|
||||
rejected: true,
|
||||
message: "Game not found!",
|
||||
statusCode: 403,
|
||||
solved: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const rejectionErrorFns: rejectionErrorFns = {
|
||||
tokenWasString(tokenType: TokenType, tokenValue: string) {
|
||||
return {
|
||||
rejected: true,
|
||||
message: `${tokenType}-Token data was a string. Token: ${tokenValue}`,
|
||||
statusCode: 401,
|
||||
solved: false,
|
||||
}
|
||||
},
|
||||
tokenNotFound(tokenType: TokenType) {
|
||||
return {
|
||||
rejected: true,
|
||||
message: `${tokenType}-Token not found in DB!`,
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
type: "warn",
|
||||
}
|
||||
},
|
||||
noToken(tokenType: TokenType) {
|
||||
return {
|
||||
rejected: true,
|
||||
message: `Unauthorized. No ${tokenType}-Token.`,
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
}
|
||||
unauthorized: {
|
||||
message: "Unauthorized",
|
||||
statusCode: 401,
|
||||
solved: true,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import sendError, { API } from "./sendError"
|
||||
import sendError from "./sendError"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
async function getPinFromBody<T>(context: API<T>) {
|
||||
const body = JSON.parse(context.req.body)
|
||||
async function getPinFromBody<T>(req: NextApiRequest, res: NextApiResponse<T>) {
|
||||
const body = JSON.parse(req.body)
|
||||
if (
|
||||
typeof body !== "object" ||
|
||||
!body ||
|
||||
!("pin" in body) ||
|
||||
typeof body.pin !== "string"
|
||||
)
|
||||
throw sendError(context, {
|
||||
throw sendError(req, res, {
|
||||
rejected: true,
|
||||
message: "No pin in request body!",
|
||||
statusCode: 401,
|
|
@ -2,16 +2,11 @@ import type { rejectionError } from "../errors"
|
|||
import logging from "../logging"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
export interface API<T> {
|
||||
req: NextApiRequest
|
||||
res: NextApiResponse<T>
|
||||
}
|
||||
|
||||
export default function sendError<T>(
|
||||
context: API<T>,
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<T>,
|
||||
err: rejectionError | Error
|
||||
) {
|
||||
const { res, req } = context
|
||||
// If something went wrong, let the client know with status 500
|
||||
res.status("statusCode" in err ? err.statusCode : 500).end()
|
||||
logging(
|
20
leaky-ships/lib/backend/sendResponse.ts
Normal file
20
leaky-ships/lib/backend/sendResponse.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import logging, { Logging } from "../logging"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
export interface Result<T> {
|
||||
message: string
|
||||
statusCode?: number
|
||||
body?: T
|
||||
type?: Logging[]
|
||||
}
|
||||
|
||||
export default function sendResponse<T>(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<T>,
|
||||
result: Result<T>
|
||||
) {
|
||||
res.status(result.statusCode ?? 200)
|
||||
result.body ? res.json(result.body) : res.end()
|
||||
logging(result.message, result.type ?? ["debug"], req)
|
||||
return "done" as const
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
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() {
|
||||
return fetch("/api/user/auth", {
|
||||
method: "GET",
|
||||
})
|
||||
.then(successfulResponse)
|
||||
.then((response) => {
|
||||
try {
|
||||
const { token } = tokenSchema.parse(response)
|
||||
|
||||
return token
|
||||
} catch (err: any) {
|
||||
const error = err as ZodError
|
||||
toast(JSON.stringify(error))
|
||||
return Promise.reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default getAccessToken
|
30
leaky-ships/lib/hooks/useGameState.tsx
Normal file
30
leaky-ships/lib/hooks/useGameState.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { gameContext } from "../../pages/_app"
|
||||
import { useSession } from "next-auth/react"
|
||||
import { useContext, useEffect } from "react"
|
||||
import { toast } from "react-toastify"
|
||||
|
||||
function useGameState() {
|
||||
const [gameProps, setGameProps] = useContext(gameContext)
|
||||
const { data: session, status } = useSession()
|
||||
useEffect(() => {
|
||||
if (!session) return
|
||||
toast(session.user.email, {
|
||||
toastId: "user",
|
||||
position: "top-center",
|
||||
icon: session.user.image ? (
|
||||
<img
|
||||
style={{ transform: "scale(1.5)", borderRadius: "100%" }}
|
||||
src={session.user.image}
|
||||
/>
|
||||
) : undefined,
|
||||
})
|
||||
}, [session])
|
||||
return {
|
||||
gameProps,
|
||||
setGameProps,
|
||||
session,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
export default useGameState
|
23
leaky-ships/lib/zodSchemas.ts
Normal file
23
leaky-ships/lib/zodSchemas.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { z } from "zod"
|
||||
|
||||
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(),
|
||||
name: z.string().optional(),
|
||||
isOwner: z.boolean().optional(),
|
||||
}),
|
||||
enemy: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
username: z.string().optional(),
|
||||
isOwner: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
|
@ -17,17 +17,17 @@
|
|||
"@fortawesome/pro-thin-svg-icons": "^6.4.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@fortawesome/sharp-solid-svg-icons": "^6.4.0",
|
||||
"@next-auth/prisma-adapter": "^1.0.6",
|
||||
"@next/font": "13.1.1",
|
||||
"@prisma/client": "^4.12.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"classnames": "^2.3.2",
|
||||
"colors": "^1.4.0",
|
||||
"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",
|
||||
"next-auth": "^4.22.1",
|
||||
"nodemailer": "^6.9.1",
|
||||
"prisma": "^4.12.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
@ -36,13 +36,12 @@
|
|||
"socket.io": "^4.6.1",
|
||||
"socket.io-client": "^4.6.1",
|
||||
"typescript": "4.9.4",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@total-typescript/ts-reset": "^0.3.7",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
|
|
|
@ -2,6 +2,7 @@ import "../styles/App.scss"
|
|||
import "../styles/globals.css"
|
||||
import "../styles/grid2.scss"
|
||||
import "../styles/grid.scss"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
import type { AppProps } from "next/app"
|
||||
import { Dispatch, SetStateAction, createContext, useState } from "react"
|
||||
import { ToastContainer } from "react-toastify"
|
||||
|
@ -14,12 +15,12 @@ interface gameContext {
|
|||
}
|
||||
player?: {
|
||||
id: string
|
||||
username?: string
|
||||
name?: string
|
||||
isOwner?: boolean
|
||||
}
|
||||
enemy?: {
|
||||
id: string
|
||||
username?: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,12 +28,17 @@ export const gameContext = createContext<
|
|||
[gameContext, Dispatch<SetStateAction<gameContext>>]
|
||||
>([{}, () => {}])
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}: AppProps) {
|
||||
const gameProps = useState<gameContext>({})
|
||||
return (
|
||||
<gameContext.Provider value={gameProps}>
|
||||
<Component {...pageProps} />
|
||||
<ToastContainer />
|
||||
</gameContext.Provider>
|
||||
<SessionProvider session={session}>
|
||||
<gameContext.Provider value={gameProps}>
|
||||
<Component {...pageProps} />
|
||||
<ToastContainer />
|
||||
</gameContext.Provider>
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
|
|
59
leaky-ships/pages/api/auth/[...nextauth].ts
Normal file
59
leaky-ships/pages/api/auth/[...nextauth].ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import prisma from "@lib/prisma"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { NextApiHandler } from "next"
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import AzureADProvider from "next-auth/providers/azure-ad"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
import {
|
||||
uniqueNamesGenerator,
|
||||
Config,
|
||||
adjectives,
|
||||
colors,
|
||||
animals,
|
||||
NumberDictionary,
|
||||
} from "unique-names-generator"
|
||||
|
||||
const numberDictionary = NumberDictionary.generate({ min: 0, max: 999 })
|
||||
const customConfig: Config = {
|
||||
dictionaries: [adjectives, animals, numberDictionary],
|
||||
separator: " ",
|
||||
style: "capital",
|
||||
length: 3,
|
||||
}
|
||||
|
||||
const options: NextAuthOptions = {
|
||||
debug: true,
|
||||
providers: [
|
||||
EmailProvider({
|
||||
server: process.env.EMAIL_SERVER,
|
||||
from: process.env.EMAIL_FROM,
|
||||
}),
|
||||
AzureADProvider({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID ?? "",
|
||||
clientSecret: process.env.AZURE_AD_CLIENT_SECRET ?? "",
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
}),
|
||||
],
|
||||
adapter: PrismaAdapter(prisma),
|
||||
secret: process.env.SECRET,
|
||||
callbacks: {
|
||||
signIn: ({ user, account }) => {
|
||||
// Custom signIn callback to add username to email provider
|
||||
if (account && account.provider === "email") {
|
||||
user.name = uniqueNamesGenerator(customConfig) // Replace with your desired username
|
||||
}
|
||||
return true
|
||||
},
|
||||
session: ({ session, user }) => {
|
||||
if (session?.user) {
|
||||
session.user.id = user.id
|
||||
}
|
||||
return session
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export { options as authOptions }
|
||||
|
||||
const authHandler: NextApiHandler = (req, res) => NextAuth(req, res, options)
|
||||
export default authHandler
|
|
@ -1,40 +1,36 @@
|
|||
import sendResponse from "@backend/components/sendResponse"
|
||||
import getPlayer from "@lib/backend/components/getPlayer"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
import sendResponse from "@backend/sendResponse"
|
||||
import prisma from "@lib/prisma"
|
||||
import { createSchema } from "@lib/zodSchemas"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import { getServerSession } from "next-auth"
|
||||
import { z } from "zod"
|
||||
|
||||
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<typeof createSchema>
|
||||
|
||||
export default async function create(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (!session) {
|
||||
return sendResponse(req, res, {
|
||||
message: "Unauthorized",
|
||||
statusCode: 401,
|
||||
type: ["error"],
|
||||
})
|
||||
}
|
||||
|
||||
if (!session.user) {
|
||||
return sendResponse(req, res, {
|
||||
message: "Unauthorized - No User",
|
||||
statusCode: 401,
|
||||
type: ["error"],
|
||||
})
|
||||
}
|
||||
const { email, id, name } = session.user
|
||||
|
||||
const { player } = await getPlayer(context)
|
||||
// Generate a random 4-digit code
|
||||
const pin = Math.floor(Math.random() * 10000)
|
||||
.toString()
|
||||
|
@ -45,49 +41,49 @@ export default async function create(
|
|||
let game = await prisma.game.findFirst({
|
||||
where: {
|
||||
running: true,
|
||||
players: {
|
||||
users: {
|
||||
some: {
|
||||
playerId: player.id,
|
||||
userId: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
pin: true,
|
||||
players: true,
|
||||
gamePin: true,
|
||||
users: true,
|
||||
},
|
||||
})
|
||||
if (!game) {
|
||||
created = true
|
||||
game = await prisma.game.create({
|
||||
data: {
|
||||
pin: {
|
||||
gamePin: {
|
||||
create: {
|
||||
pin,
|
||||
},
|
||||
},
|
||||
players: {
|
||||
users: {
|
||||
create: {
|
||||
isOwner: true,
|
||||
playerId: player.id,
|
||||
userId: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
pin: true,
|
||||
players: true,
|
||||
gamePin: true,
|
||||
users: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return sendResponse(context, {
|
||||
message: `Player: ${player.id} created game: ${game.id}`,
|
||||
return sendResponse(req, res, {
|
||||
message: `User <${email}> created game: ${game.id}`,
|
||||
statusCode: created ? 201 : 200,
|
||||
body: {
|
||||
game,
|
||||
pin: game.pin?.pin,
|
||||
pin: game.gamePin?.pin,
|
||||
player: {
|
||||
id: player.id,
|
||||
username: player.username ?? undefined,
|
||||
id,
|
||||
name: name ?? undefined,
|
||||
isOwner: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
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 { authOptions } from "../auth/[...nextauth]"
|
||||
import sendError from "@backend/sendError"
|
||||
import sendResponse from "@backend/sendResponse"
|
||||
import { rejectionErrors } from "@lib/backend/errors"
|
||||
import getPinFromBody from "@lib/backend/getPinFromBody"
|
||||
import prisma from "@lib/prisma"
|
||||
import type { Game } from "@prisma/client"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import { getServerSession } from "next-auth"
|
||||
|
||||
interface Data {
|
||||
game: Game
|
||||
|
@ -15,56 +16,64 @@ export default async function join(
|
|||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
const pin = await getPinFromBody(req, res)
|
||||
|
||||
if (!session?.user) {
|
||||
return sendResponse(req, res, rejectionErrors.unauthorized)
|
||||
}
|
||||
|
||||
const { name, email, id } = session.user
|
||||
|
||||
const pin = await getPinFromBody(context)
|
||||
const { player } = await getPlayer(context)
|
||||
try {
|
||||
const pinDB = await prisma.gamepin.update({
|
||||
const game = await prisma.game.findFirst({
|
||||
where: {
|
||||
pin,
|
||||
},
|
||||
data: {
|
||||
game: {
|
||||
update: {
|
||||
players: {
|
||||
create: {
|
||||
isOwner: false,
|
||||
playerId: player.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
gamePin: {
|
||||
pin,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
game: {
|
||||
users: {
|
||||
include: {
|
||||
players: {
|
||||
include: {
|
||||
player: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if (!game) {
|
||||
return sendResponse(req, res, {
|
||||
message: "Spiel existiert nicht",
|
||||
statusCode: 404,
|
||||
type: ["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}`,
|
||||
const player = game.users.find(({ user }) => user.id === id)?.user
|
||||
const enemy = game.users.find(({ user }) => user.id !== id)?.user
|
||||
|
||||
if (!player) {
|
||||
await prisma.user_Game.create({
|
||||
data: {
|
||||
isOwner: false,
|
||||
gameId: game.id,
|
||||
userId: id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return sendResponse(req, res, {
|
||||
message: `User <${email}> joined game: ${game.id}`,
|
||||
body: {
|
||||
game: pinDB.game,
|
||||
pin: pinDB.pin,
|
||||
game,
|
||||
pin,
|
||||
player: {
|
||||
id: player.id,
|
||||
username: player.username ?? undefined,
|
||||
id,
|
||||
name,
|
||||
isOwner: true,
|
||||
},
|
||||
enemy: {
|
||||
id: enemy?.player.id,
|
||||
username: enemy?.player.username ?? undefined,
|
||||
id: enemy?.id,
|
||||
name: enemy?.name,
|
||||
isOwner: false,
|
||||
},
|
||||
},
|
||||
|
@ -72,6 +81,6 @@ export default async function join(
|
|||
})
|
||||
} catch (err: any) {
|
||||
console.log("HERE".red, err.code, err.meta, err.message)
|
||||
throw sendError(context, rejectionErrors.gameNotFound)
|
||||
throw sendError(req, res, rejectionErrors.gameNotFound)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import checkTokenIsValid from "@backend/components/checkTokenIsValid"
|
||||
import createTokenDB from "@backend/components/createTokenDB"
|
||||
import getPlayerByIdDB from "@backend/components/getPlayerByIdDB"
|
||||
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 {
|
||||
token: string
|
||||
}
|
||||
|
||||
export default async function auth(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
const type = "ACCESS"
|
||||
|
||||
const { refreshToken, newPlayer } = await getTokenFromCookie(context)
|
||||
|
||||
let player: Player, tokenDB: Token, cookie: string | undefined
|
||||
|
||||
if (!newPlayer) {
|
||||
const token = await checkTokenIsValid(context, refreshToken)
|
||||
tokenDB = await getTokenDB(context, token)
|
||||
player = await getPlayerByIdDB(context, tokenDB)
|
||||
} else {
|
||||
player = newPlayer.player
|
||||
tokenDB = newPlayer.newTokenDB
|
||||
cookie = newPlayer.newToken.value
|
||||
}
|
||||
|
||||
const { newToken, newTokenDB } = await createTokenDB(player, type)
|
||||
return sendResponse(context, {
|
||||
message: `Access-Token generated: ${newTokenDB.id} with Refreshtoken-Token: ${tokenDB.id}`,
|
||||
body: { token: newToken.value },
|
||||
type: ["debug", "infoCyan"],
|
||||
cookie,
|
||||
})
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import sendResponse from "@backend/components/sendResponse"
|
||||
import getPlayer from "@lib/backend/components/getPlayer"
|
||||
import type { Game } from "@prisma/client"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
games: Game[]
|
||||
}
|
||||
|
||||
export default async function data(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
const { player, tokenDB } = await getPlayer(context)
|
||||
const games: any = {}
|
||||
return sendResponse(context, {
|
||||
message: `Requested data of user: ${player.id} with Access-Token: ${tokenDB.id}`,
|
||||
body: { games },
|
||||
type: ["debug", "infoCyan"],
|
||||
})
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
import checkPasswordIsValid from "@backend/components/checkPasswordIsValid"
|
||||
import createTokenDB from "@backend/components/createTokenDB"
|
||||
import getPlayerByNameDB from "@backend/components/getPlayerByNameDB"
|
||||
import getUserFromBody from "@backend/components/getUserFromBody"
|
||||
import sendError, { API } from "@backend/components/sendError"
|
||||
import sendResponse from "@backend/components/sendResponse"
|
||||
import logging from "@backend/logging"
|
||||
import { rejectionErrors } from "@lib/backend/errors"
|
||||
import prisma from "@lib/prisma"
|
||||
import jwt from "jsonwebtoken"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
loggedIn: boolean
|
||||
}
|
||||
|
||||
export default async function login(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
await preCheck(context)
|
||||
const { username, password } = await getUserFromBody(context)
|
||||
const player = await getPlayerByNameDB(context, username)
|
||||
await checkPasswordIsValid(context, player, password)
|
||||
const { newToken, newTokenDB } = await createTokenDB(player, "REFRESH")
|
||||
return sendResponse(context, {
|
||||
message:
|
||||
"User " +
|
||||
player.id +
|
||||
" logged in and generated Refresh-Token: " +
|
||||
newTokenDB.id,
|
||||
body: { loggedIn: true },
|
||||
type: ["debug", "infoCyan"],
|
||||
cookie: newToken.value,
|
||||
})
|
||||
}
|
||||
|
||||
async function preCheck<T>(context: API<T>) {
|
||||
const { req } = context
|
||||
const oldRefreshToken = req.cookies.token
|
||||
|
||||
try {
|
||||
if (!oldRefreshToken) throw sendError(context, rejectionErrors.noCookie)
|
||||
// Check for old cookie, if unused invalidate it
|
||||
const tokenData = jwt.verify(
|
||||
oldRefreshToken,
|
||||
process.env.TOKEN_SECRET as string
|
||||
)
|
||||
|
||||
if (!tokenData || typeof tokenData === "string") return
|
||||
|
||||
const oldDBToken = await prisma.token.findUniqueOrThrow({
|
||||
where: {
|
||||
id: tokenData.id,
|
||||
},
|
||||
})
|
||||
|
||||
if (oldDBToken.used)
|
||||
logging("Old login token was used: " + oldDBToken.id, ["debug"], req)
|
||||
else {
|
||||
await prisma.token.update({
|
||||
where: {
|
||||
id: tokenData.id,
|
||||
},
|
||||
data: {
|
||||
used: true,
|
||||
},
|
||||
})
|
||||
await logging("Old login token has been invalidated.", ["debug"], req)
|
||||
}
|
||||
} catch (err) {
|
||||
// err is expected if no correct cookie, just continue, otherwise it has been invalidated above
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import checkTokenIsValid from "@backend/components/checkTokenIsValid"
|
||||
import getPlayerByIdDB from "@backend/components/getPlayerByIdDB"
|
||||
import getTokenDB from "@backend/components/getTokenDB"
|
||||
import getTokenFromCookie from "@backend/components/getTokenFromCookie"
|
||||
import sendResponse from "@backend/components/sendResponse"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
token: string
|
||||
}
|
||||
|
||||
export default async function auth(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
const { refreshToken } = await getTokenFromCookie(context)
|
||||
const token = await checkTokenIsValid(context, refreshToken)
|
||||
const tokenDB = await getTokenDB(context, token)
|
||||
const player = await getPlayerByIdDB(context, tokenDB)
|
||||
return sendResponse(context, {
|
||||
message: "loginCheck -> true : " + player.id,
|
||||
type: ["debug", "infoCyan"],
|
||||
})
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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 decodeToken from "@lib/backend/components/decodeToken"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
loggedOut: boolean
|
||||
}
|
||||
|
||||
export default async function logout(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
const { refreshToken } = await getTokenFromCookie(context)
|
||||
const token = await decodeToken(context, refreshToken)
|
||||
const tokenDB = await getTokenDB(context, token, true)
|
||||
|
||||
return sendResponse(context, {
|
||||
message: "User of Token " + tokenDB.id + " logged out.",
|
||||
body: { loggedOut: true },
|
||||
type: ["debug", "infoCyan"],
|
||||
cookie: "",
|
||||
})
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import checkTokenIsValid from "@backend/components/checkTokenIsValid"
|
||||
import getPlayerByIdDB from "@backend/components/getPlayerByIdDB"
|
||||
import getTokenDB from "@backend/components/getTokenDB"
|
||||
import getTokenFromCookie from "@backend/components/getTokenFromCookie"
|
||||
import getUserFromBody from "@backend/components/getUserFromBody"
|
||||
import sendError from "@backend/components/sendError"
|
||||
import sendResponse from "@backend/components/sendResponse"
|
||||
import updatePlayerDB from "@backend/components/updatePlayerDB"
|
||||
import bcrypt from "bcrypt"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
registered: boolean
|
||||
}
|
||||
|
||||
export default async function register(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
const { refreshToken } = await getTokenFromCookie(context)
|
||||
const token = await checkTokenIsValid(context, refreshToken)
|
||||
const tokenDB = await getTokenDB(context, token)
|
||||
const { username, password } = await getUserFromBody(context)
|
||||
let player = await getPlayerByIdDB(context, tokenDB)
|
||||
player = await updatePlayerDB(context, player, {
|
||||
username,
|
||||
email: "arst",
|
||||
passwordHash: await bcrypt.hash(password, 10),
|
||||
anonymous: false,
|
||||
})
|
||||
|
||||
return sendResponse(context, {
|
||||
message: "Player registered : " + player.id,
|
||||
statusCode: 201,
|
||||
body: { registered: true },
|
||||
type: ["debug", "infoCyan"],
|
||||
})
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import checkPasswordIsValid from "@backend/components/checkPasswordIsValid"
|
||||
import getPlayerByNameDB from "@backend/components/getPlayerByNameDB"
|
||||
import getUserFromBody from "@backend/components/getUserFromBody"
|
||||
import sendError from "@backend/components/sendError"
|
||||
import sendResponse from "@backend/components/sendResponse"
|
||||
import prisma from "@lib/prisma"
|
||||
import { NextApiRequest, NextApiResponse } from "next"
|
||||
|
||||
interface Data {
|
||||
loggedIn: boolean
|
||||
}
|
||||
|
||||
export default async function remove(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const context = { req, res }
|
||||
|
||||
const { username, password } = await getUserFromBody(context)
|
||||
const player = await getPlayerByNameDB(context, username)
|
||||
await checkPasswordIsValid(context, player, password)
|
||||
prisma.player.update({
|
||||
where: {
|
||||
id: player.id,
|
||||
},
|
||||
data: {
|
||||
deleted: true,
|
||||
username: null,
|
||||
email: null,
|
||||
passwordHash: null,
|
||||
},
|
||||
})
|
||||
|
||||
return sendResponse(context, {
|
||||
message: "User successfully deleted: " + player.id,
|
||||
type: ["debug", "infoCyan"],
|
||||
})
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import type { NextApiResponseWithSocket } from "../../interfaces/NextApiSocket"
|
||||
import prisma from "@lib/prisma"
|
||||
import jwt from "jsonwebtoken"
|
||||
import type { NextApiRequest } from "next"
|
||||
import { Server } from "socket.io"
|
||||
|
||||
|
@ -37,21 +36,6 @@ interface SocketData {
|
|||
//
|
||||
// }
|
||||
|
||||
async function checkTokenIsValid(rawToken: string) {
|
||||
// Verify the token and get the payload
|
||||
let tokenData: string | jwt.JwtPayload
|
||||
try {
|
||||
tokenData = jwt.verify(rawToken, process.env.TOKEN_SECRET as string)
|
||||
} catch (err: any) {
|
||||
// Deal with the problem in more detail
|
||||
return Promise.reject()
|
||||
}
|
||||
// Making sure the token data is not a string (because it should be an object)
|
||||
if (typeof tokenData === "string") return Promise.reject()
|
||||
|
||||
return tokenData
|
||||
}
|
||||
|
||||
const SocketHandler = (req: NextApiRequest, res: NextApiResponseWithSocket) => {
|
||||
if (res.socket.server.io) {
|
||||
console.log("Socket is already running " + req.url)
|
||||
|
@ -73,22 +57,6 @@ const SocketHandler = (req: NextApiRequest, res: NextApiResponseWithSocket) => {
|
|||
io.emit("update-input", msg)
|
||||
})
|
||||
|
||||
socket.on("authenticate", (payload) => {
|
||||
checkTokenIsValid(payload.token)
|
||||
.then(async ({ tokenBody }) => {
|
||||
const token = await prisma.token.findUnique({
|
||||
where: { id: tokenBody.id },
|
||||
})
|
||||
if (!token || token.used) {
|
||||
socket.emit("unauthenticated")
|
||||
} else {
|
||||
socket.emit("authenticated")
|
||||
socket.data.isAuthenticated = true
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log("cat", err.message))
|
||||
})
|
||||
|
||||
socket.on("test", (payload) => {
|
||||
console.log("Got test:", payload)
|
||||
// ...
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
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, {
|
||||
successfulResponse,
|
||||
} from "@lib/frontend/getAccessToken"
|
||||
import { GetServerSideProps } from "next"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
import OtpInput from "react-otp-input"
|
||||
import { toast } from "react-toastify"
|
||||
import { z } from "zod"
|
||||
|
||||
interface Props {
|
||||
q: string | string[] | undefined
|
||||
}
|
||||
function isInputOnlyNumbers(input: string) {
|
||||
return /^\d+$/.test(input)
|
||||
}
|
||||
|
||||
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) =>
|
||||
fetch("/api/game/" + (!pin ? "create" : "join"), {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ token, 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, gameFetch])
|
||||
|
||||
return (
|
||||
<div className="h-full bg-theme">
|
||||
<div className="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly">
|
||||
<Logo />
|
||||
<BurgerMenu />
|
||||
{(() => {
|
||||
switch (q) {
|
||||
case "join":
|
||||
return (
|
||||
<div className="flex flex-col items-center rounded-xl border-4 border-black bg-grayish px-4 py-6 shadow-lg sm:mx-8 sm:p-12 md:w-full">
|
||||
<button
|
||||
className="-mt-2 w-20 self-start rounded-xl border-b-4 border-shield-gray bg-voidDark text-2xl text-grayish duration-100 active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
// Navigate to the same page with the `start` query parameter set to `false`
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: null,
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
</button>
|
||||
<div className="flex flex-col items-center gap-6 sm:gap-12">
|
||||
<OptionButton icon={faPlus}>Raum erstellen</OptionButton>
|
||||
<OptionButton
|
||||
action={() => {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { q: "join" },
|
||||
})
|
||||
}}
|
||||
icon={faUserPlus}
|
||||
>
|
||||
<OtpInput
|
||||
containerStyle={{ color: "initial" }}
|
||||
value={otp}
|
||||
onChange={setOtp}
|
||||
numInputs={4}
|
||||
placeholder="0000"
|
||||
renderSeparator={<span>-</span>}
|
||||
renderInput={(props) => <input {...props} />}
|
||||
/>
|
||||
</OptionButton>
|
||||
<OptionButton icon={faEye}>Zuschauen</OptionButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
case "start":
|
||||
return (
|
||||
<div className="flex flex-col items-center rounded-xl border-4 border-black bg-grayish px-4 py-6 shadow-lg sm:mx-8 sm:p-12 md:w-full">
|
||||
<button
|
||||
className="-mt-2 w-20 self-start rounded-xl border-b-4 border-shield-gray bg-voidDark text-2xl text-grayish duration-100 active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
// Navigate to the same page with the `start` query parameter set to `false`
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: null,
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
</button>
|
||||
<div className="flex flex-col items-center gap-6 sm:gap-12">
|
||||
<OptionButton action={() => gameFetch()} icon={faPlus}>
|
||||
Raum erstellen
|
||||
</OptionButton>
|
||||
<OptionButton
|
||||
action={() => {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { q: "join" },
|
||||
})
|
||||
}}
|
||||
icon={faUserPlus}
|
||||
>
|
||||
Raum beitreten
|
||||
</OptionButton>
|
||||
<OptionButton icon={faEye}>Zuschauen</OptionButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-36 w-64 items-center justify-center rounded-xl border-4 border-black bg-[#2227] sm:h-48 sm:w-96 md:h-72 md:w-[32rem] md:border-[6px] xl:h-[26rem] xl:w-[48rem]">
|
||||
<FontAwesomeIcon
|
||||
className="text-6xl sm:text-7xl md:text-8xl"
|
||||
icon={faCirclePlay}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
// Navigate to the same page with the `start` query parameter set to `true`
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { q: "start" },
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
START
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
context
|
||||
) => {
|
||||
const { q } = context.query
|
||||
|
||||
return { props: { q: q ? q : "" } }
|
||||
}
|
|
@ -1,62 +1,34 @@
|
|||
import Head from "next/head"
|
||||
import Link from "next/link"
|
||||
import BurgerMenu from "../components/BurgerMenu"
|
||||
import Logo from "../components/Logo"
|
||||
import { faCirclePlay } from "@fortawesome/pro-thin-svg-icons"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import { useRouter } from "next/router"
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main>
|
||||
<p>
|
||||
<Link href="/dev/gamefield" target="_blank">
|
||||
Gamefield
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev" target="_blank">
|
||||
Homepage
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/lobby" target="_blank">
|
||||
Lobby
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/login" target="_blank">
|
||||
Login
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/logout" target="_blank">
|
||||
Logout
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/grid" target="_blank">
|
||||
Grid Effect
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/grid2" target="_blank">
|
||||
Grid Effect with Content
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/socket" target="_blank">
|
||||
Socket
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link href="/dev/socketio" target="_blank">
|
||||
SocketIO
|
||||
</Link>
|
||||
</p>
|
||||
</main>
|
||||
</>
|
||||
<div className="h-full bg-theme">
|
||||
<div className="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly">
|
||||
<Logo />
|
||||
<BurgerMenu />
|
||||
<div className="flex h-36 w-64 items-center justify-center rounded-xl border-4 border-black bg-[#2227] sm:h-48 sm:w-96 md:h-72 md:w-[32rem] md:border-[6px] xl:h-[26rem] xl:w-[48rem]">
|
||||
<FontAwesomeIcon
|
||||
className="text-6xl sm:text-7xl md:text-8xl"
|
||||
icon={faCirclePlay}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
router.push("/start")
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
START
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import SocketIO from "../../components/SocketIO"
|
||||
import SocketIO from "../components/SocketIO"
|
||||
|
||||
export default function Home() {
|
||||
return (
|
168
leaky-ships/pages/start.tsx
Normal file
168
leaky-ships/pages/start.tsx
Normal file
|
@ -0,0 +1,168 @@
|
|||
import BurgerMenu from "../components/BurgerMenu"
|
||||
import Logo from "../components/Logo"
|
||||
import OptionButton from "../components/OptionButton"
|
||||
import { faEye, faLeftLong } from "@fortawesome/pro-regular-svg-icons"
|
||||
import { faPlus, faUserPlus } from "@fortawesome/pro-solid-svg-icons"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import useGameState from "@lib/hooks/useGameState"
|
||||
import { createSchema } from "@lib/zodSchemas"
|
||||
import status from "http-status"
|
||||
import { GetServerSideProps } from "next"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import OtpInput from "react-otp-input"
|
||||
import { toast } from "react-toastify"
|
||||
|
||||
interface Props {
|
||||
q: string | string[] | undefined
|
||||
}
|
||||
function isInputOnlyNumbers(input: string) {
|
||||
return /^\d+$/.test(input)
|
||||
}
|
||||
|
||||
export function isAuthenticated(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[res.status], {
|
||||
position: "top-center",
|
||||
type: "error",
|
||||
theme: "colored",
|
||||
})
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
export default function Home({ q }: Props) {
|
||||
const [otp, setOtp] = useState("")
|
||||
const { gameProps, setGameProps } = useGameState()
|
||||
const router = useRouter()
|
||||
const { session } = useGameState()
|
||||
|
||||
const gameFetch = useCallback(
|
||||
async (pin?: string) => {
|
||||
const gamePromise = fetch("/api/game/" + (!pin ? "create" : "join"), {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ pin }),
|
||||
})
|
||||
.then(isAuthenticated)
|
||||
.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",
|
||||
},
|
||||
})
|
||||
|
||||
console.log(res)
|
||||
setGameProps(res)
|
||||
|
||||
await toast.promise(router.push("/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, gameFetch])
|
||||
|
||||
return (
|
||||
<div className="h-full bg-theme">
|
||||
<div className="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly">
|
||||
<Logo />
|
||||
<BurgerMenu />
|
||||
<div className="flex flex-col items-center rounded-xl border-4 border-black bg-grayish px-4 py-6 shadow-lg sm:mx-8 sm:p-12 md:w-full">
|
||||
<button
|
||||
className="-mt-2 w-20 self-start rounded-xl border-b-4 border-shield-gray bg-voidDark text-2xl text-grayish duration-100 active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
// Navigate to the same page with the `start` query parameter set to `false`
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: null,
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
</button>
|
||||
<div className="flex flex-col items-center gap-6 sm:gap-12">
|
||||
<OptionButton
|
||||
action={() => gameFetch()}
|
||||
icon={faPlus}
|
||||
disabled={!session}
|
||||
>
|
||||
Raum erstellen
|
||||
</OptionButton>
|
||||
<OptionButton
|
||||
action={() => {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { q: "join" },
|
||||
})
|
||||
}}
|
||||
icon={faUserPlus}
|
||||
disabled={!session}
|
||||
>
|
||||
{q === "join" ? (
|
||||
"Raum beitreten"
|
||||
) : (
|
||||
<OtpInput
|
||||
shouldAutoFocus
|
||||
containerStyle={{ color: "initial" }}
|
||||
value={otp}
|
||||
onChange={setOtp}
|
||||
numInputs={4}
|
||||
placeholder="0000"
|
||||
renderSeparator={<span>-</span>}
|
||||
renderInput={(props) => <input {...props} />}
|
||||
/>
|
||||
)}
|
||||
</OptionButton>
|
||||
<OptionButton icon={faEye}>Zuschauen</OptionButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
context
|
||||
) => {
|
||||
const { q } = context.query
|
||||
|
||||
return { props: { q: q ? q : "" } }
|
||||
}
|
432
leaky-ships/pnpm-lock.yaml
generated
432
leaky-ships/pnpm-lock.yaml
generated
|
@ -25,24 +25,21 @@ dependencies:
|
|||
'@fortawesome/sharp-solid-svg-icons':
|
||||
specifier: ^6.4.0
|
||||
version: 6.4.0
|
||||
'@next-auth/prisma-adapter':
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(@prisma/client@4.12.0)(next-auth@4.22.1)
|
||||
'@next/font':
|
||||
specifier: 13.1.1
|
||||
version: 13.1.1
|
||||
'@prisma/client':
|
||||
specifier: ^4.12.0
|
||||
version: 4.12.0(prisma@4.12.0)
|
||||
bcrypt:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
classnames:
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
colors:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
cookies-next:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
eslint:
|
||||
specifier: 8.31.0
|
||||
version: 8.31.0
|
||||
|
@ -52,12 +49,15 @@ dependencies:
|
|||
http-status:
|
||||
specifier: ^1.6.2
|
||||
version: 1.6.2
|
||||
jsonwebtoken:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
next:
|
||||
specifier: 13.1.1
|
||||
version: 13.1.1(react-dom@18.2.0)(react@18.2.0)(sass@1.61.0)
|
||||
next-auth:
|
||||
specifier: ^4.22.1
|
||||
version: 4.22.1(next@13.1.1)(nodemailer@6.9.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
nodemailer:
|
||||
specifier: ^6.9.1
|
||||
version: 6.9.1
|
||||
prisma:
|
||||
specifier: ^4.12.0
|
||||
version: 4.12.0
|
||||
|
@ -82,6 +82,9 @@ dependencies:
|
|||
typescript:
|
||||
specifier: 4.9.4
|
||||
version: 4.9.4
|
||||
unique-names-generator:
|
||||
specifier: ^4.7.1
|
||||
version: 4.7.1
|
||||
zod:
|
||||
specifier: ^3.21.4
|
||||
version: 3.21.4
|
||||
|
@ -93,12 +96,6 @@ devDependencies:
|
|||
'@trivago/prettier-plugin-sort-imports':
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1(prettier@2.8.7)
|
||||
'@types/bcrypt':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
'@types/jsonwebtoken':
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
'@types/node':
|
||||
specifier: ^18.15.11
|
||||
version: 18.15.11
|
||||
|
@ -362,22 +359,14 @@ packages:
|
|||
/@humanwhocodes/object-schema@1.2.1:
|
||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||
|
||||
/@mapbox/node-pre-gyp@1.0.10:
|
||||
resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
|
||||
hasBin: true
|
||||
/@next-auth/prisma-adapter@1.0.6(@prisma/client@4.12.0)(next-auth@4.22.1):
|
||||
resolution: {integrity: sha512-Z7agwfSZEeEcqKqrnisBun7VndRPshd6vyDsoRU68MXbkui8storkHgvN2hnNDrqr/hSCF9aRn56a1qpihaB4A==}
|
||||
peerDependencies:
|
||||
'@prisma/client': '>=2.26.0 || >=3'
|
||||
next-auth: ^4
|
||||
dependencies:
|
||||
detect-libc: 2.0.1
|
||||
https-proxy-agent: 5.0.1
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.6.9
|
||||
nopt: 5.0.0
|
||||
npmlog: 5.0.1
|
||||
rimraf: 3.0.2
|
||||
semver: 7.3.8
|
||||
tar: 6.1.13
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
'@prisma/client': 4.12.0(prisma@4.12.0)
|
||||
next-auth: 4.22.1(next@13.1.1)(nodemailer@6.9.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@next/env@13.1.1:
|
||||
|
@ -529,6 +518,10 @@ packages:
|
|||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.15.0
|
||||
|
||||
/@panva/hkdf@1.0.4:
|
||||
resolution: {integrity: sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ==}
|
||||
dev: false
|
||||
|
||||
/@pkgr/utils@2.3.1:
|
||||
resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
@ -602,12 +595,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@types/bcrypt@5.0.0:
|
||||
resolution: {integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==}
|
||||
dependencies:
|
||||
'@types/node': 18.15.11
|
||||
dev: true
|
||||
|
||||
/@types/cookie@0.4.1:
|
||||
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
||||
dev: false
|
||||
|
@ -622,16 +609,6 @@ packages:
|
|||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
dev: false
|
||||
|
||||
/@types/jsonwebtoken@9.0.1:
|
||||
resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==}
|
||||
dependencies:
|
||||
'@types/node': 18.15.11
|
||||
dev: true
|
||||
|
||||
/@types/node@16.18.23:
|
||||
resolution: {integrity: sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==}
|
||||
dev: false
|
||||
|
||||
/@types/node@18.15.11:
|
||||
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
|
||||
|
||||
|
@ -723,10 +700,6 @@ packages:
|
|||
eslint-visitor-keys: 3.4.0
|
||||
dev: false
|
||||
|
||||
/abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
dev: false
|
||||
|
||||
/accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
@ -747,15 +720,6 @@ packages:
|
|||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
/agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ajv@6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
dependencies:
|
||||
|
@ -792,18 +756,6 @@ packages:
|
|||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
/aproba@2.0.0:
|
||||
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
|
||||
dev: false
|
||||
|
||||
/are-we-there-yet@2.0.0:
|
||||
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
delegates: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
dev: false
|
||||
|
||||
/arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
dev: true
|
||||
|
@ -914,18 +866,6 @@ packages:
|
|||
engines: {node: ^4.5.0 || >= 5.9}
|
||||
dev: false
|
||||
|
||||
/bcrypt@5.1.0:
|
||||
resolution: {integrity: sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.10
|
||||
node-addon-api: 5.1.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -953,10 +893,6 @@ packages:
|
|||
update-browserslist-db: 1.0.10(browserslist@4.21.5)
|
||||
dev: true
|
||||
|
||||
/buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
dev: false
|
||||
|
||||
/call-bind@1.0.2:
|
||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||
dependencies:
|
||||
|
@ -1006,11 +942,6 @@ packages:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/classnames@2.3.2:
|
||||
resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
|
||||
dev: false
|
||||
|
@ -1043,11 +974,6 @@ packages:
|
|||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
/color-support@1.1.3:
|
||||
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/colors@1.4.0:
|
||||
resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
@ -1061,21 +987,14 @@ packages:
|
|||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
/console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
dev: false
|
||||
|
||||
/cookie@0.4.2:
|
||||
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/cookies-next@2.1.1:
|
||||
resolution: {integrity: sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==}
|
||||
dependencies:
|
||||
'@types/cookie': 0.4.1
|
||||
'@types/node': 16.18.23
|
||||
cookie: 0.4.2
|
||||
/cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/cors@2.8.5:
|
||||
|
@ -1168,15 +1087,6 @@ packages:
|
|||
object-keys: 1.1.1
|
||||
dev: false
|
||||
|
||||
/delegates@1.0.0:
|
||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||
dev: false
|
||||
|
||||
/detect-libc@2.0.1:
|
||||
resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
dev: true
|
||||
|
@ -1205,20 +1115,10 @@ packages:
|
|||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
/ecdsa-sig-formatter@1.0.11:
|
||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.356:
|
||||
resolution: {integrity: sha512-nEftV1dRX3omlxAj42FwqRZT0i4xd2dIg39sog/CnCJeCcL1TRd2Uh0i9Oebgv8Ou0vzTPw++xc+Z20jzS2B6A==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: false
|
||||
|
||||
/emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
dev: false
|
||||
|
@ -1713,13 +1613,6 @@ packages:
|
|||
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
|
||||
dev: true
|
||||
|
||||
/fs-minipass@2.1.0:
|
||||
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
dev: false
|
||||
|
||||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
|
@ -1747,21 +1640,6 @@ packages:
|
|||
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||
dev: false
|
||||
|
||||
/gauge@3.0.2:
|
||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
aproba: 2.0.0
|
||||
color-support: 1.1.3
|
||||
console-control-strings: 1.1.0
|
||||
has-unicode: 2.0.1
|
||||
object-assign: 4.1.1
|
||||
signal-exit: 3.0.7
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wide-align: 1.1.5
|
||||
dev: false
|
||||
|
||||
/get-intrinsic@1.2.0:
|
||||
resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==}
|
||||
dependencies:
|
||||
|
@ -1924,10 +1802,6 @@ packages:
|
|||
has-symbols: 1.0.3
|
||||
dev: false
|
||||
|
||||
/has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
dev: false
|
||||
|
||||
/has@1.0.3:
|
||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
@ -1939,16 +1813,6 @@ packages:
|
|||
engines: {node: '>= 0.4.0'}
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ignore@5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
engines: {node: '>= 4'}
|
||||
|
@ -2048,11 +1912,6 @@ packages:
|
|||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -2166,6 +2025,10 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/jose@4.14.1:
|
||||
resolution: {integrity: sha512-SgjXLpP7jhQkUNKL6RpowoR/IF4QKE+WjLDMpNnh2vmhiFs67NftrNpvFtgbwpvRdtueFliahYYWz9E+XZZQlg==}
|
||||
dev: false
|
||||
|
||||
/js-sdsl@4.4.0:
|
||||
resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==}
|
||||
|
||||
|
@ -2197,16 +2060,6 @@ packages:
|
|||
minimist: 1.2.8
|
||||
dev: false
|
||||
|
||||
/jsonwebtoken@9.0.0:
|
||||
resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==}
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
dependencies:
|
||||
jws: 3.2.2
|
||||
lodash: 4.17.21
|
||||
ms: 2.1.3
|
||||
semver: 7.3.8
|
||||
dev: false
|
||||
|
||||
/jsx-ast-utils@3.3.3:
|
||||
resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
@ -2215,21 +2068,6 @@ packages:
|
|||
object.assign: 4.1.4
|
||||
dev: false
|
||||
|
||||
/jwa@1.4.1:
|
||||
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
ecdsa-sig-formatter: 1.0.11
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/jws@3.2.2:
|
||||
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
||||
dependencies:
|
||||
jwa: 1.4.1
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/language-subtag-registry@0.3.22:
|
||||
resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==}
|
||||
dev: false
|
||||
|
@ -2267,6 +2105,7 @@ packages:
|
|||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
|
@ -2282,13 +2121,6 @@ packages:
|
|||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
semver: 6.3.0
|
||||
dev: false
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -2321,32 +2153,6 @@ packages:
|
|||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
dev: false
|
||||
|
||||
/minipass@3.3.6:
|
||||
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/minipass@4.2.5:
|
||||
resolution: {integrity: sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/minizlib@2.1.2:
|
||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
|
@ -2375,6 +2181,32 @@ packages:
|
|||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/next-auth@4.22.1(next@13.1.1)(nodemailer@6.9.1)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-NTR3f6W7/AWXKw8GSsgSyQcDW6jkslZLH8AiZa5PQ09w1kR8uHtR9rez/E9gAq/o17+p0JYHE8QjF3RoniiObA==}
|
||||
peerDependencies:
|
||||
next: ^12.2.5 || ^13
|
||||
nodemailer: ^6.6.5
|
||||
react: ^17.0.2 || ^18
|
||||
react-dom: ^17.0.2 || ^18
|
||||
peerDependenciesMeta:
|
||||
nodemailer:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@panva/hkdf': 1.0.4
|
||||
cookie: 0.5.0
|
||||
jose: 4.14.1
|
||||
next: 13.1.1(react-dom@18.2.0)(react@18.2.0)(sass@1.61.0)
|
||||
nodemailer: 6.9.1
|
||||
oauth: 0.9.15
|
||||
openid-client: 5.4.1
|
||||
preact: 10.13.2
|
||||
preact-render-to-string: 5.2.6(preact@10.13.2)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
uuid: 8.3.2
|
||||
dev: false
|
||||
|
||||
/next@13.1.1(react-dom@18.2.0)(react@18.2.0)(sass@1.61.0):
|
||||
resolution: {integrity: sha512-R5eBAaIa3X7LJeYvv1bMdGnAVF4fVToEjim7MkflceFPuANY3YyvFxXee/A+acrSYwYPvOvf7f6v/BM/48ea5w==}
|
||||
engines: {node: '>=14.6.0'}
|
||||
|
@ -2420,32 +2252,13 @@ packages:
|
|||
- babel-plugin-macros
|
||||
dev: false
|
||||
|
||||
/node-addon-api@5.1.0:
|
||||
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.6.9:
|
||||
resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-releases@2.0.10:
|
||||
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
|
||||
dev: true
|
||||
|
||||
/nopt@5.0.0:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
abbrev: 1.1.1
|
||||
/nodemailer@6.9.1:
|
||||
resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dev: false
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
|
@ -2457,19 +2270,19 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/npmlog@5.0.1:
|
||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
dependencies:
|
||||
are-we-there-yet: 2.0.0
|
||||
console-control-strings: 1.1.0
|
||||
gauge: 3.0.2
|
||||
set-blocking: 2.0.0
|
||||
/oauth@0.9.15:
|
||||
resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==}
|
||||
dev: false
|
||||
|
||||
/object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
/object-hash@2.2.0:
|
||||
resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: false
|
||||
|
||||
/object-hash@3.0.0:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
@ -2536,6 +2349,11 @@ packages:
|
|||
es-abstract: 1.21.2
|
||||
dev: false
|
||||
|
||||
/oidc-token-hash@5.0.2:
|
||||
resolution: {integrity: sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ==}
|
||||
engines: {node: ^10.13.0 || >=12.0.0}
|
||||
dev: false
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
|
@ -2550,6 +2368,15 @@ packages:
|
|||
is-wsl: 2.2.0
|
||||
dev: false
|
||||
|
||||
/openid-client@5.4.1:
|
||||
resolution: {integrity: sha512-z19tZRY1CvpDf9L8nFyv5LW3CWoRDuz8EdmUFyouUrf5FDZitaJ8C+tIHF6WnjA2DPG4Fu2JWkNjexNzLWjh3Q==}
|
||||
dependencies:
|
||||
jose: 4.14.1
|
||||
lru-cache: 6.0.0
|
||||
object-hash: 2.2.0
|
||||
oidc-token-hash: 5.0.2
|
||||
dev: false
|
||||
|
||||
/optionator@0.9.1:
|
||||
resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
@ -2695,6 +2522,19 @@ packages:
|
|||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/preact-render-to-string@5.2.6(preact@10.13.2):
|
||||
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
|
||||
peerDependencies:
|
||||
preact: '>=10'
|
||||
dependencies:
|
||||
preact: 10.13.2
|
||||
pretty-format: 3.8.0
|
||||
dev: false
|
||||
|
||||
/preact@10.13.2:
|
||||
resolution: {integrity: sha512-q44QFLhOhty2Bd0Y46fnYW0gD/cbVM9dUVtNTDKPcdXSMA7jfY+Jpd6rk3GB0lcQss0z5s/6CmVP0Z/hV+g6pw==}
|
||||
dev: false
|
||||
|
||||
/prelude-ls@1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
@ -2761,6 +2601,10 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/pretty-format@3.8.0:
|
||||
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
|
||||
dev: false
|
||||
|
||||
/prisma@4.12.0:
|
||||
resolution: {integrity: sha512-xqVper4mbwl32BWzLpdznHAYvYDWQQWK2tBfXjdUD397XaveRyAP7SkBZ6kFlIg8kKayF4hvuaVtYwXd9BodAg==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
@ -2838,15 +2682,6 @@ packages:
|
|||
pify: 2.3.0
|
||||
dev: true
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
|
||||
/readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
@ -2906,10 +2741,6 @@ packages:
|
|||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: false
|
||||
|
||||
/safe-regex-test@1.0.0:
|
||||
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
|
||||
dependencies:
|
||||
|
@ -2946,10 +2777,6 @@ packages:
|
|||
lru-cache: 6.0.0
|
||||
dev: false
|
||||
|
||||
/set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
dev: false
|
||||
|
||||
/shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -2968,10 +2795,6 @@ packages:
|
|||
object-inspect: 1.12.3
|
||||
dev: false
|
||||
|
||||
/signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
dev: false
|
||||
|
||||
/slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -3047,15 +2870,6 @@ packages:
|
|||
internal-slot: 1.0.5
|
||||
dev: false
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: false
|
||||
|
||||
/string.prototype.matchall@4.0.8:
|
||||
resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==}
|
||||
dependencies:
|
||||
|
@ -3094,12 +2908,6 @@ packages:
|
|||
es-abstract: 1.21.2
|
||||
dev: false
|
||||
|
||||
/string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -3210,18 +3018,6 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/tar@6.1.13:
|
||||
resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
minipass: 4.2.5
|
||||
minizlib: 2.1.2
|
||||
mkdirp: 1.0.4
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
||||
|
@ -3256,10 +3052,6 @@ packages:
|
|||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
dev: true
|
||||
|
@ -3324,6 +3116,11 @@ packages:
|
|||
which-boxed-primitive: 1.0.2
|
||||
dev: false
|
||||
|
||||
/unique-names-generator@4.7.1:
|
||||
resolution: {integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/update-browserslist-db@1.0.10(browserslist@4.21.5):
|
||||
resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
|
||||
hasBin: true
|
||||
|
@ -3342,23 +3139,18 @@ packages:
|
|||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/vary@1.1.2:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/which-boxed-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||
dependencies:
|
||||
|
@ -3397,12 +3189,6 @@ packages:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
/wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
dev: false
|
||||
|
||||
/word-wrap@1.2.3:
|
||||
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
|
@ -7,17 +7,61 @@ datasource db {
|
|||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Player {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deleted Boolean @default(false)
|
||||
anonymous Boolean @default(true)
|
||||
username String? @unique
|
||||
email String? @unique
|
||||
passwordHash String? @unique
|
||||
tokens Token[]
|
||||
games Player_Game[]
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String @map("provider_account_id")
|
||||
refresh_token String? @db.Text
|
||||
access_token String? @db.Text
|
||||
expires_at Int?
|
||||
ext_expires_in Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String? @db.Text
|
||||
session_state String?
|
||||
oauth_token_secret String?
|
||||
oauth_token String?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
@@map("accounts")
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique @map("session_token")
|
||||
userId String @map("user_id")
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("sessions")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime? @map("email_verified")
|
||||
image String? @db.Text
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
games User_Game[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
@@map(name: "users")
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
@@map("verificationtokens")
|
||||
}
|
||||
|
||||
enum TokenType {
|
||||
|
@ -25,45 +69,35 @@ enum TokenType {
|
|||
ACCESS
|
||||
}
|
||||
|
||||
model Token {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
type TokenType
|
||||
used Boolean @default(false)
|
||||
ownerId String
|
||||
owner Player @relation(fields: [ownerId], references: [id])
|
||||
Socket Socket?
|
||||
}
|
||||
|
||||
model Game {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
running Boolean @default(true)
|
||||
pin Gamepin?
|
||||
players Player_Game[]
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
running Boolean @default(true)
|
||||
gamePin Gamepin?
|
||||
users User_Game[]
|
||||
}
|
||||
|
||||
model Player_Game {
|
||||
model User_Game {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
gameId String
|
||||
playerId String
|
||||
userId String
|
||||
isOwner Boolean
|
||||
moves Move[]
|
||||
chats Chat[]
|
||||
game Game @relation(fields: [gameId], references: [id])
|
||||
player Player @relation(fields: [playerId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@unique([gameId, playerId])
|
||||
@@unique([gameId, userId])
|
||||
}
|
||||
|
||||
model Move {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
index Int
|
||||
gameId String
|
||||
game Player_Game @relation(fields: [gameId], references: [id])
|
||||
game User_Game @relation(fields: [gameId], references: [id])
|
||||
}
|
||||
|
||||
model Gamepin {
|
||||
|
@ -75,17 +109,10 @@ model Gamepin {
|
|||
}
|
||||
|
||||
model Chat {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
message String?
|
||||
event String?
|
||||
gameId String
|
||||
game Player_Game @relation(fields: [gameId], references: [id])
|
||||
}
|
||||
|
||||
model Socket {
|
||||
sessionId String @id
|
||||
createdAt DateTime @default(now())
|
||||
tokenId String @unique
|
||||
token Token @relation(fields: [tokenId], references: [id])
|
||||
game User_Game @relation(fields: [gameId], references: [id])
|
||||
}
|
||||
|
|
12
leaky-ships/types/next-auth.d.ts
vendored
Normal file
12
leaky-ships/types/next-auth.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
import NextAuth, { DefaultSession } from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
user: {
|
||||
id: string
|
||||
} & DefaultSession["user"]
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue