diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..47c2f58559551d41e03c8584662ea5e0b17df752 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=0000 +DB_NAME=meeting +MONGO_URI=mongodb://localhost:27017/your_mongo_db +PORT=8080 +CALLBACK_URL=http://localhost:8080/auth/google/callback \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3ec544c7a4a5b0f726e269c7550d227a9093f779..f51c03d15e456a71dcab0ead5892f2ded16469f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ -.env \ No newline at end of file +.env/ +config.json/ diff --git a/app.js b/app.js index 30e4db353850f08d3fc669d8d2d7a747c4ecee45..fc5ef29229b18f4847a0f6c5ecfdef24352d3565 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,40 @@ + +// app.js + +require('dotenv').config(); + const express = require('express'); +const session = require('express-session'); +const passport = require('./passport'); // 변경된 경로 +const flash = require('connect-flash'); + const app = express(); + + +// 미들웨어 설정 +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); + +// 세션 설정 +app.use( + session({ + secret: 'your_session_secret', + resave: false, + saveUninitialized: false, + }) +); + +// Passport 초기화 및 세션 연결 +app.use(passport.initialize()); +app.use(passport.session()); + +// 플래시 메시지 (선택 사항) +app.use(flash()); + +// 라우트 설정 +const authRoutes = require('./routes/auth'); +app.use('/auth', authRoutes); + const PORT = process.env.PORT || 3000; app.get('/', (req, res) => { @@ -8,4 +43,4 @@ app.get('/', (req, res) => { app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); -}); \ No newline at end of file +}); diff --git a/config/config.json b/config/config.json index 0f858c669328baf9399cc3b487ef4c0289360e32..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/config/config.json +++ b/config/config.json @@ -1,23 +0,0 @@ -{ - "development": { - "username": "root", - "password": null, - "database": "database_development", - "host": "127.0.0.1", - "dialect": "mysql" - }, - "test": { - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1", - "dialect": "mysql" - }, - "production": { - "username": "root", - "password": null, - "database": "database_production", - "host": "127.0.0.1", - "dialect": "mysql" - } -} diff --git a/config/mongoose.js b/config/mongoose.js new file mode 100644 index 0000000000000000000000000000000000000000..3379779bf664ebd969317df7f7de48e7a74b34be --- /dev/null +++ b/config/mongoose.js @@ -0,0 +1,18 @@ +// config/mongoose.js + +const mongoose = require('mongoose'); + +mongoose.connect(process.env.MONGO_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, +}); + +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/config/sequelize.js b/config/sequelize.js new file mode 100644 index 0000000000000000000000000000000000000000..c814262f78d7785ff4fad276aad06be1d509ca27 --- /dev/null +++ b/config/sequelize.js @@ -0,0 +1,15 @@ +// config/sequelize.js + +const { Sequelize } = require('sequelize'); + +const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, { + host: process.env.DB_HOST, + dialect: 'mysql', // 사용하려는 DBMS에 맞게 변경 + logging: false, + define: { + //timestamps: true, // createdAt, updatedAt 자동 생성 + underscored: true, // created_at 형식의 필드명 사용 + }, +}); + +module.exports = sequelize; \ No newline at end of file diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..8ae9be046f8afd9efcacc14ed35648cad6fb1206 --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,15 @@ +// middlewares/auth.js + +exports.isLoggedIn = (req, res, next) => { + if (req.isAuthenticated()) { + return next(); + } + res.redirect('/auth/login'); +}; + +exports.isNotLoggedIn = (req, res, next) => { + if (!req.isAuthenticated()) { + return next(); + } + res.redirect('/'); +}; diff --git a/models/Friend.js b/models/Friend.js new file mode 100644 index 0000000000000000000000000000000000000000..d9c41e2be2ea225a11caebc7cd5fed37f1391f56 --- /dev/null +++ b/models/Friend.js @@ -0,0 +1,23 @@ +// models/Friend.js + +const { DataTypes } = require('sequelize'); +const sequelize = require('../config/sequelize'); +const User = require('./User'); + +const Friend = sequelize.define('Friend', { + type: { + type: DataTypes.ENUM('NORMAL', 'SPECIAL'), + allowNull: false, + }, +}, { + tableName: 'Friends', + timestamps: true, +}); + +Friend.belongsTo(User, { foreignKey: 'user_id', as: 'user' }); +Friend.belongsTo(User, { foreignKey: 'friend_id', as: 'friend' }); + +User.hasMany(Friend, { foreignKey: 'user_id', as: 'friends' }); +User.hasMany(Friend, { foreignKey: 'friend_id', as: 'friendOf' }); + +module.exports = Friend; diff --git a/models/Friends.js b/models/Friends.js deleted file mode 100644 index 77b56cf79a247c8f237543901402cd2ee5b45b2a..0000000000000000000000000000000000000000 --- a/models/Friends.js +++ /dev/null @@ -1,47 +0,0 @@ -// models/Friend.js -module.exports = (sequelize, DataTypes) => { - const Friend = sequelize.define('Friend', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - user_id: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - onDelete: 'CASCADE', - }, - friend_id: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - onDelete: 'CASCADE', - }, - type: { - type: DataTypes.ENUM('NORMAL', 'SPECIAL'), - allowNull: false, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, - }, { - tableName: 'Friends', - timestamps: false, - indexes: [ - { - unique: true, - fields: ['user_id', 'friend_id'], - }, - ], - }); - - return Friend; -}; diff --git a/models/chatParticipant.js b/models/chatParticipant.js deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/models/chatRoom.js b/models/chatRoom.js deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/models/index.js b/models/index.js index a7921eea49db6545d9f632920e591e578009a1fb..ec1828740c9a7b12d559857568a02e61482422fb 100644 --- a/models/index.js +++ b/models/index.js @@ -1,42 +1,18 @@ -// models/User.js -module.exports = (sequelize, DataTypes) => { - const User = sequelize.define('User', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - validate: { - isEmail: true, // 이메일 형식 검증 - }, - }, - invite_code: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, // 자동으로 UUID 생성 - allowNull: false, - unique: true, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, - // updated_at 추가 (필요 시) - updated_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, - }, { - tableName: 'Users', - timestamps: false, // created_at과 updated_at을 수동으로 관리 - }); +// models/index.js - return User; +const sequelize = require('../config/sequelize'); + +const User = require('./User'); +const Schedule = require('./Schedule'); +const Meeting = require('./Meeting'); +const MeetingParticipant = require('./MeetingParticipant'); //폴더명수정 +const Friend = require('./Friend'); + +module.exports = { + sequelize, + User, + Schedule, + Meeting, + MeetingParticipant, + Friend, }; diff --git a/models/meeting.js b/models/meeting.js index 537335b2b1fca933f401e90ea59b750d50882731..7944e355fef21458c8550ce46bbb6eeb7e3cbc46 100644 --- a/models/meeting.js +++ b/models/meeting.js @@ -1,64 +1,41 @@ // models/Meeting.js -module.exports = (sequelize, DataTypes) => { - const Meeting = sequelize.define('Meeting', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - title: { - type: DataTypes.STRING, - allowNull: false, - }, - description: { - type: DataTypes.TEXT, - allowNull: true, - }, - start_time: { - type: DataTypes.DATE, - allowNull: false, - }, - end_time: { - type: DataTypes.DATE, - allowNull: false, - validate: { - isAfterStart(value) { - if (value <= this.start_time) { - throw new Error('end_time은 start_time 이후여야 합니다.'); - } - }, - }, - }, - location: { - type: DataTypes.STRING, - allowNull: true, - }, - deadline: { - type: DataTypes.DATE, - allowNull: true, - }, - type: { - type: DataTypes.ENUM('OPEN', 'CLOSE'), - allowNull: false, - }, - created_by: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - onDelete: 'CASCADE', - }, - }, { - tableName: 'Meetings', - timestamps: false, - indexes: [ - { - fields: ['created_by'], - }, - ], - }); - return Meeting; -}; +const { DataTypes } = require('sequelize'); +const sequelize = require('../config/sequelize'); +const User = require('./User'); + +const Meeting = sequelize.define('Meeting', { + title: { + type: DataTypes.STRING, + allowNull: false, + }, + description: { + type: DataTypes.TEXT, + }, + start_time: { + type: DataTypes.DATE, + allowNull: false, + }, + end_time: { + type: DataTypes.DATE, + allowNull: false, + }, + location: { + type: DataTypes.STRING, + }, + deadline: { + type: DataTypes.DATE, + }, + type: { + type: DataTypes.ENUM('OPEN', 'CLOSE'), + allowNull: false, + }, +}, { + tableName: 'Meetings', + timestamps: false, +}); + +Meeting.belongsTo(User, { foreignKey: 'created_by', as: 'creator' }); +User.hasMany(Meeting, { foreignKey: 'created_by', as: 'meetings' }); + +module.exports = Meeting; diff --git a/models/meetingParticipant.js b/models/meetingParticipant.js index a989460b727872116a939b895649b2a6361c3d52..716a4420b9c9205398cd8d426b0e7ef4c349fad0 100644 --- a/models/meetingParticipant.js +++ b/models/meetingParticipant.js @@ -1,39 +1,19 @@ // models/MeetingParticipant.js -module.exports = (sequelize, DataTypes) => { - const MeetingParticipant = sequelize.define('MeetingParticipant', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - meeting_id: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Meetings', - key: 'id', - }, - onDelete: 'CASCADE', - }, - user_id: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - onDelete: 'CASCADE', - }, - }, { - tableName: 'MeetingParticipants', - timestamps: false, - indexes: [ - { - unique: true, - fields: ['meeting_id', 'user_id'], - }, - ], - }); - return MeetingParticipant; -}; +const { DataTypes } = require('sequelize'); +const sequelize = require('../config/sequelize'); +const Meeting = require('./Meeting'); +const User = require('./User'); + +const MeetingParticipant = sequelize.define('MeetingParticipant', {}, { + tableName: 'MeetingParticipants', + timestamps: false, +}); + +MeetingParticipant.belongsTo(Meeting, { foreignKey: 'meeting_id', as: 'meeting' }); +Meeting.hasMany(MeetingParticipant, { foreignKey: 'meeting_id', as: 'participants' }); + +MeetingParticipant.belongsTo(User, { foreignKey: 'user_id', as: 'user' }); +User.hasMany(MeetingParticipant, { foreignKey: 'user_id', as: 'meetingParticipations' }); + +module.exports = MeetingParticipant; diff --git a/models/message.js b/models/message.js deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/models/schedule.js b/models/schedule.js index e32af033a79298ccce8aa3d3444627b18c9bfd62..a3878997cdd54f753115bbb352641cb91a71a25e 100644 --- a/models/schedule.js +++ b/models/schedule.js @@ -1,48 +1,28 @@ // models/Schedule.js -module.exports = (sequelize, DataTypes) => { - const Schedule = sequelize.define('Schedule', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - user_id: { - type: DataTypes.BIGINT, - allowNull: false, - references: { - model: 'Users', - key: 'id', - }, - onDelete: 'CASCADE', - }, - title: { - type: DataTypes.STRING, - allowNull: false, - }, - start_time: { - type: DataTypes.DATE, - allowNull: false, - }, - end_time: { - type: DataTypes.DATE, - allowNull: false, - validate: { - isAfterStart(value) { - if (value <= this.start_time) { - throw new Error('end_time은 start_time 이후여야 합니다.'); - } - }, - }, - }, - }, { - tableName: 'Schedules', - timestamps: false, - indexes: [ - { - fields: ['user_id'], - }, - ], - }); - return Schedule; -}; +const { DataTypes } = require('sequelize'); +const sequelize = require('../config/sequelize'); +const User = require('./User'); + +const Schedule = sequelize.define('Schedule', { + title: { + type: DataTypes.STRING, + allowNull: false, + }, + start_time: { + type: DataTypes.DATE, + allowNull: false, + }, + end_time: { + type: DataTypes.DATE, + allowNull: false, + }, +}, { + tableName: 'Schedules', + timestamps: false, +}); + +Schedule.belongsTo(User, { foreignKey: 'user_id', as: 'user' }); +User.hasMany(Schedule, { foreignKey: 'user_id', as: 'schedules' }); + +module.exports = Schedule; diff --git a/models/user.js b/models/user.js index a7921eea49db6545d9f632920e591e578009a1fb..fbf92c144d9a6de814321390a784bc4c66c5d2eb 100644 --- a/models/user.js +++ b/models/user.js @@ -1,42 +1,24 @@ // models/User.js -module.exports = (sequelize, DataTypes) => { - const User = sequelize.define('User', { - id: { - type: DataTypes.BIGINT, - primaryKey: true, - autoIncrement: true, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - validate: { - isEmail: true, // 이메일 형식 검증 - }, - }, - invite_code: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, // 자동으로 UUID 생성 - allowNull: false, - unique: true, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, - // updated_at 추가 (필요 시) - updated_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, + +const { DataTypes } = require('sequelize'); +const sequelize = require('../config/sequelize'); // sequelize 인스턴스 경로에 맞게 수정하세요. + +const User = sequelize.define('User', { + name: { + type: DataTypes.STRING, // VARCHAR + allowNull: false, + }, + email: { + type: DataTypes.STRING, // VARCHAR + allowNull: false, + unique: true, + validate: { + isEmail: true, }, - }, { - tableName: 'Users', - timestamps: false, // created_at과 updated_at을 수동으로 관리 - }); + }, +}, { + tableName: 'Users', + timestamps: true, // createdAt과 updatedAt 자동 관리 +}); - return User; -}; +module.exports = User; diff --git a/package-lock.json b/package-lock.json index 1a8403b9545523b866368e3750e2702dcfbd7e3c..5b6f0cb241482de4514b474d918b3220c9f7acf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "connect-flash": "^0.1.1", "cookie-parser": "^1.4.7", "dotenv": "^16.4.5", "express": "^4.21.1", @@ -17,6 +18,8 @@ "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "sequelize": "^6.37.5", "sequelize-cli": "^6.6.2" }, @@ -207,6 +210,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -528,6 +540,14 @@ "proto-list": "~1.2.1" } }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1971,6 +1991,12 @@ "node": ">=0.10.0" } }, + "node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2028,6 +2054,64 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "license": "MIT", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "license": "MIT", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2071,6 +2155,11 @@ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "license": "MIT" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/pg-connection-string": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", @@ -2822,6 +2911,12 @@ "node": ">= 0.8" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==", + "license": "MIT" + }, "node_modules/umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", diff --git a/package.json b/package.json index e756213aec4f3e54bc1225e7d5bdc7be05bb2eb5..b7918ff5b96dc56a623136139e3b32e8917e2930 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "webback", "version": "1.0.0", - "main": "index.js", + "main": "app.js", "scripts": { + "start": "nodemon app", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -13,6 +14,7 @@ "license": "ISC", "description": "", "dependencies": { + "connect-flash": "^0.1.1", "cookie-parser": "^1.4.7", "dotenv": "^16.4.5", "express": "^4.21.1", @@ -21,6 +23,8 @@ "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", "sequelize": "^6.37.5", "sequelize-cli": "^6.6.2" }, diff --git a/passport/googleStrategy.js b/passport/googleStrategy.js new file mode 100644 index 0000000000000000000000000000000000000000..c5964eff4a79c6fddcea7b7a9d5cb6b07ea583c4 --- /dev/null +++ b/passport/googleStrategy.js @@ -0,0 +1,29 @@ +// passport/googleStrategy.js + +const { Strategy: GoogleStrategy } = require('passport-google-oauth20'); +const User = require('../models/User'); + +module.exports = new GoogleStrategy( + { + clientID: process.env.GOOGLE_CLIENT_ID, // .env 파일에 설정 + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.CALLBACK_URL, + }, + async (accessToken, refreshToken, profile, done) => { + try { + // 프로필에서 사용자 정보 추출 + const email = profile.emails[0].value; + const name = profile.displayName; + + // 데이터베이스에서 사용자 찾거나 생성 + let user = await User.findOne({ where: { email } }); + if (!user) { + user = await User.create({ email, name }); + } + + return done(null, user); + } catch (err) { + return done(err); + } + } +); diff --git a/passport/index.js b/passport/index.js new file mode 100644 index 0000000000000000000000000000000000000000..6f12584b083ce566a93354123eb47740084be6b9 --- /dev/null +++ b/passport/index.js @@ -0,0 +1,25 @@ +// passport/index.js + +const passport = require('passport'); +const User = require('../models/User'); + +// 직렬화 +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +// 역직렬화 +passport.deserializeUser(async (id, done) => { + try { + const user = await User.findByPk(id); + done(null, user); + } catch (err) { + done(err); + } +}); + +// 전략 가져오기 +const googleStrategy = require('./googleStrategy'); +passport.use(googleStrategy); + +module.exports = passport; diff --git a/routes/auth.js b/routes/auth.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7eda249d0f05a64d3537bc0462e2da4f7c6fd831 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -0,0 +1,35 @@ +// routes/auth.js + +const express = require('express'); +const passport = require('passport'); + +const router = express.Router(); + +// GET /auth/login +router.get('/login', (req, res) => { + res.send('<a href="/auth/google">Log in with Google</a>'); +}); + +// GET /auth/logout +router.get('/logout', (req, res) => { + req.logout(() => { + res.redirect('/'); + }); +}); + +// GET /auth/google +router.get( + '/google', + passport.authenticate('google', { scope: ['profile', 'email'] }) +); + +// GET /auth/google/callback +router.get( + '/google/callback', + passport.authenticate('google', { failureRedirect: '/auth/login' }), + (req, res) => { + res.redirect('/'); + } +); + +module.exports = router; diff --git a/schemas/ChatRoomParticipants.js b/schemas/ChatRoomParticipants.js index ad6f74d0844a9e06afab16d994ffc4d7c7630206..09279d1b6ca9c9aaecd466eb23ddeed1598ce59d 100644 --- a/schemas/ChatRoomParticipants.js +++ b/schemas/ChatRoomParticipants.js @@ -1,25 +1,23 @@ -const ChatRoomParticipantSchema = new Schema({ +// schemas/ChatRoomParticipant.js + +const mongoose = require('mongoose'); + +const ChatRoomParticipantSchema = new mongoose.Schema({ chat_room_id: { - type: mongoose.Schema.Types.BigInt, + type: mongoose.Schema.Types.ObjectId, ref: 'ChatRoom', required: true, }, user_id: { - type: mongoose.Schema.Types.BigInt, - ref: 'User', + type: Number, // SQL의 Users 테이블 ID 참조 required: true, }, - joined_at: { - type: Date, - default: Date.now, - }, left_at: { type: Date, default: null, }, +}, { + timestamps: { createdAt: 'joined_at', updatedAt: false }, }); -// 복합 인덱스 생성하여 중복 참여 방지 -ChatRoomParticipantSchema.index({ chat_room_id: 1, user_id: 1 }, { unique: true }); - module.exports = mongoose.model('ChatRoomParticipant', ChatRoomParticipantSchema); diff --git a/schemas/ChatRooms.js b/schemas/ChatRooms.js index 86e3648c32abcb544bfed78cdbd7b21ba4aad4b2..f4a963ef66b49866bd3cc4ebb01220effa3295ef 100644 --- a/schemas/ChatRooms.js +++ b/schemas/ChatRooms.js @@ -1,14 +1,14 @@ +// schemas/ChatRoom.js + const mongoose = require('mongoose'); -const { Schema } = mongoose; -const ChatRoomSchema = new Schema({ +const ChatRoomSchema = new mongoose.Schema({ name: { type: String, required: true, }, meeting_id: { - type: mongoose.Schema.Types.BigInt, // 관계형 DB의 Meetings.id와 연동 - ref: 'Meeting', + type: Number, // SQL의 Meetings 테이블 ID 참조 default: null, }, type: { @@ -17,18 +17,11 @@ const ChatRoomSchema = new Schema({ required: true, }, created_by: { - type: mongoose.Schema.Types.BigInt, // 관계형 DB의 Users.id와 연동 - ref: 'User', + type: Number, // SQL의 Users 테이블 ID 참조 required: true, }, - created_at: { - type: Date, - default: Date.now, - }, - updated_at: { - type: Date, - default: Date.now, - }, +}, { + timestamps: true, // createdAt, updatedAt 자동 관리 }); module.exports = mongoose.model('ChatRoom', ChatRoomSchema); diff --git a/schemas/Messages.js b/schemas/Messages.js index 666289712524068a732777d84fa3c12d0964604f..7c59dcf49a0137362722709dd8941ce5a66990a0 100644 --- a/schemas/Messages.js +++ b/schemas/Messages.js @@ -1,25 +1,23 @@ -const MessageSchema = new Schema({ +// schemas/Message.js + +const mongoose = require('mongoose'); + +const MessageSchema = new mongoose.Schema({ chat_room_id: { - type: mongoose.Schema.Types.BigInt, + type: mongoose.Schema.Types.ObjectId, ref: 'ChatRoom', required: true, }, sender_id: { - type: mongoose.Schema.Types.BigInt, - ref: 'User', + type: Number, // SQL의 Users 테이블 ID 참조 required: true, }, message: { type: String, required: true, }, - sent_at: { - type: Date, - default: Date.now, - }, +}, { + timestamps: { createdAt: 'sent_at', updatedAt: false }, }); -// 인덱스 추가하여 조회 성능 향상 -MessageSchema.index({ chat_room_id: 1, sent_at: -1 }); - module.exports = mongoose.model('Message', MessageSchema); diff --git a/schemas/index.js b/schemas/index.js deleted file mode 100644 index 27a1c429a9b1240f3430eacbd5efe8416651b12b..0000000000000000000000000000000000000000 --- a/schemas/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const mongoose = require('mongoose'); - -// MongoDB 연결 -mongoose.connect('mongodb://localhost:27017/chatDB', { - useNewUrlParser: true, - useUnifiedTopology: true, -}) -.then(() => console.log('MongoDB 연결 성공')) -.catch(err => console.error('MongoDB 연결 실패:', err)); - -// 위에서 정의한 ChatRoom, ChatRoomParticipant, Message 모델을 사용 -const ChatRoom = require('./models/ChatRoom'); -const ChatRoomParticipant = require('./models/ChatRoomParticipant'); -const Message = require('./models/Message'); - -// 예시: 채팅방 생성 -async function createChatRoom(data) { - try { - const chatRoom = new ChatRoom(data); - await chatRoom.save(); - console.log('채팅방 생성 완료:', chatRoom); - } catch (error) { - console.error('채팅방 생성 실패:', error); - } -} - -// 예시 함수 호출 -createChatRoom({ - name: '일반 채팅방', - type: 'OPEN', - created_by: 1, // 관계형 DB의 Users.id와 일치 -}); diff --git a/sync.js b/sync.js new file mode 100644 index 0000000000000000000000000000000000000000..60721173c64df1fd2c31675bfd1d125fe9b7872a --- /dev/null +++ b/sync.js @@ -0,0 +1,26 @@ +// sync.js + +require('dotenv').config(); // 환경 변수 로드 + +const sequelize = require('./config/sequelize'); +const model=require('./models'); // 모델들을 가져옴 (사이드 이펙트로 모델들이 등록됨) + +async function syncDatabase() { + try { + // 데이터베이스 연결 테스트 + await sequelize.authenticate(); + console.log('데이터베이스 연결 성공.'); + + // 모든 모델 동기화 + await sequelize.sync({ force: true }); + console.log('모든 모델이 성공적으로 동기화되었습니다.'); + + // 연결 종료 + await sequelize.close(); + console.log('데이터베이스 연결이 종료되었습니다.'); + } catch (error) { + console.error('데이터베이스 연결 실패:', error); + } +} + +syncDatabase();