From ddee72a0b5b646938e60785c91924c8542bd0899 Mon Sep 17 00:00:00 2001 From: Ji Seungmin Date: Fri, 29 Sep 2023 18:46:02 +0900 Subject: [PATCH] =?UTF-8?q?[refactor]:=20ES6=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20ESLint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/node.js.yml | 89 ++++++----- db.js | 35 +++-- firebaseConfig.js | 32 ++-- index.js | 48 +++--- package.json | 1 + routes/checkRoom.js | 80 +++++----- routes/createRoom.js | 57 +++---- routes/finAllRoom.js | 157 +++++++++++--------- routes/findRoom.js | 112 +++++++------- routes/leaveRoom.js | 128 +++++++++------- schemas.js | 26 ++-- socket.js | 272 ++++++++++++++++++---------------- 12 files changed, 552 insertions(+), 485 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 2657e8a..ced29a9 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -2,7 +2,7 @@ name: CD on: push: - branches: [ "master" ] + branches: ["master"] permissions: contents: read @@ -12,54 +12,53 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Clear .env file - run: echo "" > .env + - name: Clear .env file + run: echo "" > .env - - name: Set .env file - run: | - echo "PORT=${{ secrets.PORT }}" >> .env - echo "OAUTH_URL=${{ secrets.OAUTH_URL }}" >> .env - echo "MONGO_DB=${{ secrets.MONGO_DB }}" >> .env - echo "FIREBASE_TYPE=${{ secrets.FIREBASE_TYPE }}" >> .env - echo "FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }}" >> .env - echo "FIREBASE_PRIVATE_KEY_ID=${{ secrets.FIREBASE_PRIVATE_KEY_ID }}" >> .env - echo "FIREBASE_PRIVATE_KEY=${{ secrets.FIREBASE_PRIVATE_KEY }}" >> .env - echo "FIREBASE_CLIENT_EMAIL=${{ secrets.FIREBASE_CLIENT_EMAIL }}" >> .env - echo "FIREBASE_CLIENT_ID=${{ secrets.FIREBASE_CLIENT_ID }}" >> .env - echo "FIREBASE_AUTH_URI=${{ secrets.FIREBASE_AUTH_URI }}" >> .env - echo "FIREBASE_TOKEN_URI=${{ secrets.FIREBASE_TOKEN_URI }}" >> .env - echo "FIREBASE_AUTH_PROVIDER_X509_CERT_URL=${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}" >> .env - echo "FIREBASE_CLIENT_X509_CERT_URL=${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}" >> .env - echo "FIREBASE_UNIVERSE_DOMAIN=${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}" >> .env + - name: Set .env file + run: | + echo "PORT=${{ secrets.PORT }}" >> .env + echo "OAUTH_URL=${{ secrets.OAUTH_URL }}" >> .env + echo "MONGO_DB=${{ secrets.MONGO_DB }}" >> .env + echo "FIREBASE_TYPE=${{ secrets.FIREBASE_TYPE }}" >> .env + echo "FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }}" >> .env + echo "FIREBASE_PRIVATE_KEY_ID=${{ secrets.FIREBASE_PRIVATE_KEY_ID }}" >> .env + echo "FIREBASE_PRIVATE_KEY=${{ secrets.FIREBASE_PRIVATE_KEY }}" >> .env + echo "FIREBASE_CLIENT_EMAIL=${{ secrets.FIREBASE_CLIENT_EMAIL }}" >> .env + echo "FIREBASE_CLIENT_ID=${{ secrets.FIREBASE_CLIENT_ID }}" >> .env + echo "FIREBASE_AUTH_URI=${{ secrets.FIREBASE_AUTH_URI }}" >> .env + echo "FIREBASE_TOKEN_URI=${{ secrets.FIREBASE_TOKEN_URI }}" >> .env + echo "FIREBASE_AUTH_PROVIDER_X509_CERT_URL=${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}" >> .env + echo "FIREBASE_CLIENT_X509_CERT_URL=${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}" >> .env + echo "FIREBASE_UNIVERSE_DOMAIN=${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}" >> .env - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: 18.16.0 + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: 18.16.0 - - name: Install Dependencies - run: npm ci + - name: Install Dependencies + run: npm ci - - name: Build App - run: npm run build + - name: Build App + run: npm run build - - name: Docker Build and Push - run: | - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -t ${{ secrets.DOCKER_REPO }}/refit-node . - docker push ${{ secrets.DOCKER_REPO }}/refit-node - - - name: Deploy - uses: appleboy/ssh-action@v0.1.10 - with: - host: ${{ secrets.HOST }} - username: ubuntu - key: ${{ secrets.KEY }} - script: | - sudo docker rm -f $(docker ps -qa) - sudo docker pull ${{ secrets.DOCKER_REPO }}/refit-node - docker-compose up -d - docker image prune -f + - name: Docker Build and Push + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker build -t ${{ secrets.DOCKER_REPO }}/refit-node . + docker push ${{ secrets.DOCKER_REPO }}/refit-node + - name: Deploy + uses: appleboy/ssh-action@v0.1.10 + with: + host: ${{ secrets.HOST }} + username: ubuntu + key: ${{ secrets.KEY }} + script: | + sudo docker rm -f $(docker ps -qa) + sudo docker pull ${{ secrets.DOCKER_REPO }}/refit-node + docker-compose up -d + docker image prune -f diff --git a/db.js b/db.js index ebee01c..d97eda6 100644 --- a/db.js +++ b/db.js @@ -1,21 +1,24 @@ -const mongoose = require('mongoose'); -require('dotenv').config() +import mongoose from "mongoose"; +import dotenv from "dotenv"; -const connectDB = async () => { - try { - - await mongoose.connect(process.env.MONGO_DB, {useNewUrlParser: true, useUnifiedTopology: true}); +dotenv.config(); - const db = mongoose.connection; - db.on('error', console.error.bind(console, 'connection error:')); - db.once('open', function() { - console.log("Connected to MongoDB!"); - }); +const connectDB = async () => { + try { + await mongoose.connect(process.env.MONGO_DB, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); - } catch (error) { - console.error('Could not connect to MongoDB!', error); - process.exit(1); - } + const db = mongoose.connection; + db.on("error", console.error.bind(console, "connection error:")); + db.once("open", function () { + console.log("Connected to MongoDB!"); + }); + } catch (error) { + console.error("Could not connect to MongoDB!", error); + process.exit(1); + } }; -module.exports = connectDB; \ No newline at end of file +export default connectDB; diff --git a/firebaseConfig.js b/firebaseConfig.js index 79ba544..33358ed 100644 --- a/firebaseConfig.js +++ b/firebaseConfig.js @@ -1,17 +1,19 @@ -require('dotenv').config(); +import dotenv from "dotenv"; + +dotenv.config(); const firebaseConfig = { - type: process.env.FIREBASE_TYPE, - project_id: process.env.FIREBASE_PROJECT_ID, - private_key_id: process.env.FIREBASE_PRIVATE_KEY_ID, - private_key: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'), - client_email: process.env.FIREBASE_CLIENT_EMAIL, - client_id: process.env.FIREBASE_CLIENT_ID, - auth_uri: process.env.FIREBASE_AUTH_URI, - token_uri: process.env.FIREBASE_TOKEN_URI, - auth_provider_x509_cert_url: process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL, - client_x509_cert_url: process.env.FIREBASE_CLIENT_X509_CERT_URL, - universe_domain: process.env.FIREBASE_UNIVERSE_DOMAIN - }; - - module.exports = firebaseConfig; \ No newline at end of file + type: process.env.FIREBASE_TYPE, + project_id: process.env.FIREBASE_PROJECT_ID, + private_key_id: process.env.FIREBASE_PRIVATE_KEY_ID, + private_key: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"), + client_email: process.env.FIREBASE_CLIENT_EMAIL, + client_id: process.env.FIREBASE_CLIENT_ID, + auth_uri: process.env.FIREBASE_AUTH_URI, + token_uri: process.env.FIREBASE_TOKEN_URI, + auth_provider_x509_cert_url: process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL, + client_x509_cert_url: process.env.FIREBASE_CLIENT_X509_CERT_URL, + universe_domain: process.env.FIREBASE_UNIVERSE_DOMAIN, +}; + +export default firebaseConfig; diff --git a/index.js b/index.js index cbea3c6..62a42f5 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,17 @@ -const express = require('express'); //RESTful API 기능을 제공 -const axios = require('axios'); //Promise를 사용하여 비동기적으로 데이터를 처리 -const sockets = require('./socket'); -const connectDB = require('./db'); +import express from "express"; +import sockets from "./socket.js"; +import connectDB from "./db.js"; +import dotenv from "dotenv"; +import http from "http"; +import * as socketIo from "socket.io"; -//환경 변수를 .env 파일에서 로드하여 Node.js 애플리케이션에서 사용할 수 있게 해주는 라이브러리 -require('dotenv').config() +import finAllRoom from "./routes/finAllRoom.js"; +import createRoom from "./routes/createRoom.js"; +import findRoom from "./routes/findRoom.js"; +import leaveRoom from "./routes/leaveRoom.js"; +import checkRoom from "./routes/checkRoom.js"; + +dotenv.config(); const app = express(); const port = process.env.PORT; @@ -14,27 +21,16 @@ app.use(express.urlencoded({ extended: true })); connectDB(); -// Socket IO 패키지 추가 -const http = require('http'); -const socketIo = require('socket.io'); - -// 기존의 'app.listen' 대신에 다음 코드를 사용합니다. const server = http.createServer(app); -const io = socketIo(server); -sockets(io) - -const finAllRoom = require('./routes/finAllRoom'); -const createRoom = require('./routes/createRoom'); -const findRoom = require('./routes/findRoom'); -const leaveRoom = require('./routes/leaveRoom'); -const checkRoom = require('./routes/checkRoom'); +const io = new socketIo.Server(server); +sockets(io); -app.use('/chat/room/all', finAllRoom); -app.use('/chat/room/create', createRoom); -app.use('/chat/room', findRoom); -app.use('/chat/room', leaveRoom); -app.use('/chat/room', checkRoom); +app.use("/chat/room/all", finAllRoom); +app.use("/chat/room/create", createRoom); +app.use("/chat/room", findRoom); +app.use("/chat/room", leaveRoom); +app.use("/chat/room", checkRoom); server.listen(port, () => { - console.log(`App listening at http://localhost:${port}`) -}); \ No newline at end of file + console.log(`App listening at http://localhost:${port}`); +}); diff --git a/package.json b/package.json index 51c0c52..2986ff5 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "", "main": "index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node index.js", diff --git a/routes/checkRoom.js b/routes/checkRoom.js index 37c36f5..c8e359e 100644 --- a/routes/checkRoom.js +++ b/routes/checkRoom.js @@ -1,46 +1,50 @@ -const express = require('express'); -const axios = require('axios'); -const { chat, chatroom } = require('../schemas'); +import express from "express"; +import axios from "axios"; +import { chat, chatroom } from "../schemas.js"; const router = express.Router(); - -const resource_url = process.env.OAUTH_URL; +const resource_url = process.env.OAUTH_URL; //메시지가 존재하는지 체크하는 메서드 -router.get('/:roomId/check/message', async (req, res) => { +router.get("/:roomId/check/message", async (req, res) => { + const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 + const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 - const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 - const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 - - //엑세스 토큰이 존재하지 않을 경우 - if (!token) { - res.status(400).json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); - return; - } - - //리소스에 접급 - try { - const response = await axios.get(resource_url, { - headers: { - 'Authorization': `${token}` - } - }); - } catch (error) { - res.status(400).json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); - } + //엑세스 토큰이 존재하지 않을 경우 + if (!token) { + res + .status(400) + .json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); + return; + } + + //리소스에 접급 + try { + const response = await axios.get(resource_url, { + headers: { + Authorization: `${token}`, + }, + }); + } catch (error) { + res + .status(400) + .json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + } + + try { + const chatCount = await chat.exists({ roomId: roomId }); - try { - const chatCount = await chat.exists({ roomId: roomId }); - - if(chatCount === 0) { - await chatroom.deleteOne({ roomId: roomId }); - res.json({ message: "채팅방이 삭제되었습니다.", code: 200 }); - } else { - res.json({ message: "채팅방에 메시지가 존재합니다.", code: 70002 }); - } - } catch (error) { - res.status(500).json({ message: "서버 내부 오류가 발생하였습니다.", code: 50002 }); + if (chatCount === 0) { + await chatroom.deleteOne({ roomId: roomId }); + res.json({ message: "채팅방이 삭제되었습니다.", code: 200 }); + } else { + res.json({ message: "채팅방에 메시지가 존재합니다.", code: 70002 }); } - }); + } catch (error) { + res + .status(500) + .json({ message: "서버 내부 오류가 발생하였습니다.", code: 50002 }); + } +}); -module.exports = router; \ No newline at end of file +export default router; diff --git a/routes/createRoom.js b/routes/createRoom.js index 826669f..1ab1a20 100644 --- a/routes/createRoom.js +++ b/routes/createRoom.js @@ -1,18 +1,19 @@ -const express = require('express'); -const axios = require('axios'); -const { chat, chatroom } = require('../schemas'); +import express from "express"; +import axios from "axios"; +import { chatroom } from "../schemas.js"; const router = express.Router(); - -const resource_url = process.env.OAUTH_URL; +const resource_url = process.env.OAUTH_URL; //채팅방 생성 API -router.post('/', async (req, res) => { - const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 +router.post("/", async (req, res) => { + const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 //엑세스 토큰이 존재하지 않을 경우 if (!token) { - res.status(400).json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); + res + .status(400) + .json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); return; } @@ -20,8 +21,8 @@ router.post('/', async (req, res) => { try { const response = await axios.get(resource_url, { headers: { - 'Authorization': `${token}` - } + Authorization: `${token}`, + }, }); // 유저 정보 수집 @@ -34,15 +35,13 @@ router.post('/', async (req, res) => { const chatrooms = await chatroom.findOne({ buyer: me, seller: you, - postId: postId + postId: postId, }); - if(chatrooms){ - if(chatrooms.participants.includes(me)){ - + if (chatrooms) { + if (chatrooms.participants.includes(me)) { res.json({ roomId: chatrooms.roomId }); - return - + return; } else { // 채팅방에 참여자(me) 추가 chatrooms.participants.push(me); @@ -51,35 +50,37 @@ router.post('/', async (req, res) => { await chatrooms.save(); res.json({ roomId: chatrooms.roomId }); - return + return; } } else { // 채팅방이 존재하지 않으므로 생성 const newChatroom = new chatroom({ - participants: [me, you], - postId: postId, + participants: [me, you], + postId: postId, postType: postType, buyer: me, - seller: you, - buyer_enter: new Date(), + seller: you, + buyer_enter: new Date(), seller_enter: new Date(), buyer_out: new Date(), - seller_out: new Date() + seller_out: new Date(), }); await newChatroom.save(); res.json({ roomId: newChatroom.roomId }); - return + return; } - } catch (error) { if (error.message === "Chatroom already exists!") { - res.status(400).json({ message: "채팅방이 이미 존재합니다", code: 60000 }); + res + .status(400) + .json({ message: "채팅방이 이미 존재합니다", code: 60000 }); } else { - res.status(400).json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + res + .status(400) + .json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); } } }); - -module.exports = router; \ No newline at end of file +export default router; diff --git a/routes/finAllRoom.js b/routes/finAllRoom.js index 9439eb8..f138edf 100644 --- a/routes/finAllRoom.js +++ b/routes/finAllRoom.js @@ -1,87 +1,102 @@ -const express = require('express'); -const axios = require('axios'); -const { chat, chatroom } = require('../schemas'); +import express from "express"; +import axios from "axios"; +import { chat, chatroom } from "../schemas.js"; const router = express.Router(); - -const resource_url = process.env.OAUTH_URL; +const resource_url = process.env.OAUTH_URL; /*chat room api*/ //모든 채팅방 조회 API -router.get('/', async (req, res) => { - const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 - - //엑세스 토큰이 존재하지 않을 경우 - if (!token) { - res.status(400).json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); - return; - } - - //리소스에 접급 - //await을 통해 해당 요청이 마무리될 때까지 함수 실행을 멈춤 - try { - const response = await axios.get(resource_url, { - headers: { - 'Authorization': `${token}` - } - }); - - // 유저 정보 수집 - const me = response.data.userId - - // Chatroom 조회 (Read) - chatroom.find({ participants: me }) +router.get("/", async (req, res) => { + const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 + + //엑세스 토큰이 존재하지 않을 경우 + if (!token) { + res + .status(400) + .json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); + return; + } + + //리소스에 접급 + //await을 통해 해당 요청이 마무리될 때까지 함수 실행을 멈춤 + try { + const response = await axios.get(resource_url, { + headers: { + Authorization: `${token}`, + }, + }); + + // 유저 정보 수집 + const me = response.data.userId; + + // Chatroom 조회 (Read) + chatroom + .find({ participants: me }) .then(async (chatrooms) => { //Promise.all과 map을 통해 각 chatroom에 대해 반환값을 가져오는 것을 병렬로 실행 - const reducedChatrooms = await Promise.all(chatrooms.map(async (chatroom) => { - - let other = (chatroom.buyer == me) ? chatroom.seller : chatroom.buyer; - let otherEncoded = encodeURIComponent(other); - let otherImageResponse = await axios.get(resource_url + `/image?otherId=${otherEncoded}&postId=${chatroom.postId}`); - let otherImage = otherImageResponse.data.otherImage - let postState = otherImageResponse.data.postState - - let lastChat = await chat.findOne({ roomId: chatroom.roomId }).sort({ time: -1 }); - if (!lastChat) return null; + const reducedChatrooms = await Promise.all( + chatrooms.map(async (chatroom) => { + let other = chatroom.buyer == me ? chatroom.seller : chatroom.buyer; + let otherEncoded = encodeURIComponent(other); + let otherImageResponse = await axios.get( + resource_url + + `/image?otherId=${otherEncoded}&postId=${chatroom.postId}` + ); + let otherImage = otherImageResponse.data.otherImage; + let postState = otherImageResponse.data.postState; - // 안읽은 메시지 수 계산 - let lastExit = (me === chatroom.seller) ? chatroom.seller_out : chatroom.buyer_out; + let lastChat = await chat + .findOne({ roomId: chatroom.roomId }) + .sort({ time: -1 }); + if (!lastChat) return null; - // 안읽은 메시지 수 구하기 - let unreadMessagesCount = 0; - if (lastExit) { + // 안읽은 메시지 수 계산 + let lastExit = + me === chatroom.seller ? chatroom.seller_out : chatroom.buyer_out; + + // 안읽은 메시지 수 구하기 + let unreadMessagesCount = 0; + if (lastExit) { unreadMessagesCount = await chat.countDocuments({ - roomId: chatroom.roomId, - time: { $gt: lastExit } + roomId: chatroom.roomId, + time: { $gt: lastExit }, }); - } - - return { - roomId: chatroom.roomId, - postId: chatroom.postId, - postState: postState, - participants: chatroom.participants, - postType: chatroom.postType, - seller: chatroom.seller, - username: me, - other: other, - otherImage: otherImage, - message: lastChat ? lastChat.content : null, - time: lastChat ? lastChat.time : null, - remain: unreadMessagesCount - } - })); + } + + return { + roomId: chatroom.roomId, + postId: chatroom.postId, + postState: postState, + participants: chatroom.participants, + postType: chatroom.postType, + seller: chatroom.seller, + username: me, + other: other, + otherImage: otherImage, + message: lastChat ? lastChat.content : null, + time: lastChat ? lastChat.time : null, + remain: unreadMessagesCount, + }; + }) + ); - const finalChatrooms = reducedChatrooms.filter(chatroom => chatroom !== null); + const finalChatrooms = reducedChatrooms.filter( + (chatroom) => chatroom !== null + ); finalChatrooms.sort((a, b) => (b.time || 0) - (a.time || 0)); - res.json(finalChatrooms); // 조회된 chatrooms을 응답으로 전송 + res.json(finalChatrooms); // 조회된 chatrooms을 응답으로 전송 }) - .catch((err) => { - res.status(500).json({ message: "채팅방을 불러올 수 없습니다.", code: 50000 }); + .catch((err) => { + res + .status(500) + .json({ message: "채팅방을 불러올 수 없습니다.", code: 50000 }); }); - } catch (error) { - res.status(400).json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); - } - }); + } catch (error) { + res + .status(400) + .json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + } +}); -module.exports = router; \ No newline at end of file +export default router; diff --git a/routes/findRoom.js b/routes/findRoom.js index 3ec835b..7ecae75 100644 --- a/routes/findRoom.js +++ b/routes/findRoom.js @@ -1,68 +1,76 @@ -const express = require('express'); -const axios = require('axios'); -const { chat, chatroom } = require('../schemas'); +import express from "express"; +import axios from "axios"; +import { chat, chatroom } from "../schemas.js"; const router = express.Router(); - -const resource_url = process.env.OAUTH_URL; +const resource_url = process.env.OAUTH_URL; //특정 채팅방의 모든 채팅 내용 조회 API -router.get('/:roomId', async (req, res) => { - const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 - const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 - - //엑세스 토큰이 존재하지 않을 경우 - if (!token) { - res.status(400).json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); - return; - } - - //리소스에 접급 - try { - const response = await axios.get(resource_url, { - headers: { - 'Authorization': `${token}` - } - }); - - // 유저 정보 수집 - const me = response.data.userId - - // 채팅방 참여 여부 확인 - const room = await chatroom.findOne({ roomId: roomId, participants: me }); - - if(room) { - // buyer 또는 seller에 따른 enter time 설정 - let enter_time; - if (me === room.buyer) { - enter_time = room.buyer_enter; - } else if (me === room.seller) { - enter_time = room.seller_enter; - } - - // enter_time 이후의 채팅 내용 조회 - chat.find({ roomId: roomId, time: { $gte: enter_time } }) +router.get("/:roomId", async (req, res) => { + const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 + const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 + + //엑세스 토큰이 존재하지 않을 경우 + if (!token) { + res + .status(400) + .json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); + return; + } + + //리소스에 접급 + try { + const response = await axios.get(resource_url, { + headers: { + Authorization: `${token}`, + }, + }); + + // 유저 정보 수집 + const me = response.data.userId; + + // 채팅방 참여 여부 확인 + const room = await chatroom.findOne({ roomId: roomId, participants: me }); + + if (room) { + // buyer 또는 seller에 따른 enter time 설정 + let enter_time; + if (me === room.buyer) { + enter_time = room.buyer_enter; + } else if (me === room.seller) { + enter_time = room.seller_enter; + } + + // enter_time 이후의 채팅 내용 조회 + chat + .find({ roomId: roomId, time: { $gte: enter_time } }) .then((chats) => { - const reducedChats = chats.map(chat => { + const reducedChats = chats.map((chat) => { return { content: chat.content, username: chat.username, time: chat.time, isMy: chat.username == me, - notificationId: chat.notificationId - } + notificationId: chat.notificationId, + }; }); - res.json(reducedChats); // 조회된 chat들을 응답으로 전송 + res.json(reducedChats); // 조회된 chat들을 응답으로 전송 }) .catch((err) => { - res.status(500).json({ message: "채팅 내용을 불러올 수 없습니다.", code: 50001 }); + res + .status(500) + .json({ message: "채팅 내용을 불러올 수 없습니다.", code: 50001 }); }); - } else { - res.status(400).json({ message: "채팅방이 존재하지 않습니다.", code: 60001 }); // roomId에 해당하는 방이 없거나, 사용자가 참여하지 않은 방인 경우 - } - } catch (error) { - res.status(400).json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + } else { + res + .status(400) + .json({ message: "채팅방이 존재하지 않습니다.", code: 60001 }); // roomId에 해당하는 방이 없거나, 사용자가 참여하지 않은 방인 경우 } - }); + } catch (error) { + res + .status(400) + .json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + } +}); -module.exports = router; \ No newline at end of file +export default router; diff --git a/routes/leaveRoom.js b/routes/leaveRoom.js index 5dce93b..c27ec5d 100644 --- a/routes/leaveRoom.js +++ b/routes/leaveRoom.js @@ -1,74 +1,96 @@ -const express = require('express'); -const axios = require('axios'); -const { chat, chatroom } = require('../schemas'); +import express from "express"; +import axios from "axios"; +import { chat, chatroom } from "../schemas.js"; const router = express.Router(); - -const resource_url = process.env.OAUTH_URL; +const resource_url = process.env.OAUTH_URL; //채팅방 떠나기 API -router.delete('/:roomId/leave', async (req, res) => { - const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 - const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 - - //엑세스 토큰이 존재하지 않을 경우 - if (!token) { - res.status(400).json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); - return; - } - - //리소스에 접급 - try { - const response = await axios.get(resource_url, { - headers: { - 'Authorization': `${token}` - } - }); - - // 유저 정보 수집 - const me = response.data.userId - - // Chatroom 찾기 - chatroom.findOne({ roomId: roomId }) +router.delete("/:roomId/leave", async (req, res) => { + const token = req.headers.authorization; // 헤더에서 액세스 토큰 추출 + const roomId = req.params.roomId; // route parameter로부터 roomId를 추출 + + //엑세스 토큰이 존재하지 않을 경우 + if (!token) { + res + .status(400) + .json({ message: "JWT Token이 존재하지 않습니다.", code: 10100 }); + return; + } + + //리소스에 접급 + try { + const response = await axios.get(resource_url, { + headers: { + Authorization: `${token}`, + }, + }); + + // 유저 정보 수집 + const me = response.data.userId; + + // Chatroom 찾기 + chatroom + .findOne({ roomId: roomId }) .then((chatroomToLeave) => { - if(chatroomToLeave) { + if (chatroomToLeave) { // 해당 채팅방의 participants에 me가 있다면 삭제 - if(chatroomToLeave.participants.includes(me)) { + if (chatroomToLeave.participants.includes(me)) { chatroomToLeave.participants.pull(me); // participants가 비어있다면 채팅방 삭제 - if(chatroomToLeave.participants.length == 0) { + if (chatroomToLeave.participants.length == 0) { Promise.all([ chatroom.deleteOne({ _id: chatroomToLeave._id }), // 채팅방 삭제 - chat.deleteMany({ roomId: roomId }) // 해당 roomId의 모든 채팅 삭제 + chat.deleteMany({ roomId: roomId }), // 해당 roomId의 모든 채팅 삭제 ]) - .then(() => { - res.json({message: `Room with id ${roomId} deleted successfully`}); // 채팅방 삭제 성공 메시지 전송 - }) - .catch((err) => { - res.status(500).json({ message: "채팅방을 삭제할 수 없습니다.", code: 50002 }); - }); + .then(() => { + res.json({ + message: `Room with id ${roomId} deleted successfully`, + }); // 채팅방 삭제 성공 메시지 전송 + }) + .catch((err) => { + res.status(500).json({ + message: "채팅방을 삭제할 수 없습니다.", + code: 50002, + }); + }); } else { - chatroomToLeave.save() - .then(() => { - res.json({message: `User with id ${me} left the room successfully`}); // 참여자 리스트에서 제거 성공 메시지 전송 - }) - .catch((err) => { - res.status(500).json({ message: "채팅방을 나가지 못했습니다.", code: 50003 }); - }); + chatroomToLeave + .save() + .then(() => { + res.json({ + message: `User with id ${me} left the room successfully`, + }); // 참여자 리스트에서 제거 성공 메시지 전송 + }) + .catch((err) => { + res.status(500).json({ + message: "채팅방을 나가지 못했습니다.", + code: 50003, + }); + }); } } else { - res.status(400).json({ message: "채팅방에 참여하고 있지 않습니다.", code: 60002 }); // 참여자 리스트에 없는 경우 + res.status(400).json({ + message: "채팅방에 참여하고 있지 않습니다.", + code: 60002, + }); // 참여자 리스트에 없는 경우 } } else { - res.status(400).json({ message: "채팅방을 찾을 수 없습니다.", code: 60002 }); // roomId에 해당하는 방이 없는 경우 + res + .status(400) + .json({ message: "채팅방을 찾을 수 없습니다.", code: 60002 }); // roomId에 해당하는 방이 없는 경우 } }) .catch((err) => { - res.status(500).json({ message: "채팅방을 찾을 수 없습니다.", code: 50004 }); + res + .status(500) + .json({ message: "채팅방을 찾을 수 없습니다.", code: 50004 }); }); - } catch (error) { - res.status(400).json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); - } - }); + } catch (error) { + res + .status(400) + .json({ message: "유효하지 않은 JWT Token 입니다.", code: 10101 }); + } +}); -module.exports = router; +export default router; diff --git a/schemas.js b/schemas.js index d023ff0..f4529c6 100644 --- a/schemas.js +++ b/schemas.js @@ -1,12 +1,12 @@ -const mongoose = require('mongoose'); +import mongoose from "mongoose"; // 카운터 스키마 설정 const counterSchema = mongoose.Schema({ _id: { type: String, required: true }, - seq: { type: Number, default: 0 } + seq: { type: Number, default: 0 }, }); -const counter = mongoose.model('counter', counterSchema); +const counter = mongoose.model("counter", counterSchema); // 채팅방 스키마 설정 const roomSchema = new mongoose.Schema({ @@ -19,7 +19,7 @@ const roomSchema = new mongoose.Schema({ buyer_enter: Date, seller_enter: Date, buyer_out: Date, - seller_out: Date + seller_out: Date, }); // 채팅 스키마 설정 @@ -27,18 +27,18 @@ const chatSchema = new mongoose.Schema({ content: String, roomId: Number, username: String, - time : Date, - notificationId : String + time: Date, + notificationId: String, }); -roomSchema.pre('save', async function(next) { +roomSchema.pre("save", async function (next) { if (this.isNew) { var doc = this; try { const counterDoc = await counter.findByIdAndUpdate( - {_id: 'roomId'}, - {$inc: {seq: 1}}, - {new: true, upsert: true} + { _id: "roomId" }, + { $inc: { seq: 1 } }, + { new: true, upsert: true } ); doc.roomId = counterDoc.seq; @@ -52,7 +52,7 @@ roomSchema.pre('save', async function(next) { }); // 스키마 생성 -const chat = mongoose.model('chat', chatSchema); -const chatroom = mongoose.model('chatroom', roomSchema); +const chat = mongoose.model("chat", chatSchema); +const chatroom = mongoose.model("chatroom", roomSchema); -module.exports = { chat, chatroom }; \ No newline at end of file +export { chat, chatroom }; diff --git a/socket.js b/socket.js index da46b95..db57f5a 100644 --- a/socket.js +++ b/socket.js @@ -1,148 +1,164 @@ -const axios = require('axios'); -const { chat, chatroom } = require('./schemas'); - -var admin = require('firebase-admin'); - -var serviceAccount = require('./firebaseConfig'); +import axios from "axios"; +import { chat, chatroom } from "./schemas.js"; +import admin from "firebase-admin"; +import serviceAccount from "./firebaseConfig.js"; admin.initializeApp({ credential: admin.credential.cert(serviceAccount), - databaseURL: "https://refit-b8e5f.firebaseio.com" + databaseURL: "https://refit-b8e5f.firebaseio.com", }); const getFcm = async (otherId) => { - try { - const response = await axios.get("http://www.umc-refit.com/oauth2/fcm", { - headers: { - 'otherId': encodeURIComponent(otherId) - } - }); - if (response.status === 200) { - return { - otherFcm: response.data.otherFcm - }; - } - } catch (error) { - console.error('Failed to fetch FCM tokens:'); - return null; + try { + const response = await axios.get("http://www.umc-refit.com/oauth2/fcm", { + headers: { + otherId: encodeURIComponent(otherId), + }, + }); + if (response.status === 200) { + return { + otherFcm: response.data.otherFcm, + }; } + } catch (error) { + console.error("Failed to fetch FCM tokens:"); + return null; + } }; -const sendNotificationToToken = async (fcmToken, title, body, notificationId) => { - const message = { - data: { - title: title, - body: body, - notificationId: notificationId - }, - token: fcmToken - }; - - try { - const response = await admin.messaging().send(message); - console.log('Successfully sent message:', response); - } catch (error) { - console.error('Error sending message:', error); - } -} - -module.exports = (io) => { - - io.on('connection', (socket) => { - console.log("connect success") - - //클라이언트가 joinRoom 이벤트를 보내면(즉 특정 방에 참여하면) 처리하는 핸들러 - //사용자가 특정 채팅방에 참여하려고할때 클라이언트에서 발생함, 이때 roomId와 userId를 매개변수로 전달 - socket.on('joinRoom', (roomId, userId) => { - socket.join(roomId); //소켓을 roomId에 지정된 방에 조인 - socket.userId = userId; //사용자 ID를 소켓에 연결 - console.log(`User ${userId} joined room ${roomId}`); - }); +const sendNotificationToToken = async ( + fcmToken, + title, + body, + notificationId +) => { + const message = { + data: { + title: title, + body: body, + notificationId: notificationId, + }, + token: fcmToken, + }; + + try { + const response = await admin.messaging().send(message); + console.log("Successfully sent message:", response); + } catch (error) { + console.error("Error sending message:", error); + } +}; - //사용자가 메시지 보내면 일어나는 이벤트 - //사용자가 채팅 메시지를 보낼때 클라이언트에서 발생함, 이때 매개변수로 roomId, userId, message 전달 - socket.on('message', async (roomId, userId, otherId, message, notificationId) => { - console.log(`Message from user ${userId} in room ${roomId}: ${message}`); - - // 채팅방 참여자 확인 및 추가 - const room = await chatroom.findOne({ roomId: roomId }); - - if (room) { - if (!room.participants.includes(otherId)) { - room.participants.push(otherId); - - // otherId가 buyer일 경우 - if (otherId == room.buyer) { - room.buyer_enter = new Date(); - room.buyer_out = new Date(); - } - // otherId가 seller일 경우 - else if (otherId == room.seller) { - room.seller_enter = new Date(); - room.seller_out = new Date(); - } - - await room.save(); - } - } else { - console.log(`Room ${roomId} does not exist`); - return; - } +const socketModule = (io) => { + io.on("connection", (socket) => { + console.log("connect success"); + + //클라이언트가 joinRoom 이벤트를 보내면(즉 특정 방에 참여하면) 처리하는 핸들러 + //사용자가 특정 채팅방에 참여하려고할때 클라이언트에서 발생함, 이때 roomId와 userId를 매개변수로 전달 + socket.on("joinRoom", (roomId, userId) => { + socket.join(roomId); //소켓을 roomId에 지정된 방에 조인 + socket.userId = userId; //사용자 ID를 소켓에 연결 + console.log(`User ${userId} joined room ${roomId}`); + }); - // 새로운 메시지 생성 - const newMessage = new chat({ - content: message, - roomId: roomId, - username: userId, - time: new Date(), - notificationId: notificationId - }); - - // 메시지 저장 - await newMessage.save(); //저장 메시지 생성 - - const socketsInRoom = await io.in(roomId).allSockets(); //모든 소켓의 회원 ID 목록을 가지고 옴 - const isInRoom = Array.from(socketsInRoom).some(socketId => io.sockets.sockets.get(socketId).userId === otherId); // 해당 방에 otherId가 있는지 확인 - - //만약 방에 있다면, 알림 전송 - if (!isInRoom) { - const { otherFcm } = await getFcm(otherId); //fcm 토큰 정보 받아오기 - - if (otherFcm) { - await sendNotificationToToken(otherFcm, userId, newMessage.content, notificationId); - } else { - console.error('No valid FCM token available'); - } + //사용자가 메시지 보내면 일어나는 이벤트 + //사용자가 채팅 메시지를 보낼때 클라이언트에서 발생함, 이때 매개변수로 roomId, userId, message 전달 + socket.on( + "message", + async (roomId, userId, otherId, message, notificationId) => { + console.log( + `Message from user ${userId} in room ${roomId}: ${message}` + ); + + // 채팅방 참여자 확인 및 추가 + const room = await chatroom.findOne({ roomId: roomId }); + + if (room) { + if (!room.participants.includes(otherId)) { + room.participants.push(otherId); + + // otherId가 buyer일 경우 + if (otherId == room.buyer) { + room.buyer_enter = new Date(); + room.buyer_out = new Date(); + } + // otherId가 seller일 경우 + else if (otherId == room.seller) { + room.seller_enter = new Date(); + room.seller_out = new Date(); } - // 같은 채팅방에 있는 모든 클라이언트에게 메시지 전송 - io.to(roomId).emit('message', { - content: newMessage.content, - username: newMessage.username, - time: newMessage.time, - notificationId: notificationId - }); + await room.save(); + } + } else { + console.log(`Room ${roomId} does not exist`); + return; + } + + // 새로운 메시지 생성 + const newMessage = new chat({ + content: message, + roomId: roomId, + username: userId, + time: new Date(), + notificationId: notificationId, }); - // 클라이언트가 방을 나갈 때 실행할 이벤트 핸들러 - socket.on('leaveRoom', async (roomId, userId) => { - socket.leave(roomId); - - const room = await chatroom.findOne({ roomId: roomId }); - if (room) { - if (userId === room.buyer) { - room.buyer_out = new Date(); - } else if (userId === room.seller) { - room.seller_out = new Date(); - } - await room.save(); - } + // 메시지 저장 + await newMessage.save(); //저장 메시지 생성 + + const socketsInRoom = await io.in(roomId).allSockets(); //모든 소켓의 회원 ID 목록을 가지고 옴 + const isInRoom = Array.from(socketsInRoom).some( + (socketId) => io.sockets.sockets.get(socketId).userId === otherId + ); // 해당 방에 otherId가 있는지 확인 + + //만약 방에 있다면, 알림 전송 + if (!isInRoom) { + const { otherFcm } = await getFcm(otherId); //fcm 토큰 정보 받아오기 + + if (otherFcm) { + await sendNotificationToToken( + otherFcm, + userId, + newMessage.content, + notificationId + ); + } else { + console.error("No valid FCM token available"); + } + } - console.log(`User ${userId} left room ${roomId}`); + // 같은 채팅방에 있는 모든 클라이언트에게 메시지 전송 + io.to(roomId).emit("message", { + content: newMessage.content, + username: newMessage.username, + time: newMessage.time, + notificationId: notificationId, }); + } + ); + + // 클라이언트가 방을 나갈 때 실행할 이벤트 핸들러 + socket.on("leaveRoom", async (roomId, userId) => { + socket.leave(roomId); + + const room = await chatroom.findOne({ roomId: roomId }); + if (room) { + if (userId === room.buyer) { + room.buyer_out = new Date(); + } else if (userId === room.seller) { + room.seller_out = new Date(); + } + await room.save(); + } - socket.on('disconnect', () => { - console.log('user disconnected'); - }); + console.log(`User ${userId} left room ${roomId}`); }); + + socket.on("disconnect", () => { + console.log("user disconnected"); + }); + }); }; + +export default socketModule;