From 9802f52860c0a7add7e87cacbc08ee988b22c06f Mon Sep 17 00:00:00 2001 From: grulla79 Date: Tue, 9 Jul 2024 23:23:29 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=94=A5=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/user/user.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/user/user.service.js b/src/routes/user/user.service.js index 53fb77e..f1a1ae1 100644 --- a/src/routes/user/user.service.js +++ b/src/routes/user/user.service.js @@ -83,7 +83,6 @@ exports.updateRequest = async (requestId, formData) => { exports.updateRequestState = async (userId, requestId, status, formData, requestType) => { if (userId) { - console.log("업데이트 서비스 진입!!!!!!!!!!!!", userId, requestId, status, formData, requestType) await userRepository.updateRequestState(userId, requestId, status, formData); if (requestType === 'add') { await wordRepository.addWord(requestId, formData); From 4dff4d55516977803aed5978392d541cf10e045a Mon Sep 17 00:00:00 2001 From: grulla79 Date: Thu, 11 Jul 2024 15:45:04 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=A8=EB=8B=A8=EC=96=B4=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=A4=91=EB=B3=B5=EA=B2=80=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/constants/error-message.js | 2 ++ src/common/constants/success-message.js | 3 +++ src/routes/word/word.controller.js | 28 +++++++++++++++++++++++++ src/routes/word/word.repository.js | 12 +++++++++++ src/routes/word/word.route.js | 5 ++++- src/routes/word/word.service.js | 7 +++++++ 6 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/common/constants/error-message.js b/src/common/constants/error-message.js index 0261a19..839b136 100644 --- a/src/common/constants/error-message.js +++ b/src/common/constants/error-message.js @@ -34,6 +34,8 @@ const ErrorMessage = Object.freeze({ RANK_WORDS_ERROR: '인기 검색어 조회 중 오류가 발생하였습니다.', RELATED_WORDS_ERROR: '연관 검색어 조회 중 오류가 발생하였습니다.', REGISTER_WORDS_ERROR: '등록 요청 중 오류가 발생하였습니다.', + EXIST_WORD: '이미 존재하는 단어입니다.', + CHECK_DUPLICATE_WORD_ERROR: '단어 중복검사중 오류가 발생하였습니다.', // REQUEST GET_REQUESTS_ERROR: '요청 조회중 오류가 발생하였습니다.', diff --git a/src/common/constants/success-message.js b/src/common/constants/success-message.js index e9575e3..be55de1 100644 --- a/src/common/constants/success-message.js +++ b/src/common/constants/success-message.js @@ -34,6 +34,9 @@ const SuccessMessage = Object.freeze({ // Word - 등록요청 REGISTER_WORDS_SUCCESS: '등록 요청 성공', + + // Word - 중복검사 + CHECK_DUPLICATE_REQUEST_SUCCESS: '중복 검사 성공', }); module.exports = SuccessMessage; diff --git a/src/routes/word/word.controller.js b/src/routes/word/word.controller.js index fc0c60e..74e0dd0 100644 --- a/src/routes/word/word.controller.js +++ b/src/routes/word/word.controller.js @@ -87,3 +87,31 @@ exports.getAllWords = async (req, res) => { sendResponse.fail(req, res, ErrorMessage.GET_WORDS_ERROR); } }; + +// 등록요청 중복 단어 검사 +exports.checkDuplicateWord = async (req, res) => { + try { + const { word } = req.body; // req.body 에서 word 추출 + console.log("전달된 중복 검수 단어", ); + const isDataExist = await wordService.checkDuplicateWord(word); + data = { isDataExist }; + console.log("중복검수 결과", data); + + // 중복 단어가 있는 경우 + if (isDataExist) { + return sendResponse.ok(res, { + message: ErrorMessage.EXIST_WORD, + data, + }); + } + // 중복 단어가 없는 경우 + return sendResponse.ok(res, { + message: SuccessMessage.CHECK_DUPLICATE_REQUEST_SUCCESS, + data, + }); + + } catch (error) { + sendResponse.fail(req, res, ErrorMessage.CHECK_DUPLICATE_REQUEST_ERROR); + } +} + diff --git a/src/routes/word/word.repository.js b/src/routes/word/word.repository.js index ec7ad47..c5cb8dd 100644 --- a/src/routes/word/word.repository.js +++ b/src/routes/word/word.repository.js @@ -140,3 +140,15 @@ exports.updateWord = async (requestId, formData) => { return null; } }; + +exports.checkDuplicateWord = async (word) => { + try { + const wordExists = await Word.findOne({ word: { $regex: new RegExp(`^${word}$`, 'i') } }); + console.log('wordExists:', wordExists); + return wordExists; + } + catch (error) { + console.log('Error while checking duplicate word:', error); + return null; + } +} \ No newline at end of file diff --git a/src/routes/word/word.route.js b/src/routes/word/word.route.js index 979c4f7..0bbf5f1 100644 --- a/src/routes/word/word.route.js +++ b/src/routes/word/word.route.js @@ -1,7 +1,7 @@ const express = require('express'); const wordRouter = express.Router(); const { isUser } = require('../../common/utils/auth'); -const { getRankWords, getSearchWords, getRelatedWords, getAllWords } = require('./word.controller'); +const { getRankWords, getSearchWords, getRelatedWords, getAllWords, checkDuplicateWord } = require('./word.controller'); // 전체 단어 목록 (쿼리 스트링에 정렬 & 페이지 정보) wordRouter.get('/', getAllWords); @@ -13,5 +13,8 @@ wordRouter.get('/search/related', getRelatedWords); // 검색어 조회 wordRouter.post('/search/:searchTerm', isUser, getSearchWords); +//등록요청 단어 중복검사 +wordRouter.post('/checkDuplicateWord', checkDuplicateWord); + module.exports = wordRouter; diff --git a/src/routes/word/word.service.js b/src/routes/word/word.service.js index 362097a..c4d10e3 100644 --- a/src/routes/word/word.service.js +++ b/src/routes/word/word.service.js @@ -23,3 +23,10 @@ exports.getAllWords = async (sort, page, limit) => { const words = await wordRepository.getAllWords(sort, page, limit); return words; }; + +// 등록 단어 중복 검사 +exports.checkDuplicateWord = async (word) => { + const isDuplicate = await wordRepository.checkDuplicateWord(word); + console.log("isDuplicate: ", isDuplicate) + return isDuplicate; +}; From 8d30f049fe9a8c808469b430742187375c479d4d Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 13 Jul 2024 01:45:23 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20user=ED=83=88=ED=87=B4?= =?UTF-8?q?=EC=8B=9C=20=EA=B8=B0=EC=97=AC=EC=9E=90=20null=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/user/user.controller.js | 2 ++ src/routes/word/word.repository.js | 21 ++++++++++++++++++++- src/routes/word/word.service.js | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index f86eaa4..4e22ff7 100755 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -3,6 +3,7 @@ const jwt = require('jsonwebtoken'); const config = require('../../common/config'); const userService = require('./user.service'); +const wordService = require('../word/word.service'); const sendResponse = require('../../common/utils/response-handler'); const ErrorMessage = require('../../common/constants/error-message'); const SuccessMessage = require('../../common/constants/success-message'); @@ -319,6 +320,7 @@ exports.updateRequestState = async (req, res) => { exports.deleteUser = async (req, res) => { try { const { _id } = req.user; + await wordService.deleteWordContributor(_id); await userService.deleteUser(_id); sendResponse.ok(res, { message: SuccessMessage.DELETE_USER_SUCCESS, diff --git a/src/routes/word/word.repository.js b/src/routes/word/word.repository.js index ec7ad47..1329f50 100644 --- a/src/routes/word/word.repository.js +++ b/src/routes/word/word.repository.js @@ -73,7 +73,7 @@ exports.getAllWords = async (isSorted, page, limit) => { exports.addWord = async (requestId, formData) => { try { - // requestId에 해당하는 request를 찾습니다. + // requestId에 해당하는 request를 찾습니다. const user = await User.findOne({ 'requests._id': requestId }); if (!user) { console.log('User with the given request not found'); @@ -140,3 +140,22 @@ exports.updateWord = async (requestId, formData) => { return null; } }; + +exports.deleteWordContributor = async (_id) => { + // _id가 일치하는 유저의 닉네임 값을 가져오고, 해당 유저가 suggestedBy로 있는 모든 단어의 suggestedBy를 null로 변경 + try { + const user = await User.findById(_id); + + if (!user) { + console.log('User not found'); + return null; + } + const nickname = user.nickname; + + const words = await Word.updateMany({ suggestedBy: nickname }, { suggestedBy: null }); + return words; + } catch (error) { + console.log('Error while deleting word contributor:', error); + return null; + } +}; diff --git a/src/routes/word/word.service.js b/src/routes/word/word.service.js index 362097a..68a3046 100644 --- a/src/routes/word/word.service.js +++ b/src/routes/word/word.service.js @@ -23,3 +23,7 @@ exports.getAllWords = async (sort, page, limit) => { const words = await wordRepository.getAllWords(sort, page, limit); return words; }; + +exports.deleteWordContributor = async (_id) => { + return await wordRepository.deleteWordContributor(_id); +}; From 8a8e26ed724259acea7e0c6453547c8d0278a737 Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 13 Jul 2024 20:53:20 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9E=95=20redis=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 151 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0fca27..778e7e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,8 @@ "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", - "pm2": "^5.3.1" + "pm2": "^5.3.1", + "redis": "^4.6.15" }, "devDependencies": { "eslint": "^8.57.0", @@ -380,6 +381,59 @@ "debug": "^4.3.1" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.17", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.17.tgz", + "integrity": "sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -1010,6 +1064,14 @@ "node": ">=8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2413,6 +2475,14 @@ "node": ">=10" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4548,6 +4618,22 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.6.15", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.15.tgz", + "integrity": "sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.17", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -5888,6 +5974,46 @@ "debug": "^4.3.1" } }, + "@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "requires": {} + }, + "@redis/client": { + "version": "1.5.17", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.17.tgz", + "integrity": "sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==", + "requires": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + } + }, + "@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "requires": {} + }, + "@redis/json": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", + "requires": {} + }, + "@redis/search": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", + "requires": {} + }, + "@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "requires": {} + }, "@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -6368,6 +6494,11 @@ } } }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7434,6 +7565,11 @@ "wide-align": "^1.1.2" } }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" + }, "get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -8992,6 +9128,19 @@ "picomatch": "^2.2.1" } }, + "redis": { + "version": "4.6.15", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.15.tgz", + "integrity": "sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==", + "requires": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.17", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" + } + }, "regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", diff --git a/package.json b/package.json index 5fd3dc5..b7e72f3 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", - "pm2": "^5.3.1" + "pm2": "^5.3.1", + "redis": "^4.6.15" }, "devDependencies": { "eslint": "^8.57.0", From c16391e9f215fa43ce286729f900ffb531e54e55 Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 13 Jul 2024 20:54:56 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E2=9C=A8=20redis=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/config/index.js | 5 +++++ src/common/modules/redis/index.js | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/common/modules/redis/index.js diff --git a/src/common/config/index.js b/src/common/config/index.js index 42eb807..a3c7d80 100644 --- a/src/common/config/index.js +++ b/src/common/config/index.js @@ -23,6 +23,11 @@ const conf = { // social login kakaoRestApiKey: process.env.KAKAO_REST_API_KEY, + + redisHost: process.env.REDIS_HOST, + redisPort: process.env.REDIS_PORT, + redisUsername: process.env.REDIS_USERNAME, + redisPassword: process.env.REDIS_PASSWORD, }; switch (process.env.NODE_ENV) { diff --git a/src/common/modules/redis/index.js b/src/common/modules/redis/index.js new file mode 100644 index 0000000..41f649a --- /dev/null +++ b/src/common/modules/redis/index.js @@ -0,0 +1,20 @@ +const conf = require('../../config'); +const { createClient } = require('redis'); + +const redisClient = createClient({ + url: `redis://${conf.redisUsername}:${conf.redisPassword}@${conf.redisHost}:${conf.redisPort}/0`, + legacyMode: true, // 레거시 모드를 사용하는 경우에만 설정합니다. +}); + +redisClient.on('connect', () => { + console.info('✅ Connected to Redis'); +}); + +redisClient.on('error', (err) => { + console.error('Redis Client Error', err); +}); + +redisClient.connect().then(); +const redisCli = redisClient.v4; + +module.exports = redisCli; From 289b008783ca4b2e8143db668fa6e2a699f5f4a6 Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 13 Jul 2024 20:56:37 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refresh=20rotation?= =?UTF-8?q?=EC=8B=9C=20redis=EC=97=90=EC=84=9C=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/constants/error-message.js | 5 +- src/routes/user/user.controller.js | 89 +++++++++++++++++++-------- src/routes/user/user.route.js | 5 +- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/common/constants/error-message.js b/src/common/constants/error-message.js index e825ffe..52e3126 100644 --- a/src/common/constants/error-message.js +++ b/src/common/constants/error-message.js @@ -23,8 +23,9 @@ const ErrorMessage = Object.freeze({ EXIST_EMAIL: '이미 존재하는 이메일 입니다.', LOGIN_ERROR: '로그인중 오류가 발생하였습니다.', KAKAO_LOGIN_ERROR: '카카오 로그인중 오류가 발생하였습니다.', - NO_REFRESH_TOKEN: 'refresh token이 존재하지 않습니다.', - REFRESH_TOKEN_ERROR: 'refresh token 검증중 오류가 발생하였습니다.', + NO_REFRESH_TOKEN: '비로그인 상태입니다.', + REFRESH_TOKEN_ERROR: 'Refresh Token 검증중 오류가 발생하였습니다.', + REFRESH_TOKEN_MISMATCH: '유효하지 않은 Refresh Token입니다.', // WORD RECENT_WORDS_ERROR: '최근 검색어 조회중 오류가 발생하였습니다.', diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index 29d1410..b95fd19 100755 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -1,7 +1,7 @@ const passport = require('passport'); const jwt = require('jsonwebtoken'); const config = require('../../common/config'); - +const redisClient = require('../../common/modules/redis'); const userService = require('./user.service'); const sendResponse = require('../../common/utils/response-handler'); @@ -89,7 +89,7 @@ exports.isEmailExist = async (req, res) => { exports.localLogin = async (req, res, next) => { try { req.body = validateRequest(loginBodySchema, req.body); - passport.authenticate('local', (authError, user, info) => { + passport.authenticate('local', async (authError, user, info) => { if (authError) { console.error(authError); return next(authError); @@ -101,6 +101,8 @@ exports.localLogin = async (req, res, next) => { const accessToken = generateAccessToken(user); const refreshToken = generateRefreshToken(user); + await redisClient.set(user.email, refreshToken); + res.cookie('refreshToken', refreshToken, config.cookieInRefreshTokenOptions); return sendResponse.ok(res, { @@ -133,6 +135,9 @@ exports.kakaoLogin = async (req, res) => { const accessToken = generateAccessToken(user); const refreshToken = generateRefreshToken(user); + console.log(user.email); + const re = await redisClient.set(user.email, refreshToken); + console.log('hh', re); res.cookie('refreshToken', refreshToken, config.cookieInRefreshTokenOptions); sendResponse.ok(res, { @@ -150,29 +155,52 @@ exports.refreshToken = async (req, res) => { const refreshToken = req.cookies.refreshToken; if (!refreshToken) { // NOTE : 로그인 하지 않은 유저도 refresh token이 없는 경우에 해당하기 때문에, ok로 응답 - console.log(ErrorMessage.NO_REFRESH_TOKEN); return sendResponse.ok(res, { message: ErrorMessage.NO_REFRESH_TOKEN, }); } - jwt.verify(refreshToken, config.jwtRefreshSecret, (err, user) => { + jwt.verify(refreshToken, config.jwtRefreshSecret, async (err, user) => { if (err) return sendResponse.forbidden(res, { message: ErrorMessage.REFRESH_TOKEN_ERROR, }); - const newAccessToken = generateAccessToken({ _id: user.userId, nickname: user.nickname, email: user.email }); - const newRefreshToken = generateRefreshToken({ _id: user.userId, nickname: user.nickname, email: user.email }); + try { + const storedRefreshToken = await redisClient.get(user.email); - res.cookie('refreshToken', newRefreshToken, config.cookieInRefreshTokenOptions); + if (storedRefreshToken !== refreshToken) { + return sendResponse.unAuthorized(res, { + message: ErrorMessage.REFRESH_TOKEN_MISMATCH, + }); + } - sendResponse.ok(res, { - message: SuccessMessage.REFRESH_TOKEN, - data: { - accessToken: newAccessToken, - }, - }); + const newAccessToken = generateAccessToken({ + _id: user.userId, + nickname: user.nickname, + email: user.email, + }); + const newRefreshToken = generateRefreshToken({ + _id: user.userId, + nickname: user.nickname, + email: user.email, + }); + + await redisClient.set(user.email, newRefreshToken); + res.cookie('refreshToken', newRefreshToken, config.cookieInRefreshTokenOptions); + + sendResponse.ok(res, { + message: SuccessMessage.REFRESH_TOKEN, + data: { + accessToken: newAccessToken, + }, + }); + } catch (error) { + console.error('Redis error:', error); + sendResponse.fail(res, { + message: ErrorMessage.REFRESH_TOKEN_ERROR, + }); + } }); }; @@ -185,7 +213,17 @@ exports.getProfile = (req, res) => { }); }; -exports.logout = (_, res) => { +exports.logout = async (req, res) => { + const refreshToken = req.cookies.refreshToken; + if (refreshToken) { + try { + const email = req.user.email; + await redisClient.del(email); + } catch (err) { + console.error('Redis error:', err); + } + } + res.clearCookie('refreshToken'); return sendResponse.ok(res, { message: SuccessMessage.LOGOUT_SUCCESS, @@ -221,7 +259,7 @@ exports.delRecentSearch = async (req, res) => { }; exports.UserRequests = async (req, res) => { - try{ + try { const { _id } = req.user; const requests = await userService.getUserRequests(_id); sendResponse.ok(res, { @@ -235,7 +273,7 @@ exports.UserRequests = async (req, res) => { }; exports.UserRequestsAll = async (req, res) => { - try{ + try { const requests = await userService.getUserRequestsAll(); sendResponse.ok(res, { message: SuccessMessage.GET_REQUESTS_SUCCESS, @@ -245,10 +283,10 @@ exports.UserRequestsAll = async (req, res) => { console.log(err); sendResponse.fail(req, res, ErrorMessage.GET_REQUESTS_ERROR); } -} +}; exports.deleteRequest = async (req, res) => { - try{ + try { const { _id } = req.user; // 현재 로그인한 사용자의 고유 식별자 const { word } = req.params; await userService.deleteRequest(_id, word); @@ -259,7 +297,7 @@ exports.deleteRequest = async (req, res) => { console.log(err); sendResponse.fail(req, res, ErrorMessage.DELETE_REQUEST_ERROR); } -} +}; exports.getRole = async (req, res) => { const { _id } = req.user; @@ -268,7 +306,7 @@ exports.getRole = async (req, res) => { message: SuccessMessage.GET_ROLE_SUCCESS, data: { role }, }); -} +}; exports.updateRequest = async (req, res) => { const { _id } = req.user; @@ -276,17 +314,17 @@ exports.updateRequest = async (req, res) => { const { formData } = req.body; await userService.updateRequest(_id, word, formData); sendResponse.ok(res, { - message: SuccessMessage.UPDATE_REQUEST_SUCCESS + message: SuccessMessage.UPDATE_REQUEST_SUCCESS, }); -} +}; exports.updateRequestState = async (req, res) => { try { - const { _id} = req.user; + const { _id } = req.user; const { requestId } = req.params; const { status } = req.body; - console.log("요청업데이트컨트롤러 진입!!!!", _id, requestId, status) + console.log('요청업데이트컨트롤러 진입!!!!', _id, requestId, status); await userService.updateRequestState(_id, requestId, status); sendResponse.ok(res, { @@ -296,5 +334,4 @@ exports.updateRequestState = async (req, res) => { console.log(err); sendResponse.fail(req, res, ErrorMessage.UPDATE_REQUEST_STATE_ERROR); } -} - +}; diff --git a/src/routes/user/user.route.js b/src/routes/user/user.route.js index 661e981..09f3306 100644 --- a/src/routes/user/user.route.js +++ b/src/routes/user/user.route.js @@ -29,7 +29,7 @@ userRouter.get('/check/email', isEmailExist); userRouter.post('/local/login', isNotLoggedIn, localLogin); userRouter.post('/kakao/login', isNotLoggedIn, kakaoLogin); userRouter.post('/refresh', refreshToken); -userRouter.post('/logout', logout); +userRouter.post('/logout', isLoggedIn, logout); userRouter.get('/profile', isLoggedIn, getProfile); @@ -44,8 +44,7 @@ userRouter.get('/role', isLoggedIn, getRole); // 사용자 역할 가져오기 userRouter.delete('/requests/:word', isLoggedIn, deleteRequest); // 사용자 요청 삭제 userRouter.post('/requests/:word', isLoggedIn, updateRequest); // 사용자 요청 수정 - -//요청 상태 변경 +//요청 상태 변경 userRouter.post('/requests/:requestId/status', isLoggedIn, updateRequestState); // 사용자 요청 status 변경 module.exports = userRouter; From e9f9733d33b85401643be4df46a1aa3f97ef2aa8 Mon Sep 17 00:00:00 2001 From: jjikky Date: Sat, 13 Jul 2024 21:11:59 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=EB=B3=91=ED=95=A9?= =?UTF-8?q?=EC=8B=A4=EC=88=98=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/user/user.controller.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/routes/user/user.controller.js b/src/routes/user/user.controller.js index e6b1a21..02d3760 100755 --- a/src/routes/user/user.controller.js +++ b/src/routes/user/user.controller.js @@ -226,7 +226,7 @@ exports.logout = async (req, res) => { } } - res.clearCookie('refreshToken',config.cookieInRefreshTokenDeleteOptions); + res.clearCookie('refreshToken', config.cookieInRefreshTokenDeleteOptions); return sendResponse.ok(res, { message: SuccessMessage.LOGOUT_SUCCESS, }); @@ -346,13 +346,7 @@ exports.updateRequestState = async (req, res) => { const { requestId } = req.params; const { status, formData, requestType } = req.body; -<<<<<<< HEAD - console.log('요청업데이트컨트롤러 진입!!!!', _id, requestId, status); - - await userService.updateRequestState(_id, requestId, status); -======= await userService.updateRequestState(_id, requestId, status, formData, requestType); ->>>>>>> dev sendResponse.ok(res, { message: SuccessMessage.UPDATE_REQUEST_STATE_SUCCESS, }); @@ -361,8 +355,6 @@ exports.updateRequestState = async (req, res) => { sendResponse.fail(req, res, ErrorMessage.UPDATE_REQUEST_STATE_ERROR); } }; -<<<<<<< HEAD -======= exports.deleteUser = async (req, res) => { try { @@ -377,4 +369,3 @@ exports.deleteUser = async (req, res) => { sendResponse.fail(req, res, ErrorMessage.DELETE_USER_ERROR); } }; ->>>>>>> dev