From f54d19636f05904f65094ee92811fcbed42ce784 Mon Sep 17 00:00:00 2001 From: IceTeaXXD <13521024@std.stei.itb.ac.id> Date: Wed, 15 Nov 2023 23:28:10 +0700 Subject: [PATCH] feat: redis --- controllers/auth.controller.ts | 170 +++++++++++++++++++++++++-------- package.json | 2 + redis.ts | 11 +++ server.ts | 15 +++ yarn.lock | 58 ++++++++++- 5 files changed, 217 insertions(+), 39 deletions(-) create mode 100644 redis.ts diff --git a/controllers/auth.controller.ts b/controllers/auth.controller.ts index 2a07a02..739a867 100644 --- a/controllers/auth.controller.ts +++ b/controllers/auth.controller.ts @@ -8,8 +8,9 @@ import { TokenExpiredError, JsonWebTokenError } from "jsonwebtoken" - const prismaClient = new PrismaClient() +import { client } from "../redis" +import { promisify } from "util" const generateAccessToken = ( user_id: number, @@ -190,10 +191,7 @@ export const handleLogout = async ( } } -export const handleRefreshToken = async ( - req: Request, - res: Response -): Promise<void> => { +export const handleRefreshToken = async (req: Request, res: Response) => { const cookies = req.cookies if (!cookies?.jwt) { res.sendStatus(401) @@ -201,48 +199,144 @@ export const handleRefreshToken = async ( } const refreshToken: string = cookies.jwt + try { - const findUser = await prismaClient.user.findFirst({ - where: { - refreshToken: refreshToken - } - }) + const redisData = await client.get(refreshToken) - if (!findUser) { - res.sendStatus(403) - return - } + console.log("handleRefreshToken") + if (redisData) { + // Data found in Redis + console.log("Data found in Redis") + const decoded = JSON.parse(redisData) + const { user_id, email, name, roles, accessToken } = decoded + res.cookie("accToken", accessToken, { + maxAge: 2 * 60 * 1000 + }) + res.json({ user_id, email, name, roles, accessToken }) + } else { + // Data not found in Redis + console.log("Data not found in Redis") + try { + const findUser = await prismaClient.user.findFirst({ + where: { + refreshToken: refreshToken + } + }) - verify( - refreshToken, - process.env.REFRESH_TOKEN_SECRET as string, - (err, decoded: any) => { - if (err || !findUser || findUser.email !== decoded.email) { + if (!findUser) { res.sendStatus(403) return } - const name = findUser.name - const email = findUser.email - const roles = findUser.role - const accessTokenSecret: string = String( - process.env.ACCESS_TOKEN_SECRET - ) - const accessToken = generateAccessToken( - findUser.user_id, - findUser.name, - findUser.email, - roles, - accessTokenSecret + + verify( + refreshToken, + process.env.REFRESH_TOKEN_SECRET as string, + (err, decoded: any) => { + if (err || !findUser || findUser.email !== decoded.email) { + res.sendStatus(403) + return + } + const name = findUser.name + const email = findUser.email + const roles = findUser.role + const accessTokenSecret: string = String( + process.env.ACCESS_TOKEN_SECRET + ) + const accessToken = generateAccessToken( + findUser.user_id, + findUser.name, + findUser.email, + roles, + accessTokenSecret + ) + // Save the refreshToken in Redis + client.set( + refreshToken, + JSON.stringify({ + user_id: findUser.user_id, + email, + name, + roles, + accessToken + }) + ) + res.cookie("accToken", accessToken, { + maxAge: 2 * 60 * 1000 + }) + res.json({ + user_id: findUser.user_id, + email, + name, + roles, + accessToken + }) + } ) - res.cookie("accToken", accessToken, { - maxAge: 2 * 60 * 1000 - }) - res.json({ user_id: findUser.user_id, email, name, roles, accessToken }) + } catch (error) { + console.error(error) + res.sendStatus(500) + return } - ) - } catch (error) { - console.error(error) + } + } catch (redisErr) { + console.error("Error reading from Redis:", redisErr) res.sendStatus(500) return } } +// export const handleRefreshToken = async ( +// req: Request, +// res: Response +// ): Promise<void> => { +// const cookies = req.cookies +// if (!cookies?.jwt) { +// res.sendStatus(401) +// return +// } + +// const refreshToken: string = cookies.jwt +// try { +// const findUser = await prismaClient.user.findFirst({ +// where: { +// refreshToken: refreshToken +// } +// }) + +// if (!findUser) { +// res.sendStatus(403) +// return +// } + +// verify( +// refreshToken, +// process.env.REFRESH_TOKEN_SECRET as string, +// (err, decoded: any) => { +// if (err || !findUser || findUser.email !== decoded.email) { +// res.sendStatus(403) +// return +// } +// const name = findUser.name +// const email = findUser.email +// const roles = findUser.role +// const accessTokenSecret: string = String( +// process.env.ACCESS_TOKEN_SECRET +// ) +// const accessToken = generateAccessToken( +// findUser.user_id, +// findUser.name, +// findUser.email, +// roles, +// accessTokenSecret +// ) +// res.cookie("accToken", accessToken, { +// maxAge: 2 * 60 * 1000 +// }) +// res.json({ user_id: findUser.user_id, email, name, roles, accessToken }) +// } +// ) +// } catch (error) { +// console.error(error) +// res.sendStatus(500) +// return +// } +// } diff --git a/package.json b/package.json index 6a4fa6c..f3cc10c 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@prisma/client": "^5.5.2", + "@redis/client": "^1.5.11", "@types/bcrypt": "^5.0.1", "@types/cookie": "^0.5.3", "@types/js-cookie": "^3.0.5", @@ -45,6 +46,7 @@ "multer": "^1.4.5-lts.1", "mysql2": "^3.6.2", "nodemon": "^3.0.1", + "redis": "^4.6.10", "util": "^0.12.5", "xml2js": "^0.6.2" } diff --git a/redis.ts b/redis.ts new file mode 100644 index 0000000..bfd3d96 --- /dev/null +++ b/redis.ts @@ -0,0 +1,11 @@ +import { createClient } from 'redis'; +import dotenv from 'dotenv'; + +dotenv.config(); + +export const client = createClient({ + socket: { + host: "127.0.0.1", + port: 6379, + } +}); diff --git a/server.ts b/server.ts index a4833fa..dd72027 100644 --- a/server.ts +++ b/server.ts @@ -2,6 +2,7 @@ import verifyJWT from "./middleware/verifyJWT" import express from "express" import cors from "cors" import dotenv from "dotenv" +import { client } from "./redis" import { sync } from "./polling/sync" dotenv.config() @@ -51,3 +52,17 @@ app.listen(PORT, () => { console.log(`âš¡ï¸[server]: Server is running at http://localhost:${PORT}`) sync() }) + +const start = async () => { + try { + await client.connect() + app.listen(PORT, () => { + console.log(`âš¡ï¸[server]: Server is running at http://localhost:${PORT}`) + sync() + }) + } catch (error) { + console.error(error) + } +} + +start() diff --git a/yarn.lock b/yarn.lock index a0fe20f..4849081 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,6 +59,40 @@ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.5.2.tgz#fe0d2361a48c7d59568ccf0d35c75432594e1ac1" integrity sha512-Be5hoNF8k+lkB3uEMiCHbhbfF6aj1GnrTBnn5iYFT7GEr3TsOEp1soviEcBR0tYCgHbxjcIxJMhdbvxALJhAqg== +"@redis/bloom@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" + integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== + +"@redis/client@1.5.11", "@redis/client@^1.5.11": + version "1.5.11" + resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.11.tgz#5ee8620fea56c67cb427228c35d8403518efe622" + integrity sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA== + dependencies: + cluster-key-slot "1.1.2" + generic-pool "3.9.0" + yallist "4.0.0" + +"@redis/graph@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519" + integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg== + +"@redis/json@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.6.tgz#b7a7725bbb907765d84c99d55eac3fcf772e180e" + integrity sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw== + +"@redis/search@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.5.tgz#682b68114049ff28fdf2d82c580044dfb74199fe" + integrity sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg== + +"@redis/time-series@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.5.tgz#a6d70ef7a0e71e083ea09b967df0a0ed742bc6ad" + integrity sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -427,6 +461,11 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +cluster-key-slot@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + color-support@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -776,6 +815,11 @@ generate-function@^2.3.1: dependencies: is-property "^1.0.2" +generic-pool@3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" + integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== + get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" @@ -1478,6 +1522,18 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redis@^4.6.10: + version "4.6.10" + resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.10.tgz#07f6ea2b2c5455b098e76d1e8c9b3376114e9458" + integrity sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg== + dependencies: + "@redis/bloom" "1.2.0" + "@redis/client" "1.5.11" + "@redis/graph" "1.1.0" + "@redis/json" "1.0.6" + "@redis/search" "1.1.5" + "@redis/time-series" "1.0.5" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -1827,7 +1883,7 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@^4.0.0: +yallist@4.0.0, yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -- GitLab