Improve code quality

This commit is contained in:
aronmal 2023-04-10 20:21:27 +02:00
parent 39ddf8fde9
commit 8193b85274
Signed by: aronmal
GPG key ID: 816B7707426FC612
26 changed files with 187 additions and 138 deletions

View file

@ -4,7 +4,7 @@ import type { Socket as NetSocket } from "net"
import type { Server as IOServer } from "socket.io"
interface SocketServer extends HTTPServer {
io?: IOServer | undefined
io?: IOServer
}
interface SocketWithIO extends NetSocket {

View file

@ -1,4 +1,4 @@
import { Player } from "@prisma/client"
import type { Player } from "@prisma/client"
import bcrypt from "bcrypt"
import errors from "../errors"
import sendError, { API } from "./sendError"
@ -10,7 +10,7 @@ export default async function checkPasswordIsValid(
next: () => void
) {
// Validate for correct password
const result = await bcrypt.compare(password, player.passwordHash)
const result = await bcrypt.compare(password, player.passwordHash ?? "")
if (!result) return sendError(context, errors.wrongPassword)
return next()

View file

@ -2,7 +2,7 @@ import jwt from "jsonwebtoken"
import errors from "../errors"
import jwtVerifyCatch from "../jwtVerifyCatch"
import sendError, { API } from "./sendError"
import { IdToken, RawToken } from "./createTokenDB"
import type { IdToken, RawToken } from "./createTokenDB"
async function checkTokenIsValid(
context: API,
@ -14,7 +14,7 @@ async function checkTokenIsValid(
// Verify the token and get the payload
let data: string | jwt.JwtPayload
try {
data = jwt.verify(value, process.env.ACCESS_TOKEN_SECRET as string)
data = jwt.verify(value, process.env.TOKEN_SECRET as string)
} catch (err: any) {
// Deal with the problem in more detail
return sendError(context, jwtVerifyCatch(type, err))

View file

@ -1,28 +0,0 @@
import prisma from "../../prisma"
async function createAnonymousDB<T>(payload: T) {
const player = await prisma.player.create({
data: {
anonymous: true,
},
})
// .catch((err: any) => {
// if (err.code === 11000) {
// return Promise.reject({
// message: `Duplicate key error while creating Player in DB!`,
// statusCode: 409,
// solved: true,
// type: 'warn'
// })
// } else {
// console.log(err)
// return Promise.reject({
// message: `Unknown error while creating Player in DB.`,
// solved: false
// })
// }
// })
return { ...payload, player }
}
export default createAnonymousDB

View file

@ -1,19 +1,32 @@
import { Player } from "@prisma/client"
import type { Player } from "@prisma/client"
import bcrypt from "bcrypt"
import prisma from "../../prisma"
import logging from "../logging"
async function createPlayerDB(
username: string,
password: string,
payload:
| {
username: string
password: string
}
| "anonymous",
next: (player: Player) => void
) {
const player = await prisma.player.create({
data: {
username,
passwordHash: await bcrypt.hash(password, 10),
anonymous: false,
},
})
let player: Player
if (payload === "anonymous") {
player = await prisma.player.create({ data: {} })
logging("Anonymous player created: " + player.id, ["debug"])
} else {
const { username, password } = payload
player = await prisma.player.create({
data: {
username,
passwordHash: await bcrypt.hash(password, 10),
anonymous: false,
},
})
logging("Player registered: " + player.id, ["debug"])
}
return next(player)
}

View file

@ -1,4 +1,4 @@
import { Player, Token, TokenType } from "@prisma/client"
import type { Player, Token, TokenType } from "@prisma/client"
import jwt from "jsonwebtoken"
import prisma from "../../prisma"
@ -41,5 +41,5 @@ export default async function createTokenDB(
{ expiresIn: tokenLifetime[newTokenType] }
)
return next({ value: newToken, type: "ACCESS" }, newTokenDB)
return next({ value: newToken, type: newTokenType }, newTokenDB)
}

View file

@ -1,4 +1,4 @@
import { Player, Token } from "@prisma/client"
import type { Player, Token } from "@prisma/client"
import errors from "../errors"
import prisma from "../../prisma"
import sendError, { API } from "./sendError"

View file

@ -1,4 +1,4 @@
import { Player } from "@prisma/client"
import type { Player } from "@prisma/client"
import errors from "../errors"
import prisma from "../../prisma"
import sendError, { API } from "./sendError"

View file

@ -1,8 +1,8 @@
import type { Token } from "@prisma/client"
import type { IdToken } from "./createTokenDB"
import prisma from "../../prisma"
import { Token } from "@prisma/client"
import errors from "../errors"
import sendError, { API } from "./sendError"
import { IdToken } from "./createTokenDB"
async function getTokenDB(
context: API,

View file

@ -1,26 +1,25 @@
import type { RawToken } from "./createTokenDB"
import sendError, { API } from "./sendError"
import { RawToken } from "./createTokenDB"
import errors from "../errors"
async function getTokenFromBody(
context: API,
next: (accessToken: RawToken) => void
) {
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"
)
return sendError(context, {
message: "Unauthorized. No Access-Token.",
statusCode: 401,
solved: true,
})
const value = body.token
typeof body === "object" &&
body &&
"token" in body &&
typeof body.token === "string"
) {
const value = body.token
return next({ value, type })
}
return next({ value, type: "ACCESS" })
return sendError(context, errors.noToken(type))
}
export default getTokenFromBody

View file

@ -1,17 +1,21 @@
import errors from "../errors"
import { RawToken } from "./createTokenDB"
import sendError, { API } from "./sendError"
import createPlayerDB from "./createPlayerDB"
import createTokenDB, { RawToken } from "./createTokenDB"
import type { API } from "./sendError"
async function getTokenFromCookie(
context: API,
next: (refreshToken: RawToken) => void
) {
const type = "REFRESH"
const value = context.req.cookies.token
// Checking for cookie presens, because it is necessary
if (!value) return sendError(context, errors.noCookie)
return next({ value, type: "REFRESH" })
if (!value) {
return createPlayerDB("anonymous", (player) =>
createTokenDB(player, type, (newToken) => next(newToken))
)
}
return next({ value, type })
}
export default getTokenFromCookie

View file

@ -1,17 +0,0 @@
import { Token } from "@prisma/client"
import { Logging } from "../logging"
export default async function loginCheck<T>(
payload: T & { loginCheck: boolean; tokenDB: Token; tokenType: "REFRESH" }
) {
const { loginCheck, tokenDB } = payload
// True login check response
if (loginCheck) {
return Promise.resolve({
message: "loginCheck " + loginCheck + " of " + tokenDB.id,
body: { loggedIn: true },
type: ["debug", "info.cyan"] as Logging[],
})
}
return payload
}

View file

@ -1,15 +1,16 @@
import { NextApiRequest, NextApiResponse } from "next"
import type { NextApiRequest, NextApiResponse } from "next"
import logging from "../logging"
import type { rejectionError } from "../errors"
export interface API {
req: NextApiRequest
res: NextApiResponse
}
export default function sendError(context: API, err: any) {
export default function sendError(context: API, err: rejectionError | Error) {
const { res, req } = context
// If something went wrong, let the client know with status 500
res.status(err.statusCode ?? 500).end()
logging(err.message, [err.type ?? (err.solved ? "debug" : "error")], req)
if (err.name) console.log(err)
res.status("statusCode" in err ? err.statusCode : 500).end()
logging(err.message, ["solved" in err && err.solved ? "debug" : "error"], req)
if ("name" in err) console.log(err)
}

View file

@ -1,21 +1,23 @@
import { TokenType } from "@prisma/client"
import type { TokenType } from "@prisma/client"
import type { rejectionError } from "./errors"
export default async function jwtVerifyCatch(tokenType: TokenType, err: Error) {
export default function jwtVerifyCatch(
tokenType: TokenType,
err: Error
): rejectionError {
switch (err.message) {
case "jwt expired":
return {
message: `JWT (${tokenType}) expired!`,
statusCode: 403,
solved: true,
type: "warn",
}
case "invalid signature":
return {
message: `Invalid JWT (${tokenType}) signature! Token: `,
statusCode: 401,
solved: true,
type: "error",
solved: false,
}
case "jwt must be provided":
@ -23,11 +25,14 @@ export default async function jwtVerifyCatch(tokenType: TokenType, err: Error) {
message: `No JWT (${tokenType}) given.`,
statusCode: 401,
solved: true,
type: "warn",
}
default:
console.log(err)
return { message: `Unknown error on 'JWT.verify()'.`, solved: false }
return {
statusCode: 500,
message: `Unknown error on 'JWT.verify()'.`,
solved: false,
}
}
}

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useReducer, useState } from "react"
import { useCallback, useEffect, useReducer, useState } from "react"
import {
hitReducer,
initlialLastLeftTile,
@ -13,7 +13,7 @@ import {
Target,
Position,
} from "../../interfaces/frontend"
import { PointerProps } from "../../components/Gamefield/GamefieldPointer"
import type { PointerProps } from "../../components/Gamefield/GamefieldPointer"
const modes: Mode[] = [
{

View file

@ -1,4 +1,4 @@
import { Hit, HitDispatch } from "../../interfaces/frontend"
import type { Hit, HitDispatch } from "../../interfaces/frontend"
export function borderCN(count: number, x: number, y: number) {
if (x === 0) return "left"

View file

@ -1,6 +1,5 @@
import { NextApiRequest, NextApiResponse } from "next"
import { Logging } from "../../lib/backend/logging"
import { Token } from "@prisma/client"
import type { NextApiRequest, NextApiResponse } from "next"
import type { Token } from "@prisma/client"
import sendError from "../../lib/backend/components/sendError"
import getTokenFromCookie from "../../lib/backend/components/getTokenFromCookie"
import checkTokenIsValid from "../../lib/backend/components/checkTokenIsValid"
@ -9,7 +8,7 @@ import getPlayerByIdDB from "../../lib/backend/components/getPlayerByIdDB"
import createTokenDB, {
RawToken,
} from "../../lib/backend/components/createTokenDB"
import sendResponse from "../../lib/backend/components/sendResponse"
import sendResponse, { Result } from "../../lib/backend/components/sendResponse"
interface Data {
token: string
@ -20,12 +19,13 @@ export default async function auth(
res: NextApiResponse<Data>
) {
const context = { req, res }
const type = "ACCESS"
getTokenFromCookie(context, (refreshToken) => {
checkTokenIsValid(context, refreshToken, (token) => {
getTokenDB(context, token, (tokenDB) => {
getPlayerByIdDB(context, tokenDB, (player) => {
createTokenDB(player, "ACCESS", (newToken, newTokenDB) => {
createTokenDB(player, type, (newToken, newTokenDB) => {
sendResponse(context, authResponse(newToken, newTokenDB, tokenDB))
})
})
@ -34,11 +34,15 @@ export default async function auth(
}).catch((err) => sendError(context, err))
}
function authResponse(newToken: RawToken, newTokenDB: Token, tokenDB: Token) {
function authResponse(
newToken: RawToken,
newTokenDB: Token,
tokenDB: Token
): Result<Data> {
// Successfull response
return {
message: `Access-Token generated: ${newTokenDB.id} with Refreshtoken-Token: ${tokenDB.id}`,
body: { token: newToken.value },
type: ["debug", "info.cyan"] as Logging[],
type: ["debug", "infoCyan"],
}
}

View file

@ -1,12 +1,11 @@
import { NextApiRequest, NextApiResponse } from "next"
import type { NextApiRequest, NextApiResponse } from "next"
import type { Game, Player, Token } from "@prisma/client"
import getTokenFromBody from "../../lib/backend/components/getTokenFromBody"
import checkTokenIsValid from "../../lib/backend/components/checkTokenIsValid"
import getTokenDB from "../../lib/backend/components/getTokenDB"
import getPlayerByIdDB from "../../lib/backend/components/getPlayerByIdDB"
import sendResponse from "../../lib/backend/components/sendResponse"
import sendResponse, { Result } from "../../lib/backend/components/sendResponse"
import sendError from "../../lib/backend/components/sendError"
import { Logging } from "../../lib/backend/logging"
import { Game, Player, Token } from "@prisma/client"
interface Data {
games: Game[]
@ -29,12 +28,12 @@ export default async function data(
}).catch((err) => sendError(context, err))
}
function dataResponse<T>(player: Player, tokenDB: Token) {
function dataResponse(player: Player, tokenDB: Token): Result<Data> {
const games: any = {}
// Successfull response
return {
message: `Requested data of user: ${player.id} with Access-Token: ${tokenDB.id}`,
body: { games },
type: ["debug", "info.cyan"] as Logging[],
type: ["debug", "infoCyan"],
}
}

View file

@ -1,14 +1,13 @@
import { NextApiRequest, NextApiResponse } from "next"
import logging, { Logging } from "../../lib/backend/logging"
import type { NextApiRequest, NextApiResponse } from "next"
import type { Player, Token } from "@prisma/client"
import logging from "../../lib/backend/logging"
import getPlayerByNameDB from "../../lib/backend/components/getPlayerByNameDB"
import checkPasswordIsValid from "../../lib/backend/components/checkPasswordIsValid"
import createTokenDB, {
IdToken,
RawToken,
} from "../../lib/backend/components/createTokenDB"
import sendResponse from "../../lib/backend/components/sendResponse"
import sendResponse, { Result } from "../../lib/backend/components/sendResponse"
import { setCookie } from "cookies-next"
import { Player, Token } from "@prisma/client"
import prisma from "../../lib/prisma"
import jwt from "jsonwebtoken"
import errors from "../../lib/backend/errors"
@ -50,7 +49,7 @@ async function preCheck(context: API, next: () => void) {
// Check for old cookie, if unused invalidate it
const tokenData = jwt.verify(
oldRefreshToken,
process.env.ACCESS_TOKEN_SECRET as string
process.env.TOKEN_SECRET as string
)
if (!tokenData || typeof tokenData === "string") return
@ -86,7 +85,7 @@ function loginResponse(
player: Player,
newToken: RawToken,
newTokenDB: Token
) {
): Result<Data> {
const { req, res } = context
// const { player, req, res } = payload
const { value } = newToken
@ -110,6 +109,6 @@ function loginResponse(
" logged in and generated Refresh-Token: " +
newTokenDB.id,
body: { loggedIn: true },
type: ["debug", "info.cyan"] as Logging[],
type: ["debug", "infoCyan"],
}
}

View file

@ -0,0 +1,31 @@
import type { NextApiRequest, NextApiResponse } from "next"
import sendError from "../../lib/backend/components/sendError"
import getTokenFromCookie from "../../lib/backend/components/getTokenFromCookie"
import checkTokenIsValid from "../../lib/backend/components/checkTokenIsValid"
import getTokenDB from "../../lib/backend/components/getTokenDB"
import getPlayerByIdDB from "../../lib/backend/components/getPlayerByIdDB"
import sendResponse from "../../lib/backend/components/sendResponse"
interface Data {
token: string
}
export default async function auth(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const context = { req, res }
getTokenFromCookie(context, (refreshToken) => {
checkTokenIsValid(context, refreshToken, (token) => {
getTokenDB(context, token, (tokenDB) => {
getPlayerByIdDB(context, tokenDB, (player) => {
sendResponse(context, {
message: "loginCheck -> true : " + player.id,
type: ["debug", "infoCyan"],
})
})
})
})
}).catch((err) => sendError(context, err))
}

View file

@ -1,12 +1,11 @@
import { NextApiRequest, NextApiResponse } from "next"
import type { NextApiRequest, NextApiResponse } from "next"
import type { Token } from "@prisma/client"
import checkTokenIsValid from "../../lib/backend/components/checkTokenIsValid"
import sendResponse from "../../lib/backend/components/sendResponse"
import sendResponse, { Result } from "../../lib/backend/components/sendResponse"
import sendError from "../../lib/backend/components/sendError"
import { deleteCookie } from "cookies-next"
import { Token } from "@prisma/client"
import getTokenDB from "../../lib/backend/components/getTokenDB"
import getTokenFromCookie from "../../lib/backend/components/getTokenFromCookie"
import { Logging } from "../../lib/backend/logging"
interface Data {
loggedOut: boolean
@ -31,7 +30,7 @@ function logoutResponse<T>(
tokenDB: Token,
req: NextApiRequest,
res: NextApiResponse<T>
) {
): Result<Data> {
// Set login cookie
deleteCookie("token", { req, res })
@ -39,6 +38,6 @@ function logoutResponse<T>(
return {
message: "User of Token " + tokenDB.id + " logged out.",
body: { loggedOut: true },
type: ["debug", "info.cyan"] as Logging[],
type: ["debug", "infoCyan"],
}
}

View file

@ -1,9 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next"
import type { NextApiRequest, NextApiResponse } from "next"
import type { Player } from "@prisma/client"
import createPlayerDB from "../../lib/backend/components/createPlayerDB"
import sendError from "../../lib/backend/components/sendError"
import sendResponse from "../../lib/backend/components/sendResponse"
import { Logging } from "../../lib/backend/logging"
import { Player } from "@prisma/client"
import sendResponse, { Result } from "../../lib/backend/components/sendResponse"
import getUserFromBody from "../../lib/backend/components/getUserFromBody"
interface Data {
@ -17,18 +16,18 @@ export default async function register(
const context = { req, res }
return getUserFromBody(context, (username, password) =>
createPlayerDB(username, password, (player) => {
createPlayerDB({ username, password }, (player) => {
sendResponse(context, registerResponse(player))
}).catch((err) => sendError(context, err))
)
}
function registerResponse<T>(player: Player) {
function registerResponse(player: Player): Result<Data> {
// Successfull response
return {
message: "Player created : " + player.id,
statusCode: 201,
body: { registered: true },
type: ["debug", "info.cyan"] as Logging[],
type: ["debug", "infoCyan"],
}
}

View file

@ -0,0 +1,40 @@
import { NextApiRequest, NextApiResponse } from "next"
import getPlayerByNameDB from "../../lib/backend/components/getPlayerByNameDB"
import checkPasswordIsValid from "../../lib/backend/components/checkPasswordIsValid"
import sendResponse from "../../lib/backend/components/sendResponse"
import prisma from "../../lib/prisma"
import sendError from "../../lib/backend/components/sendError"
import getUserFromBody from "../../lib/backend/components/getUserFromBody"
interface Data {
loggedIn: boolean
}
export default async function remove(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const context = { req, res }
return getUserFromBody(context, (username, password) =>
getPlayerByNameDB(context, username, (player) => {
checkPasswordIsValid(context, player, password, () => {
prisma.player.update({
where: {
id: player.id,
},
data: {
deleted: true,
username: null,
email: null,
passwordHash: null,
},
})
sendResponse(context, {
message: "User successfully deleted: " + player.id,
type: ["debug", "infoCyan"],
})
})
})
).catch((err) => sendError(context, err))
}

View file

@ -1,6 +1,6 @@
import type { NextApiRequest } from "next"
import type { NextApiResponseWithSocket } from "../../interfaces/NextApiSocket"
import { Server } from "socket.io"
import { NextApiResponseWithSocket } from "../../interfaces/NextApiSocket"
import jwt from "jsonwebtoken"
import prisma from "../../lib/prisma"
@ -41,7 +41,7 @@ async function checkTokenIsValid(rawToken: string) {
// Verify the token and get the payload
let tokenData: string | jwt.JwtPayload
try {
tokenData = jwt.verify(rawToken, process.env.ACCESS_TOKEN_SECRET as string)
tokenData = jwt.verify(rawToken, process.env.TOKEN_SECRET as string)
} catch (err: any) {
// Deal with the problem in more detail
return Promise.reject()

View file

@ -1,7 +1,7 @@
import { ChangeEventHandler, useEffect, useState } from "react"
import { io, Socket } from "socket.io-client"
import getAccessToken from "../../lib/frontend/getAccessToken"
import { ClientToServerEvents, ServerToClientEvents } from "../api/ws"
import type { ClientToServerEvents, ServerToClientEvents } from "../api/ws"
let socket: Socket<ServerToClientEvents, ClientToServerEvents>
const Home = () => {

View file

@ -11,10 +11,11 @@ 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 @default("")
passwordHash String? @unique
tokens Token[]
games Player_Game[]
}