diff --git a/libs/middlewares/authMiddleware.ts b/libs/middlewares/authMiddleware.ts new file mode 100644 index 0000000..4271e1b --- /dev/null +++ b/libs/middlewares/authMiddleware.ts @@ -0,0 +1,38 @@ +import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; +import jwt from "jsonwebtoken" +import 'dotenv/config' + +const secret_key = process.env.SECRET_KEY || ""; +export async function middleware( + req: NextRequest, + ev: NextFetchEvent, +){ + try { + if(req.cookies.has('accessToken')){ + const accessToken = req.cookies.get('accessToken'); + if(accessToken !== undefined){ + jwt.verify(accessToken, secret_key, (err, decoded) => { + if(err){ + return new Response( + JSON.stringify({err}), + { status: 401 }, + ); + }; + return NextResponse.next(); + }) + }else{ + return new Response( + JSON.stringify({"message":"로그인이 필요한 기능입니다."}), + { status: 401 }, + ); + } + } + } catch (error:any) { + return new Response( + JSON.stringify({error}), + { status: 401 }, + ); + } + + +} diff --git a/libs/middlewares/authWithRefreshToken.ts b/libs/middlewares/authWithRefreshToken.ts new file mode 100644 index 0000000..a79cf8f --- /dev/null +++ b/libs/middlewares/authWithRefreshToken.ts @@ -0,0 +1,45 @@ +import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; +// import { setCookie } from '../../utils/cookies' +import client from "../client"; +import jwt from "jsonwebtoken" +import 'dotenv/config' + +const secret_key = process.env.SECRET_KEY || ""; + +let userId: string | object | Buffer; + +export const authNoRefreshToken = async ( + req: NextRequest, + ev: NextFetchEvent, +) => { + try { + if(req.cookies.has('refreshToken')){ + const refreshToken = req.cookies.get('refreshToken'); + if(refreshToken !== undefined){ + const identifier = jwt.verify(refreshToken, secret_key); + + if(!identifier){ + throw new Error("토큰이 존재하지 않습니다."); + }; + + const user = await client.user.findUnique({where:{identifier}}); + if(user == null){ + throw new Error("토큰이 유효하지 않습니다. 다시 로그인해 주십시오."); + }; + + if(user?.refreshToken !== refreshToken){ + throw new Error("서버에 저장된 토큰 정보와 일치하지 않습니다. 다시 로그인해 주십시오."); + }; + + const response = NextResponse.next(); + response.cookies.set('accessToken', jwt.sign(userId, secret_key, {expiresIn: '1h'})); // body에 담아주도록 수정 + + return response; + } + }else{ + throw new Error("토큰이 존재하지 않습니다."); + } + } catch (error:any) { + throw new Error(error) + } +} \ No newline at end of file diff --git a/libs/middlewares/isLogin.ts b/libs/middlewares/isLogin.ts new file mode 100644 index 0000000..16f58f9 --- /dev/null +++ b/libs/middlewares/isLogin.ts @@ -0,0 +1,32 @@ +import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; +import client from "../client"; +import jwt from "jsonwebtoken" +// import cookies from 'next-cookies'; + +import 'dotenv/config' +const secret_key = process.env.SECRET_KEY || ""; + +export function middleware( + req: NextRequest, + ev: NextFetchEvent, + ) { + try { + if(!req.cookies.has('accessToken')){ + return NextResponse.rewrite(new URL('/login', req.url)); + } else { + return new Response( + JSON.stringify({"message": "이미 로그인된 상태입니다."}), + { status: 401 }, + ) + } + } catch (error) { + return new Response( + JSON.stringify({error}), + { status: 400 }, + ) + } + } + + export const config = { + matcher: ['/api/user/login', '/api/user/signup'] + } \ No newline at end of file diff --git a/libs/middlewares/validation.ts b/libs/middlewares/validation.ts new file mode 100644 index 0000000..750debd --- /dev/null +++ b/libs/middlewares/validation.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; +import Joi from "joi"; + +export function joiValidation( + req: NextRequest, + ev: NextFetchEvent, + ) { + const emailRegExp = /[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]$/i; + const passwordRegExp = /(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])/i; + const schema = Joi.object().keys({ + email: Joi.string().regex(emailRegExp).email(), + password: Joi.string().regex(passwordRegExp) + }); + try { + schema.validate(req.body) + } catch (error) { + return new Response( + JSON.stringify({error}), + { status: 400 }, + ) + } + + return NextResponse.rewrite(new URL('/home', req.nextUrl)); // 경로 수정 필요 + + } +export const config = { + matcher: ['/api/user/signup'] +} + diff --git a/package-lock.json b/package-lock.json index a915877..6f3fb97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,13 +10,21 @@ "dependencies": { "@prisma/client": "^4.5.0", "@tanstack/react-query": "^4.14.1", + "@types/sanitize-html": "^2.6.2", + "bcryptjs": "^2.4.3", + "joi": "^17.7.0", "jsonwebtoken": "^8.5.1", "next": "13.0.0", + "next-connect": "^0.13.0", + "next-cookies": "^2.0.3", + "next-joi": "^2.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-hook-form": "^7.38.0" + "react-hook-form": "^7.38.0", + "sanitize-html": "^2.7.3" }, "devDependencies": { + "@types/bcryptjs": "^2.4.2", "@types/jsonwebtoken": "^8.5.9", "@types/node": "^18.11.8", "@types/react": "18.0.24", @@ -93,6 +101,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", @@ -433,6 +454,24 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "dev": true }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@swc/helpers": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", @@ -500,6 +539,17 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -547,6 +597,80 @@ "@types/react": "*" } }, + "node_modules/@types/sanitize-html": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.6.2.tgz", + "integrity": "sha512-7Lu2zMQnmHHQGKXVvCOhSziQMpa+R2hMHFefzbYoYMHeaXR0uXqNeOc3JeQQQ8/6Xa2Br/P1IQTLzV09xxAiUQ==", + "dependencies": { + "htmlparser2": "^6.0.0" + } + }, + "node_modules/@types/sanitize-html/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@types/sanitize-html/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -926,6 +1050,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1176,6 +1305,14 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-pure": { "version": "3.26.0", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", @@ -1282,6 +1419,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -1396,7 +1541,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, "funding": [ { "type": "github", @@ -1542,7 +1686,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -2607,6 +2750,14 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -2683,6 +2834,18 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/joi": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz", + "integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -3051,6 +3214,31 @@ } } }, + "node_modules/next-connect": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-0.13.0.tgz", + "integrity": "sha512-f2G4edY01XomjCECSrgOpb/zzQinJO6Whd8Zds0+rLUYhj5cLwkh6FVvZsQCSSbxSc4k9nCwNuk5NLIhvO1gUA==", + "dependencies": { + "trouter": "^3.2.0" + } + }, + "node_modules/next-cookies": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/next-cookies/-/next-cookies-2.0.3.tgz", + "integrity": "sha512-YVCQzwZx+sz+KqLO4y9niHH9jjz6jajlEQbAKfsYVT6DOfngb/0k5l6vFK4rmpExVug96pGag8OBsdSRL9FZhQ==", + "dependencies": { + "universal-cookie": "^4.0.2" + } + }, + "node_modules/next-joi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/next-joi/-/next-joi-2.2.1.tgz", + "integrity": "sha512-m6/rDj9a9sp0CeMGy3np/7T2663QFinfiTY4MuJ9LEicU+6SiDim4wnsqG5CfzI4IQX4tupN6jSCtsv0t2EWnQ==", + "peerDependencies": { + "joi": ">=17.1.1", + "next": ">=9.5.1" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -3293,6 +3481,11 @@ "node": ">=6" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, "node_modules/parse5": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz", @@ -3390,7 +3583,6 @@ "version": "8.4.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -3685,6 +3877,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-1.3.0.tgz", + "integrity": "sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g==", + "engines": { + "node": ">=6" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -3804,6 +4004,85 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sanitize-html": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.7.3.tgz", + "integrity": "sha512-jMaHG29ak4miiJ8wgqA1849iInqORgNv7SLfSw9LtfOhEUQ1C0YHKH73R+hgyufBW9ZFeJrb057k9hjlfBCVlw==", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^6.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/sanitize-html/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -4064,6 +4343,17 @@ "node": ">=8.0" } }, + "node_modules/trouter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-3.2.0.tgz", + "integrity": "sha512-rLLXbhTObLy2MBVjLC+jTnoIKw99n0GuJs9ov10J870vDw5qhTurPzsDrudNtBf5w/CZ9ctZy2p2IMmhGcel2w==", + "dependencies": { + "regexparam": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -4212,6 +4502,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "dependencies": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -4405,6 +4704,19 @@ "strip-json-comments": "^3.1.1" } }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@humanwhocodes/config-array": { "version": "0.11.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", @@ -4593,6 +4905,24 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "dev": true }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "@swc/helpers": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", @@ -4639,6 +4969,17 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, + "@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -4686,6 +5027,60 @@ "@types/react": "*" } }, + "@types/sanitize-html": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.6.2.tgz", + "integrity": "sha512-7Lu2zMQnmHHQGKXVvCOhSziQMpa+R2hMHFefzbYoYMHeaXR0uXqNeOc3JeQQQ8/6Xa2Br/P1IQTLzV09xxAiUQ==", + "requires": { + "htmlparser2": "^6.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } + } + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -4940,6 +5335,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5116,6 +5516,11 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, "core-js-pure": { "version": "3.26.0", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", @@ -5191,6 +5596,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -5274,8 +5684,7 @@ "domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" }, "domhandler": { "version": "5.0.3", @@ -5384,8 +5793,7 @@ "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { "version": "8.26.0", @@ -6156,6 +6564,11 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -6208,6 +6621,18 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "joi": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz", + "integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -6506,6 +6931,28 @@ } } }, + "next-connect": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-0.13.0.tgz", + "integrity": "sha512-f2G4edY01XomjCECSrgOpb/zzQinJO6Whd8Zds0+rLUYhj5cLwkh6FVvZsQCSSbxSc4k9nCwNuk5NLIhvO1gUA==", + "requires": { + "trouter": "^3.2.0" + } + }, + "next-cookies": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/next-cookies/-/next-cookies-2.0.3.tgz", + "integrity": "sha512-YVCQzwZx+sz+KqLO4y9niHH9jjz6jajlEQbAKfsYVT6DOfngb/0k5l6vFK4rmpExVug96pGag8OBsdSRL9FZhQ==", + "requires": { + "universal-cookie": "^4.0.2" + } + }, + "next-joi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/next-joi/-/next-joi-2.2.1.tgz", + "integrity": "sha512-m6/rDj9a9sp0CeMGy3np/7T2663QFinfiTY4MuJ9LEicU+6SiDim4wnsqG5CfzI4IQX4tupN6jSCtsv0t2EWnQ==", + "requires": {} + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -6662,6 +7109,11 @@ "callsites": "^3.0.0" } }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, "parse5": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz", @@ -6732,7 +7184,6 @@ "version": "8.4.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", - "dev": true, "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -6908,6 +7359,11 @@ "functions-have-names": "^1.2.2" } }, + "regexparam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-1.3.0.tgz", + "integrity": "sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g==" + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -6971,6 +7427,65 @@ "is-regex": "^1.1.4" } }, + "sanitize-html": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.7.3.tgz", + "integrity": "sha512-jMaHG29ak4miiJ8wgqA1849iInqORgNv7SLfSw9LtfOhEUQ1C0YHKH73R+hgyufBW9ZFeJrb057k9hjlfBCVlw==", + "requires": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^6.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + }, + "dependencies": { + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } + } + }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -7153,6 +7668,14 @@ "is-number": "^7.0.0" } }, + "trouter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-3.2.0.tgz", + "integrity": "sha512-rLLXbhTObLy2MBVjLC+jTnoIKw99n0GuJs9ov10J870vDw5qhTurPzsDrudNtBf5w/CZ9ctZy2p2IMmhGcel2w==", + "requires": { + "regexparam": "^1.3.0" + } + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -7255,6 +7778,15 @@ "which-boxed-primitive": "^1.0.2" } }, + "universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "requires": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, "update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", diff --git a/package.json b/package.json index 74b7963..1f9d2a7 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,21 @@ "dependencies": { "@prisma/client": "^4.5.0", "@tanstack/react-query": "^4.14.1", + "@types/sanitize-html": "^2.6.2", + "bcryptjs": "^2.4.3", + "joi": "^17.7.0", "jsonwebtoken": "^8.5.1", "next": "13.0.0", + "next-connect": "^0.13.0", + "next-cookies": "^2.0.3", + "next-joi": "^2.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-hook-form": "^7.38.0" + "react-hook-form": "^7.38.0", + "sanitize-html": "^2.7.3" }, "devDependencies": { + "@types/bcryptjs": "^2.4.2", "@types/jsonwebtoken": "^8.5.9", "@types/node": "^18.11.8", "@types/react": "18.0.24", diff --git a/pages/api/login.ts b/pages/api/login.ts deleted file mode 100644 index b02b65d..0000000 --- a/pages/api/login.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from "next"; -import client from "../../libs/client"; - -// 크롤링 -/* import axios from 'axios'; -const cheerio = require('cheerio'); */ - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - const { email, password, nickname } = req.body; - try { - const findOneUser = await client.user.findUnique({ - where: { - email: email, - }, - }); - console.log(findOneUser); - - const result = await client.user.create({ - data: { - email: email + "qw", - identifier: "testtest", - password: password, - nickname: nickname + "qw", - profileImg: "../test", - }, - }); - - res.status(200).send("후잉"); - } catch (error) { - console.log(error); - res.status(200).send({ result: "실패" }); - } -} - -// userId Int @id @default(autoincrement()) -// email String @unique -// identifier String -// nickname String @unique -// password String -// profileImg String ? -// refreshToken String ? -// createdAt DateTime @default(now()) -// updatedAt DateTime @updatedAt diff --git a/pages/api/middlewares/_authNoRefreshToken.ts b/pages/api/middlewares/_authNoRefreshToken.ts deleted file mode 100644 index c4f48cd..0000000 --- a/pages/api/middlewares/_authNoRefreshToken.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -// import { setCookie } from '../../utils/cookies' -import client from "../../../libs/client"; -import jwt from "jsonwebtoken" -import 'dotenv/config' - -const secret_key = process.env.SECRET_KEY || ""; -export const authNoRefreshToken = async ( - req: NextApiRequest, - res: NextApiResponse, -) => { - if(req.cookies.accessToken){ - const accessToken = req.cookies.accessToken; - // let userId: string | jwt.JwtPayload | undefined; - - jwt.verify(accessToken, secret_key, (err, decoded) => { - if(err){ - res.status(400).json({"message": "accessToken이 유효하지 않습니다.", errorMessage: err}) - }; - // The if (res.locals) in itself is problematic, because res.locals is an Express convention. - // This condition may be always false if you use another Node framework. - // userId = decoded; - }) - }else{ - res.status(400).json({"message": "로그인이 필요합니다."}); - } - -} \ No newline at end of file diff --git a/pages/api/middlewares/authWithRefreshToken.ts b/pages/api/middlewares/authWithRefreshToken.ts deleted file mode 100644 index f30756d..0000000 --- a/pages/api/middlewares/authWithRefreshToken.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { setCookie } from '../../utils/cookies' -import client from "../../../libs/client"; -import jwt from "jsonwebtoken" -import 'dotenv/config' - -const secret_key = process.env.SECRET_KEY || ""; -let userId: string | object | Buffer; - -export const authNoRefreshToken = async ( - req: NextApiRequest, - res: NextApiResponse, -) => { - if(req.cookies.refreshToken){ - const refreshToken = req.cookies.refreshToken; - const identifier = jwt.verify(refreshToken, secret_key); - if(!identifier){ - res.status(400).json({"message": "토큰이 존재하지 않습니다. "}) - } - - const user = await client.user.findUnique({where:{identifier}}); - - if(user == null){ - res.status(400).json({"message": "토큰이 유효하지 않습니다. 다시 로그인해 주십시오."}) - }; - - if(user?.refreshToken !== refreshToken){ - res.status(400).json({"message": "서버에 저장된 토큰 정보와 일치하지 않습니다. 다시 로그인해 주십시오."}) - } - // The if (res.locals) in itself is problematic, because res.locals is an Express convention. - // This condition may be always false if you use another Node framework. - - setCookie(res, 'accessToken', jwt.sign(userId, secret_key, {expiresIn: '1h'})) - res.getHeader('Set-Cookie') - - }else{ - res.status(400).json({"message": "로그인이 필요합니다."}); - } -} \ No newline at end of file diff --git a/pages/api/user/_isLogin.ts b/pages/api/user/_isLogin.ts deleted file mode 100644 index 4dc2a7b..0000000 --- a/pages/api/user/_isLogin.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { NextResponse } from "next/server"; -import client from "../../../libs/client"; -import jwt from "jsonwebtoken" -import 'dotenv/config' -const secret_key = process.env.SECRET_KEY || ""; - -export const isLogin = async ( - req: NextApiRequest, - res: NextApiResponse, - ) => { - const accessToken = req.cookies.accessToken; - if(accessToken){ - jwt.verify(accessToken, secret_key, (err, decoded) => { - if(err){ - res.status(400).send(false); - } - res.status(200).send(true); - }); - } - } - - export const isLogout = async ( - req: NextApiRequest, - res: NextApiResponse, - ) => { - const accessToken = req.cookies.accessToken; - if(accessToken){ - jwt.verify(accessToken, secret_key, (err, decoded) => { - if(err){ - res.status(200).send(true); - } - - res.status(400).send(false); - }); - } - } diff --git a/pages/api/user/login.ts b/pages/api/user/login.ts index 5528f70..617b516 100644 --- a/pages/api/user/login.ts +++ b/pages/api/user/login.ts @@ -2,7 +2,8 @@ import type { NextApiRequest, NextApiResponse } from "next"; import client from "../../../libs/client"; import jwt from "jsonwebtoken"; -import 'dotenv/config' +import 'dotenv/config'; +import bcrypt from "bcryptjs" const secret_key = process.env.SECRET_KEY || "" interface reqBody { email: string | undefined @@ -13,47 +14,49 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { - const { email, password }:reqBody = req.body; - + const { email, password }: reqBody = req.body; + try { if(!email||!password){ throw new Error("아이디 혹은 비밀번호를 입력해 주세요") } const user = await client.user.findUnique({where:{email}}); - const identifier = user?.identifier - /**accessToken 발급 */ - if(identifier == undefined){ - res.status(400).send("유저 정보가 없습니다."); + const userId = user?.userId; + const encPass = user?.password; + + if(userId == undefined){ + res.status(400).send("이메일 또는 패스워드가 일치하지 않습니다."); }; + if(encPass !== undefined){ + const re = await bcrypt.compare(encPass, password); + if(!re){ + res.status(400).send("이메일 또는 패스워드가 일치하지 않습니다."); + } + }else { + res.status(400).send("존재하지 않는 유저입니다."); + } + const accessToken = jwt.sign( - {identifier}, + {userId}, secret_key, { expiresIn: '1h' } ); - /**refreshToken 발급 */ const refreshToken = jwt.sign( - {identifier}, + {userId}, secret_key, { expiresIn: '7d' } ); - await client.user.update({where:{identifier},data:{refreshToken}}); + await client.user.update({where:{userId},data:{refreshToken}}); + + res.setHeader('Set-Cookie', `accessToken: ${accessToken}`); + res.status(200).json({refreshToken}); - res.status(200).json({accessToken,refreshToken}) } catch (error) { - res.status(400).json({ error, result: "실패" }); + res.status(400).json({ error, result: "로그인에 실패하였습니다." }); } } -// userId Int @id @default(autoincrement()) -// email String @unique -// identifier String -// nickname String @unique -// password String -// profileImg String ? -// refreshToken String ? -// createdAt DateTime @default(now()) -// updatedAt DateTime @updatedAt diff --git a/pages/api/user/signup.ts b/pages/api/user/signup.ts index e8041fe..caef170 100644 --- a/pages/api/user/signup.ts +++ b/pages/api/user/signup.ts @@ -1,40 +1,34 @@ import type { NextApiRequest, NextApiResponse } from "next"; import client from "../../../libs/client"; - - -function makeid(length:number) { - var result = ''; - var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - var charactersLength = characters.length; - for ( var i = 0; i < length; i++ ) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; -} - +import bcrypt from 'bcryptjs' export default async function handler( req: NextApiRequest, res: NextApiResponse ) { - const {email,nickname,password,confirmPassword}=req.body; - const user = await client.user.findUnique({ - where:{email} - }) - if(password!==confirmPassword){ - res.status(400).send("비밀번호를 다시 확인 해주세요.") - } - if(user){ - res.status(400).send("중복된 이메일 입니다.") - } - await client.user.create({ - data:{ - email, - nickname, - password, - identifier:makeid(7) - } - }) + const {email, password, size}=req.body; + + try { + const user = await client.user.findUnique({ + where:{email} + }) - res.status(200).send("회원가입이 완료 되었습니다.") -} \ No newline at end of file + if(user){ + res.status(400).send("중복된 이메일 입니다.") + } + + const encPass = await bcrypt.hash(password, 10); + const nickname = email.split("@")[0] + await client.user.create({ + data:{ + email, + nickname, + size, + password: encPass + } + }) + res.status(200).send("회원가입이 완료 되었습니다.") + } catch (error) { + res.status(400).json(error) + } +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6fe40b7..3577639 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,11 +19,10 @@ datasource db { model User { userId Int @id @default(autoincrement()) email String @unique - identifier String @unique - nickname String @unique + nickname String? @unique password String - profileImg String ? - refreshToken String ? + refreshToken String? + size Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt comment Comment[] @@ -31,45 +30,38 @@ model User { } model Product { - productId Int @id @default(autoincrement()) - identifier String @unique - name String + productId Int @id @default(autoincrement()) price Int - koeanDesc String? - size Int? - releasedDate DateTime? @default(now()) - color String? - likeNumber Int? @default(0) + nameEng String brand String - category String? - gender String? + nameKr String + quickdlivery Boolean + imgUrl String @db.VarChar(500) + pid String createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt } model Style { styleId Int @id @default(autoincrement()) - identifier String @unique profileImg String styleImg String hashtag String like Int content String - user User @relation(fields: [userId], references: [identifier],onDelete: Cascade) - userId String + user User @relation(fields: [userId], references: [userId],onDelete: Cascade) + userId Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt comment Comment[] } model Comment { - commentId Int @id @default(autoincrement()) - identifier String @unique + commentId Int @id @default(autoincrement()) comment String - user User @relation(fields: [userId], references: [identifier],onDelete: Cascade) - userId String - style Style @relation(fields: [styleId], references: [identifier],onDelete: Cascade) - styleId String + user User @relation(fields: [userId], references: [userId], onDelete: Cascade) + userId Int + style Style @relation(fields: [styleId], references: [styleId], onDelete: Cascade) + styleId Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } \ No newline at end of file