diff --git a/app.js b/app.js index f343703673275bd6a1a1e53a27d3055cdd2326e9..dcc56c03adc0476f9a365a184f6c16e66829a4d7 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,4 @@ + // app.js require('dotenv').config(); @@ -6,23 +7,10 @@ const express = require('express'); const session = require('express-session'); const passport = require('./passport'); // 변경된 경로 const flash = require('connect-flash'); -const { initScheduleCleaner } = require('./utils/scheduler'); -const connectMongoDB = require('./config/mongoose'); // MongoDB 연결 -const { sequelize } = require('./config/sequelize'); // Sequelize 연결 -const cors = require('cors'); +const { initScheduleCleaner } = require('./utils/scheduler'); // 유동 스케줄 자동 삭제 유틸 const app = express(); -// CORS 설정 -app.use( - cors({ - origin: 'http://localhost:3000', // 허용할 도메인 설정 (예: 프론트엔드 주소) - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'], - credentials: true, // 쿠키와 인증 정보를 허용하려면 true로 설정 - }) -); - // 미들웨어 설정 app.use(express.json()); @@ -47,43 +35,28 @@ app.use(flash()); /** * 라우터 등록 */ +// 로그인 라우터 const authRoutes = require('./routes/auth'); app.use('/auth', authRoutes); +// Schedule 라우터 const scheduleRoutes = require('./routes/schedule'); app.use('/api/schedule', scheduleRoutes); +// Friend 라우터 const friendRoutes = require('./routes/friend'); app.use('/api/friend', friendRoutes); -const meetingRoutes = require('./routes/meetingRoute'); -app.use('/api/meeting', meetingRoutes); -const chatRoutes = require('./routes/chatRoute'); -app.use('/api/chat', chatRoutes); -// 스케줄 클리너 초기화 initScheduleCleaner(); const PORT = process.env.PORT || 3000; -// MongoDB 및 MySQL 연결 후 서버 시작 -(async () => { - try { - // MongoDB 연결 - await connectMongoDB(); - console.log('✅ MongoDB 연결 성공'); - - // MySQL 연결 확인 - await sequelize.authenticate(); - console.log('✅ MySQL 연결 성공'); - - // 서버 시작 - app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); - }); - } catch (error) { - console.error('❌ 서버 시작 중 오류 발생:', error); - process.exit(1); - } -})(); \ No newline at end of file +app.get('/', (req, res) => { + res.send('Hello, World!'); +}); + +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/config/mongoose.js b/config/mongoose.js index 692a4aa2a9d31e14916b06892469db5e81ad980e..3379779bf664ebd969317df7f7de48e7a74b34be 100644 --- a/config/mongoose.js +++ b/config/mongoose.js @@ -2,17 +2,17 @@ const mongoose = require('mongoose'); -const connectMongoDB = async () => { - try { - await mongoose.connect(process.env.MONGODB_URI, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - console.log('✅ MongoDB 연결 성공'); - } catch (error) { - console.error('❌ MongoDB 연결 실패:', error); - throw error; - } -}; +mongoose.connect(process.env.MONGO_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, +}); -module.exports = connectMongoDB; \ No newline at end of file +mongoose.connection.on('connected', () => { + console.log('Mongoose connected.'); +}); + +mongoose.connection.on('error', (err) => { + console.error('Mongoose connection error:', err); +}); + +module.exports = mongoose; diff --git a/controllers/chatController.js b/controllers/chatController.js index 4c61e515f0748955dc020a4e21f4441ffcf7ffe5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/controllers/chatController.js +++ b/controllers/chatController.js @@ -1,105 +0,0 @@ -const chatService = require('../services/chatService'); - -// 내부용 채팅방 생성 -exports.createChatRoomInternal = async (params) => { - try { - return await chatService.createChatRoom(params); - } catch (err) { - console.error('Error in createChatRoomInternal:', err); - return { success: false, error: err.message }; - } -}; - -// 새 채팅방 생성 -exports.createChatRoom = async (req, res) => { - try { - const chatRoomId = await chatService.createChatRoom(); - res.json({ chatRoomId }); - } catch (err) { - console.error('Error creating room:', err); - res.status(500).json({ error: 'Failed to create room' }); - } -}; - -// 채팅방 목록 조회 -exports.getChatRooms = async (req, res) => { - try { - const roomData = await chatService.getChatRooms(); - res.json(roomData); - } catch (err) { - console.error('Error fetching rooms:', err); - res.status(500).json({ error: 'Failed to fetch rooms' }); - } -}; - -// 사용자 상태 업데이트 -exports.updateStatus = async (req, res) => { - const { chatRoomId, nickname, isOnline } = req.body; - try { - await chatService.updateStatus(chatRoomId, nickname, isOnline); - res.status(200).json({ message: 'User status updated successfully' }); - } catch (err) { - console.error('Error updating user status:', err); - res.status(500).json({ error: 'Failed to update user status' }); - } -}; - -// 읽음 상태 업데이트 -exports.updateReadStatus = async (req, res) => { - const { chatRoomId, nickname } = req.body; - try { - await chatService.updateReadStatus(chatRoomId, nickname); - res.status(200).json({ message: 'Read status updated' }); - } catch (err) { - console.error('Error updating read status:', err); - res.status(500).json({ error: 'Failed to update read status' }); - } -}; - -// 읽지 않은 메시지 조회 -exports.getUnreadMessages = async (req, res) => { - const { nickname } = req.params; - try { - const unreadMessages = await chatService.getUnreadMessages(nickname); - res.status(200).json(unreadMessages); - } catch (err) { - console.error('Error fetching unread messages:', err); - res.status(500).json({ error: 'Failed to fetch unread messages' }); - } -}; - -// 읽지 않은 메시지 수 조회 -exports.getUnreadCount = async (req, res) => { - const { chatRoomId } = req.params; - try { - const unreadCountMap = await chatService.getUnreadCount(chatRoomId); - res.status(200).json(unreadCountMap); - } catch (err) { - console.error('Error fetching unread counts:', err); - res.status(500).json({ error: 'Failed to fetch unread counts' }); - } -}; - -// 읽은 로그 ID 업데이트 -exports.updateReadLogId = async (req, res) => { - const { chatRoomId, nickname, logId } = req.body; - try { - await chatService.updateReadLogId(chatRoomId, nickname, logId); - res.status(200).json({ message: 'Last read logID updated' }); - } catch (err) { - console.error('Error updating last read logID:', err); - res.status(500).json({ error: 'Failed to update last read logID' }); - } -}; - -// 상태와 로그 ID 동시 업데이트 -exports.updateStatusAndLogId = async (req, res) => { - const { chatRoomId, nickname, isOnline, logId } = req.body; - try { - await chatService.updateStatusAndLogId(chatRoomId, nickname, isOnline, logId); - res.status(200).json({ message: 'User status and lastReadLogId updated successfully' }); - } catch (err) { - console.error('Error updating user status and lastReadLogId:', err); - res.status(500).json({ error: 'Failed to update user status and lastReadLogId' }); - } -}; \ No newline at end of file diff --git a/controllers/meetingController.js b/controllers/meetingController.js index 4860322910afe398d397dafe2a38044979d42930..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/controllers/meetingController.js +++ b/controllers/meetingController.js @@ -1,68 +0,0 @@ -const MeetingService = require('../services/meetingService'); - -class MeetingController { - async createMeeting(req, res) { - try { - const result = await MeetingService.createMeeting(req.body); - res.status(201).json(result); - } catch (err) { - console.error('번개 모임 생성 오류:', err); - res.status(500).json({ error: err.message || '번개 모임 생성 실패' }); - } - } - - async getMeetings(req, res) { - const { userId } = req.query; - - if (!userId) { - return res.status(400).json({ error: '사용자 ID가 필요합니다.' }); - } - - try { - const meetings = await MeetingService.getMeetings(userId); - res.status(200).json(meetings); - } catch (err) { - console.error('모임 목록 조회 오류:', err); - res.status(500).json({ error: err.message || '모임 목록 조회 실패' }); - } - } - - async closeMeeting(req, res) { - const { meetingId } = req.params; - - try { - const meeting = await MeetingService.closeMeeting(meetingId); - res.status(200).json({ message: '모임이 마감되었습니다.', meeting }); - } catch (err) { - console.error('모임 마감 오류:', err); - res.status(500).json({ error: err.message || '모임 마감 실패' }); - } - } - - async joinMeeting(req, res) { - const { meetingId } = req.params; - const { user_id } = req.body; - - try { - await MeetingService.joinMeeting(meetingId, user_id); - res.status(200).json({ message: '모임 및 채팅방 참가 완료' }); - } catch (err) { - console.error('모임 참가 오류:', err); - res.status(500).json({ error: err.message || '모임 참가 실패' }); - } - } - - async getMeetingDetail(req, res) { - const { meetingId } = req.params; - - try { - const meetingDetail = await MeetingService.getMeetingDetail(meetingId); - res.status(200).json(meetingDetail); - } catch (err) { - console.error('모임 상세 조회 오류:', err); - res.status(500).json({ error: err.message || '모임 상세 조회 실패' }); - } - } -} - -module.exports = new MeetingController(); \ No newline at end of file diff --git a/dtos/MeetingDetailResponse.js b/dtos/MeetingDetailResponse.js deleted file mode 100644 index 6670fe49a85573d4654b2e13c5b31a28572fbac0..0000000000000000000000000000000000000000 --- a/dtos/MeetingDetailResponse.js +++ /dev/null @@ -1,20 +0,0 @@ -class MeetingDetailResponse { - constructor(meeting) { - this.id = meeting.id; - this.title = meeting.title; - this.description = meeting.description; - this.startTime = meeting.start_time; - this.endTime = meeting.end_time; - this.location = meeting.location; - this.deadline = meeting.deadline; - this.type = meeting.type; - this.creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; - this.participants = meeting.participants.map(participant => ({ - userId: participant.user_id, - name: participant.participantUser ? participant.participantUser.name : 'Unknown', - email: participant.participantUser ? participant.participantUser.email : 'Unknown' - })); - } -} - -module.exports = MeetingDetailResponse; \ No newline at end of file diff --git a/dtos/MeetingResponse.js b/dtos/MeetingResponse.js deleted file mode 100644 index 8ee8b833c23b8ff6fb70d200f4384965230f57b5..0000000000000000000000000000000000000000 --- a/dtos/MeetingResponse.js +++ /dev/null @@ -1,19 +0,0 @@ -// dtos/MeetingResponse.js - -class MeetingResponse { - constructor(meeting, isParticipant, isScheduleConflict, creatorName) { - this.id = meeting.id; - this.title = meeting.title; - this.description = meeting.description; - this.startTime = meeting.start_time; - this.endTime = meeting.end_time; - this.location = meeting.location; - this.deadline = meeting.deadline; - this.type = meeting.type; - this.creatorName = creatorName; - this.isParticipant = isParticipant; - this.isScheduleConflict = isScheduleConflict; - } -} - -module.exports = MeetingResponse; \ No newline at end of file diff --git a/models/chatRooms.js b/models/chatRooms.js deleted file mode 100644 index ca68c3941a49d64f8abd4806c399793c03590301..0000000000000000000000000000000000000000 --- a/models/chatRooms.js +++ /dev/null @@ -1,21 +0,0 @@ -const mongoose = require('mongoose'); - -// MongoDB 채팅방 스키마 수정 (현재 참가 중인 유저 목록 추가) -const chatRoomsSchema = new mongoose.Schema({ - chatRoomId: { type: String, required: true, unique: true }, - messages: [{ - sender: String, - message: String, - timestamp: Date, - type: { type: String, default: 'message' } // 기본값은 'message', 다른 값으로 'join', 'leave' 가능 - }], - participants: [{ type: String }], - lastReadAt: { type: Map, of: Date }, // 각 참가자의 마지막 읽은 메시지 시간 기록 - lastReadLogId: { type: Map, of: String }, // 각 참가자의 마지막으로 읽은 logID 기록 - isOnline: { type: Map, of: Boolean } // 각 참가자의 온라인 상태 -}, { collection: 'chatrooms' }); - -// 모델이 이미 정의되어 있는 경우 재정의하지 않음 -const ChatRooms = mongoose.models.ChatRooms || mongoose.model('ChatRooms', chatRoomsSchema); - -module.exports = ChatRooms; \ No newline at end of file diff --git a/models/meeting.js b/models/meeting.js index 14c50b58aa78c52bd14340c678778a49a8d1c994..7944e355fef21458c8550ce46bbb6eeb7e3cbc46 100644 --- a/models/meeting.js +++ b/models/meeting.js @@ -1,15 +1,10 @@ // models/Meeting.js const { DataTypes } = require('sequelize'); -const { sequelize } = require('../config/sequelize'); +const sequelize = require('../config/sequelize'); const User = require('./User'); const Meeting = sequelize.define('Meeting', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, title: { type: DataTypes.STRING, allowNull: false, @@ -35,34 +30,12 @@ const Meeting = sequelize.define('Meeting', { type: DataTypes.ENUM('OPEN', 'CLOSE'), allowNull: false, }, - created_by: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - }, - chatRoomId: { // 새로운 필드 추가 - type: DataTypes.STRING, - allowNull: false, - }, }, { tableName: 'Meetings', timestamps: false, }); +Meeting.belongsTo(User, { foreignKey: 'created_by', as: 'creator' }); +User.hasMany(Meeting, { foreignKey: 'created_by', as: 'meetings' }); -// 연관 관계 설정 -Meeting.associate = (models) => { - Meeting.belongsTo(models.User, { - foreignKey: 'created_by', // FK 설정 - as: 'creator', // 별칭 - }); - Meeting.hasMany(models.MeetingParticipant, { - foreignKey: 'meeting_id', - as: 'participants', - }); -}; - -module.exports = Meeting; \ No newline at end of file +module.exports = Meeting; diff --git a/models/meetingParticipant.js b/models/meetingParticipant.js index 15c93c13aa7e6721fb250422333bcd772ffc0f1c..716a4420b9c9205398cd8d426b0e7ef4c349fad0 100644 --- a/models/meetingParticipant.js +++ b/models/meetingParticipant.js @@ -1,32 +1,19 @@ // models/MeetingParticipant.js const { DataTypes } = require('sequelize'); -const { sequelize } = require('../config/sequelize'); +const sequelize = require('../config/sequelize'); +const Meeting = require('./Meeting'); +const User = require('./User'); -const MeetingParticipant = sequelize.define('MeetingParticipant', { - meeting_id: { - type: DataTypes.BIGINT, - allowNull: false - }, - user_id: { - type: DataTypes.BIGINT, - allowNull: false - } -}, { +const MeetingParticipant = sequelize.define('MeetingParticipant', {}, { tableName: 'MeetingParticipants', timestamps: false, }); -MeetingParticipant.associate = (models) => { - MeetingParticipant.belongsTo(models.Meeting, { - foreignKey: 'meeting_id', - as: 'meeting' - }); +MeetingParticipant.belongsTo(Meeting, { foreignKey: 'meeting_id', as: 'meeting' }); +Meeting.hasMany(MeetingParticipant, { foreignKey: 'meeting_id', as: 'participants' }); - MeetingParticipant.belongsTo(models.User, { - foreignKey: 'user_id', - as: 'participantUser' - }); -}; +MeetingParticipant.belongsTo(User, { foreignKey: 'user_id', as: 'user' }); +User.hasMany(MeetingParticipant, { foreignKey: 'user_id', as: 'meetingParticipations' }); module.exports = MeetingParticipant; diff --git a/models/user.js b/models/user.js index cb14b0bc547c79ac88535d4d88c4a4331e6d113a..fbf92c144d9a6de814321390a784bc4c66c5d2eb 100644 --- a/models/user.js +++ b/models/user.js @@ -1,14 +1,9 @@ // models/User.js const { DataTypes } = require('sequelize'); -const { sequelize } = require('../config/sequelize'); +const sequelize = require('../config/sequelize'); // sequelize 인스턴스 경로에 맞게 수정하세요. const User = sequelize.define('User', { - id: { - type: DataTypes.BIGINT, // 수정: id 필드를 BIGINT로 설정 - autoIncrement: true, - primaryKey: true, - }, name: { type: DataTypes.STRING, // VARCHAR allowNull: false, @@ -26,17 +21,4 @@ const User = sequelize.define('User', { timestamps: true, // createdAt과 updatedAt 자동 관리 }); -User.associate = (models) => { - User.hasMany(models.Meeting, { - foreignKey: 'created_by', - as: 'createdMeetings', - }); - - User.hasMany(models.MeetingParticipant, { - foreignKey: 'user_id', - as: 'userMeetingParticipations', - }); -}; - - module.exports = User; diff --git a/routes/chat.js b/routes/chat.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/routes/chatRoute.js b/routes/chatRoute.js deleted file mode 100644 index 6ae03f13f5e429e9c245c289d859400f99ac799a..0000000000000000000000000000000000000000 --- a/routes/chatRoute.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const chatController = require('../controllers/chatController'); - -router.post('/create-room', chatController.createChatRoom); -router.get('/rooms', chatController.getChatRooms); -router.post('/update-status', chatController.updateStatus); -router.post('/update-read-status', chatController.updateReadStatus); -router.get('/unread-messages/:nickname', chatController.getUnreadMessages); -router.get('/unread-count/:chatRoomId', chatController.getUnreadCount); -router.post('/update-status-and-logid', chatController.updateStatusAndLogId); -router.post('/update-read-log-id', chatController.updateReadLogId); - -module.exports = router; diff --git a/routes/meeting.js b/routes/meeting.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/routes/meetingRoute.js b/routes/meetingRoute.js deleted file mode 100644 index a2788cad0c127f0b8014ffe20ccca7f86b061b9b..0000000000000000000000000000000000000000 --- a/routes/meetingRoute.js +++ /dev/null @@ -1,25 +0,0 @@ -// routes/meetingRoutes.js - -const express = require('express'); -const router = express.Router(); -const { isLoggedIn } = require('../middlewares/auth'); -const MeetingController = require('../controllers/meetingController'); - -router.use(isLoggedIn); - -// 번개 모임 생성 -router.post('/', MeetingController.createMeeting); - -// 번개 모임 목록 조회 -router.get('/', MeetingController.getMeetings); - -// 번개 모임 마감 -router.put('/:meetingId/close', MeetingController.closeMeeting); - -// 번개 모임 참가 -router.post('/:meetingId/join', MeetingController.joinMeeting); - -// 번개 모임 상세 조회 -router.get('/:meetingId', MeetingController.getMeetingDetail); - -module.exports = router; \ No newline at end of file diff --git a/services/chatService.js b/services/chatService.js deleted file mode 100644 index 4135d1581f9cf64efc6377c6d7cabb9313303a7f..0000000000000000000000000000000000000000 --- a/services/chatService.js +++ /dev/null @@ -1,149 +0,0 @@ -const ChatRoom = require('../models/chatRooms'); -const { v4: uuidv4 } = require('uuid'); - -class ChatService { - - // 채팅방 생성 - async createChatRoom({ meeting_id, participants }) { - try { - const chatRoomId = uuidv4(); - const newRoom = new ChatRoom({ - chatRoomId: chatRoomId, - meeting_id, - participants, - messages: [], - lastReadAt: participants.reduce((acc, user) => { - acc[user] = new Date(); - return acc; - }, {}), - lastReadLogId: participants.reduce((acc, user) => { - acc[user] = null; - return acc; - }, {}), - isOnline: participants.reduce((acc, user) => { - acc[user] = true; - return acc; - }, {}), - }); - - const joinMessage = { - message: `${participants[0]}님이 번개 모임을 생성했습니다.`, - timestamp: new Date(), - type: 'join', - }; - - newRoom.messages.push(joinMessage); - await newRoom.save(); - - return { success: true, chatRoomId }; - } catch (err) { - console.error('Error creating chat room:', err); - throw new Error('Failed to create chat room'); - } - } - - // 채팅방 목록 조회 - async getChatRooms() { - const rooms = await ChatRoom.find({}, { chatRoomId: 1, messages: { $slice: -1 } }); - return rooms.map(room => { - const lastMessage = room.messages[0] || {}; - return { - chatRoomId: room.chatRoomId, - lastMessage: { - sender: lastMessage.sender || '없음', - message: lastMessage.message || '메시지 없음', - timestamp: lastMessage.timestamp || null, - } - }; - }); - } - - // 사용자 상태 업데이트 - async updateStatus(chatRoomId, nickname, isOnline) { - await ChatRoom.updateOne( - { chatRoomId }, - { $set: { [`isOnline.${nickname}`]: isOnline } } - ); - } - - // 읽음 상태 업데이트 - async updateReadStatus(chatRoomId, nickname) { - const now = new Date(); - await ChatRoom.updateOne( - { chatRoomId }, - { $set: { [`lastReadAt.${nickname}`]: now } } - ); - } - - // 읽지 않은 메시지 조회 - async getUnreadMessages(nickname) { - const chatRooms = await ChatRoom.find({ participants: nickname }); - return await Promise.all(chatRooms.map(async (chatRoom) => { - const lastReadAt = chatRoom.lastReadAt.get(nickname) || new Date(0); - const unreadMessagesCount = chatRoom.messages.filter(message => - message.timestamp > lastReadAt - ).length; - return { - chatRoomId: chatRoom.chatRoomId, - unreadCount: unreadMessagesCount, - }; - })); - } - - // 읽지 않은 메시지 수 조회 - async getUnreadCount(chatRoomId) { - const chatRoom = await ChatRoom.findOne({ chatRoomId }); - if (!chatRoom) { - throw new Error('Chat room not found'); - } - - const unreadCounts = chatRoom.participants - .filter(user => chatRoom.lastReadLogId.get(user)) - .map(user => chatRoom.lastReadLogId.get(user)) - .reduce((acc, logId) => { - acc[logId] = (acc[logId] || 0) + 1; - return acc; - }, {}); - - let count = 0; - return Object.entries(unreadCounts) - .sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) - .reduce((acc, [logId, value]) => { - count += value; - acc[count] = logId; - return acc; - }, {}); - } - - // 읽은 메시지 로그 ID 업데이트 - async updateReadLogId(chatRoomId, nickname, logId) { - await ChatRoom.updateOne( - { chatRoomId }, - { $set: { [`lastReadLogId.${nickname}`]: logId } } - ); - } - - // 상태와 로그 ID 동시 업데이트 - async updateStatusAndLogId(chatRoomId, nickname, isOnline, logId) { - let finalLogId = logId; - - if (!isOnline && logId === null) { - const chatRoom = await ChatRoom.findOne({ chatRoomId }); - if (chatRoom && chatRoom.messages.length > 0) { - finalLogId = chatRoom.messages[chatRoom.messages.length - 1]._id; - } - } - - await ChatRoom.updateOne( - { chatRoomId }, - { - $set: { - [`isOnline.${nickname}`]: isOnline, - [`lastReadLogId.${nickname}`]: isOnline ? null : finalLogId, - }, - } - ); - } -} - -module.exports = new ChatService(); diff --git a/services/meetingService.js b/services/meetingService.js deleted file mode 100644 index 912a120a3881a985ac5e215da0a7fed6ba729bc5..0000000000000000000000000000000000000000 --- a/services/meetingService.js +++ /dev/null @@ -1,159 +0,0 @@ -const { v4: uuidv4 } = require('uuid'); -const { Meeting, MeetingParticipant, User } = require('../models'); -const ChatRoom = require('../models/chatRooms'); -const chatController = require('../controllers/chatController'); -const MeetingResponse = require('../dtos/MeetingResponse'); -const MeetingDetailResponse = require('../dtos/MeetingDetailResponse'); - -class MeetingService { - async createMeeting(meetingData) { - const { title, description, start_time, end_time, location, deadline, type, created_by } = meetingData; - - const user = await User.findOne({ where: { id: created_by } }); - if (!user) { - throw new Error('사용자를 찾을 수 없습니다.'); - } - - const chatRoomData = { - participants: [user.name], - }; - const chatRoomResponse = await chatController.createChatRoomInternal(chatRoomData); - - if (!chatRoomResponse.success) { - throw new Error('채팅방 생성 실패'); - } - - const chatRoomId = chatRoomResponse.chatRoomId; - - const newMeeting = await Meeting.create({ - title, - description, - start_time, - end_time, - location, - deadline, - type, - created_by, - chatRoomId, - }); - - await MeetingParticipant.create({ - meeting_id: newMeeting.id, - user_id: created_by, - }); - - return { meeting_id: newMeeting.id, chatRoomId: chatRoomResponse.chatRoomId }; - } - - async getMeetings(userId) { - const meetings = await Meeting.findAll({ - attributes: ['id', 'title', 'description', 'start_time', 'end_time', 'location', 'deadline', 'type'], - include: [ - { - model: User, - as: 'creator', - attributes: ['name'], - }, - { - model: MeetingParticipant, - as: 'participants', - attributes: ['user_id'], - }, - ], - }); - - return meetings.map((meeting) => { - const creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; - const isParticipant = meeting.participants.some(participant => participant.user_id === parseInt(userId, 10)); - - return new MeetingResponse( - meeting, - isParticipant, - false, - creatorName - ); - }); - } - - async closeMeeting(meetingId) { - const meeting = await Meeting.findByPk(meetingId); - if (!meeting) { - throw new Error('모임을 찾을 수 없습니다.'); - } - - if (meeting.type === 'CLOSE') { - throw new Error('이미 마감된 모임입니다.'); - } - - meeting.type = 'CLOSE'; - await meeting.save(); - return meeting; - } - - async joinMeeting(meetingId, userId) { - const meeting = await Meeting.findByPk(meetingId); - if (!meeting) { - throw new Error('모임을 찾을 수 없습니다.'); - } - - if(meeting.type === 'CLOSE') { - throw new Error('이미 마감된 모임입니다.'); - } - - if (new Date() > new Date(meeting.deadline)) { - throw new Error('참가 신청이 마감되었습니다.'); - } - - const existingParticipant = await MeetingParticipant.findOne({ - where: { meeting_id: meetingId, user_id: userId } - }); - - if (existingParticipant) { - throw new Error('이미 참가한 사용자입니다.'); - } - - await MeetingParticipant.create({ meeting_id: meetingId, user_id: userId }); - - const user = await User.findOne({ where: { id: userId } }); - const chatRoom = await ChatRoom.findOne({ meeting_id: meetingId }); - - if (chatRoom && !chatRoom.participants.includes(user.name)) { - chatRoom.participants.push(user.name); - chatRoom.isOnline.set(user.name, true); - chatRoom.lastReadAt.set(user.name, new Date()); - chatRoom.lastReadLogId.set(user.name, null); - await chatRoom.save(); - } - } - - async getMeetingDetail(meetingId) { - const meeting = await Meeting.findByPk(meetingId, { - include: [ - { - model: User, - as: 'creator', - attributes: ['name'] - }, - { - model: MeetingParticipant, - as: 'participants', - include: [ - { - model: User, - as: 'participantUser', - attributes: ['name', 'email'] - } - ] - } - ] - }); - - if (!meeting) { - throw new Error('모임을 찾을 수 없습니다.'); - } - - return new MeetingDetailResponse(meeting); - } -} - -module.exports = new MeetingService(); diff --git a/wsServer.js b/wsServer.js deleted file mode 100644 index 3f303b5db4c2b4aadd965b7adead567e547a4efc..0000000000000000000000000000000000000000 --- a/wsServer.js +++ /dev/null @@ -1,296 +0,0 @@ -const http = require('http'); -const crypto = require('crypto'); -// const ChatRoom = require('./models/chatRoom.js'); -const mongoose = require('mongoose'); -const ChatRoom = require('./models/chatRooms'); - -// WebSocket 관련 데이터 -let clients = []; -let chatRooms = {}; - -// MongoDB 연결 설정 -async function connectMongoDB() { - try { - await mongoose.connect('mongodb://localhost:27017/chat', { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - console.log('MongoDB에 성공적으로 연결되었습니다.'); - - // MongoDB 연결 성공 후 WebSocket 서버 시작 - startWebSocketServer(); - } catch (err) { - console.error('MongoDB 연결 실패:', err); - process.exit(1); - } -} - -// 채팅방 기록 불러오기 함수 -async function getChatHistory(chatRoomId) { - const chatRoom = await ChatRoom.findOne({ chatRoomId }); - return chatRoom ? chatRoom.messages : []; -} - -// WebSocket 서버 생성 및 핸드셰이크 처리 -function startWebSocketServer() { - const wsServer = http.createServer((req, res) => { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.end('WebSocket server is running'); - }); - - wsServer.on('upgrade', (req, socket, head) => { - const key = req.headers['sec-websocket-key']; - const acceptKey = generateAcceptValue(key); - const responseHeaders = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - `Sec-WebSocket-Accept: ${acceptKey}` - ]; - socket.write(responseHeaders.join('\r\n') + '\r\n\r\n'); - - // 클라이언트를 clients 배열에 추가 - clients.push(socket); - - let chatRoomId = null; - let nickname = null; - - socket.on('data', async buffer => { - let message; - try { - message = parseMessage(buffer); - const parsedData = JSON.parse(message); - const { type, chatRoomId: clientChatRoomId, nickname: clientNickname, text } = parsedData; - - console.log('서버에서 수신한 메시지:', { type, clientChatRoomId, clientNickname, text }); - - if (type === 'join' || type === 'leave') { - await ChatRoom.updateOne( - { chatRoomId: clientChatRoomId }, - { $set: { [`isOnline.${clientNickname}`]: type === 'join' } } - ); - - const statusMessage = { - type: 'status', - chatRoomId: clientChatRoomId, - nickname: clientNickname, - isOnline: type === 'join', - }; - - clients.forEach(client => { - client.write(constructReply(JSON.stringify(statusMessage))); - }); - } - - if (type === 'join') { - chatRoomId = clientChatRoomId; - nickname = clientNickname; - console.log("join시 chatRoomId", chatRoomId); - console.log("join시 nickname", nickname); - - await ChatRoom.updateOne( - { chatRoomId }, - { - $set: { - [`isOnline.${nickname}`]: true, - [`lastReadLogId.${nickname}`]: null, - }, - } - ); - - if (!chatRooms[chatRoomId]) { - chatRooms[chatRoomId] = []; - } - - const chatRoom = await ChatRoom.findOne({ chatRoomId }); - console.log("join시 chatRoom", chatRoom); - if (!chatRoom) { - console.error(`ChatRoom을 찾을 수 없습니다: chatRoomId = ${chatRoomId}`); - } else { - console.log(`ChatRoom 조회 성공: ${chatRoom}`); - } - - const isAlreadyParticipant = chatRoom.participants.includes(nickname); - if (!isAlreadyParticipant) { - const joinMessage = { - message: `${nickname}님이 참가했습니다.`, - timestamp: new Date(), - type: 'join' - }; - - chatRooms[chatRoomId].push(joinMessage); - - await ChatRoom.updateOne({ chatRoomId }, { - $push: { messages: joinMessage, participants: nickname } - }); - - clients.forEach(client => { - client.write(constructReply(JSON.stringify(joinMessage))); - }); - } else { - console.log(`${nickname}은 이미 채팅방에 참가 중입니다.`); - } - - try { - const previousMessages = await getChatHistory(chatRoomId); - if (previousMessages.length > 0) { - socket.write(constructReply(JSON.stringify({ type: 'previousMessages', messages: previousMessages }))); - console.log(`이전 메시지 전송: ${previousMessages.length}개`); - } - } catch (err) { - console.error('이전 채팅 기록 불러오기 중 오류 발생:', err); - } - - } else if (type === 'message') { - const chatMessage = { - message: text, - timestamp: new Date(), - type: 'message', - sender: nickname - }; - - - chatRooms[chatRoomId].push(chatMessage); - - try { - // 새로운 메시지를 messages 배열에 추가 - const updatedChatRoom = await ChatRoom.findOneAndUpdate( - { chatRoomId }, - { $push: { messages: chatMessage } }, - { new: true, fields: { "messages": { $slice: -1 } } } // 마지막 추가된 메시지만 가져옴 - ); - - // 마지막에 추가된 메시지의 _id를 가져오기 - const savedMessage = updatedChatRoom.messages[updatedChatRoom.messages.length - 1]; - - // 새로운 메시지 전송: 클라이언트로 메시지 브로드캐스트 - const messageData = { - type: 'message', - chatRoomId, - sender: nickname, - message: text, - timestamp: chatMessage.timestamp, - _id: savedMessage._id // 저장된 메시지의 _id 사용 - }; - - clients.forEach(client => { - client.write(constructReply(JSON.stringify(messageData))); - console.log('채팅 메시지 전송:', messageData); - }); - - } catch (err) { - console.error('MongoDB 채팅 메시지 저장 오류:', err); - } - } else if (type === 'leave') { - const leaveMessage = { message: `${nickname}님이 퇴장했습니다.`, timestamp: new Date() }; - chatRooms[chatRoomId].push(leaveMessage); - - await ChatRoom.updateOne( - { chatRoomId }, - { $set: { [`isOnline.${nickname}`]: false } } - ); - - await ChatRoom.updateOne({ chatRoomId }, { - $push: { messages: leaveMessage }, - $pull: { participants: nickname } - }); - - clients.forEach(client => { - client.write(constructReply(JSON.stringify(leaveMessage))); - }); - - clients = clients.filter(client => client !== socket); - } - } catch (err) { - console.error('메시지 처리 중 오류 발생:', err); - } - }); - - socket.on('close', async () => { - if (nickname && chatRoomId) { - await ChatRoom.updateOne( - { chatRoomId }, - { $set: { [`isOnline.${nickname}`]: false } } - ); - - const statusMessage = { - type: 'status', - chatRoomId, - nickname, - isOnline: false, - }; - - clients.forEach(client => { - client.write(constructReply(JSON.stringify(statusMessage))); - }); - } - }); - - socket.on('error', (err) => { - console.error(`WebSocket error: ${err}`); - clients = clients.filter(client => client !== socket); - }); - }); - - wsServer.listen(8081, () => { - console.log('WebSocket 채팅 서버가 8081 포트에서 실행 중입니다.'); - }); -} - -// Sec-WebSocket-Accept 헤더 값 생성 -> env처리 -function generateAcceptValue(key) { - return crypto.createHash('sha1').update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary').digest('base64'); -} - -// WebSocket 메시지 파싱 함수 -function parseMessage(buffer) { - const byteArray = [...buffer]; - const secondByte = byteArray[1]; - let length = secondByte & 127; - let maskStart = 2; - - if (length === 126) { - length = (byteArray[2] << 8) + byteArray[3]; - maskStart = 4; - } else if (length === 127) { - length = 0; - for (let i = 0; i < 8; i++) { - length = (length << 8) + byteArray[2 + i]; - } - maskStart = 10; - } - - const dataStart = maskStart + 4; - const mask = byteArray.slice(maskStart, dataStart); - const data = byteArray.slice(dataStart, dataStart + length).map((byte, i) => byte ^ mask[i % 4]); - - return new TextDecoder('utf-8').decode(Uint8Array.from(data)); -} - -// 클라이언트 메시지 응답 생성 함수 -function constructReply(message) { - const messageBuffer = Buffer.from(message, 'utf-8'); - const length = messageBuffer.length; - const reply = [0x81]; - if (length < 126) { - reply.push(length); - } else if (length < 65536) { - reply.push(126, (length >> 8) & 255, length & 255); - } else { - reply.push( - 127, - (length >> 56) & 255, - (length >> 48) & 255, - (length >> 40) & 255, - (length >> 32) & 255, - (length >> 24) & 255, - (length >> 16) & 255, - (length >> 8) & 255, - length & 255 - ); - } - return Buffer.concat([Buffer.from(reply), messageBuffer]); -} - -// MongoDB 연결 후 WebSocket 서버 시작 -connectMongoDB(); \ No newline at end of file