Select Git revision
app.js 14.28 KiB
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const cors = require('cors');
//app.use(express.urlencoded({ extended: false }));
const passport = require('./config/passport');
//const cookieParser = require('cookie-parser');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(passport.initialize());
//const session = require('express-session');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const secretKey = process.env.JWT_SECRET; // 실제 환경에서는 환경 변수로 관리하세요.
const refreshTokenSecretKey = process.env.JWT_REFRESH_SECRET;
const https = require('https');
const fs = require('fs');
const expressSanitizer = require('express-sanitizer');
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(expressSanitizer());
app.use('/', express.static('public'));
// SSL 인증서와 개인 키 파일 경로 설정
// SSL 옵션 설정
const sslOptions = {
key: fs.readFileSync('./cert/server.key'),
cert: fs.readFileSync('./cert/server.cert'),
};
// // HTTP 서버 설정 (포트 8001)
// const HTTP_PORT = 8001;
// app.listen(HTTP_PORT, () => {
// console.log(`HTTP server started on port ${HTTP_PORT}`);
// });
// // HTTPS 서버 설정 (포트 8080)
// const HTTPS_PORT = 8080;
// https.createServer(options, app).listen(HTTPS_PORT, () => {
// console.log(`HTTPS server started on port ${HTTPS_PORT}`);
// });
//middleware
app.use(cors({
origin: "https://localhost:3000",
credentials: true, //쿠키전송 허용
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
//비밀번호 변경 라우트 등록
const passwordRoutes = require('./route/conf_password');
//닉네임 변경 라우트 등록
const confNameRoutes = require('./route/conf_name.js');
//회원 탈퇴 라우트 등록
const deleteUser = require('./route/del_user');
//비밀번호 암호화용 모듈
const bcrypt = require('bcrypt');
//User define
const User = require('./models/user');
//Trip define
const Trip = require('./models/trips');
//Favorite define
const Favorite = require('./models/favorite');
// JSON 요청 본문을 파싱하는 미들웨어 추가
app.use(express.json());
//비밀번호 변경 라우트 추가
app.use('/conf_password',passwordRoutes);
//이름 변경 라우트
app.use('/conf_name', confNameRoutes);
//회원 탈퇴 라우트
app.use('/del_user', deleteUser);
//여행 라우트
const tripRoutes = require('./route/tripRoute'); // 라우트 파일 경로
// Routes
app.use('/api/trips', tripRoutes);
//리뷰 라우트
const reviewRoutes = require('./route/review'); // 라우트 파일 경로
app.use('/api/review', reviewRoutes);
//dotenv
require('dotenv').config();
//favorite 라우트
const favoriteRoutes = require('./route/favorites'); // 라우트 파일 경로
app.use('/favorites', favoriteRoutes);
const authenticateToken = require('./middleware/authMiddleware.js');
// 보호된 라우트에 미들웨어 적용
app.use('/conf_password', authenticateToken, passwordRoutes);
app.use('/conf_name', authenticateToken, confNameRoutes);
app.use('/del_user', authenticateToken, deleteUser);
//확인
console.log('GOOGLE_CLIENT_ID:', process.env.GOOGLE_CLIENT_ID);
console.log('GOOGLE_CLIENT_SECRET:', process.env.GOOGLE_CLIENT_SECRET);
// // MongoDB 연결
// mongoose.connect('mongodb://localhost:27017/logininfo', {
// useNewUrlParser: true,
// useUnifiedTopology: true,
// }).then(() => console.log('MongoDB 연결 성공'))
// .catch(err => console.error('MongoDB 연결 오류:', err));
// application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }));
// db connect
mongoose
.connect(process.env.mongoURL, {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: "festivelo_DB" // 이 이름으로 db가 생성됩니다.
})
.then(() => {console.log(`MongoDB connected to festivelo_DB`);
// DB 연결 확인
const db = mongoose.connection;
console.log('Current database:', db.name);
})
.catch((err) => console.error(err));
// 세션 설정
// app.use(session({
// secret: 'your_secret_key',
// resave: false,
// saveUninitialized: false,
// }));
// Passport 초기화
//app.use(passport.initialize());
//app.use(passport.session());
// Google 로그인 라우트
app.get('/googlelogin',
passport.authenticate('google', { scope: ['profile', 'email'], session: false })
);
//jwt 전--------------------------------------
// app.get('/googlelogin/redirect',
// passport.authenticate('google', { failureRedirect: '/' }),
// (req, res) => {
// const userInfo = encodeURIComponent(JSON.stringify({
// _id: req.user._id,
// email: req.user.email,
// name: req.user.name
// }));
// // 로그인 성공 시 리다이렉트
// res.redirect(`http://localhost:3000/login?user=${userInfo}`);
// console.log(`구글 로그인 성공: ${userInfo}`);
// }
// );
//----------------------------------------------
//jwt 후-----------------------------------------
app.get('/googlelogin/redirect',
passport.authenticate('google', { failureRedirect: '/', session: false }),
async (req, res) => {
const accessToken = jwt.sign(
{ userId: req.user._id, email: req.user.email, name: req.user.name },
secretKey,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: req.user._id },
refreshTokenSecretKey,
{ expiresIn: '7d' } // 리프레시 토큰 유효 기간: 7일
);
// 토큰 로그 출력
console.log('생성된 액세스 토큰:', accessToken);
console.log('생성된 리프레시 토큰:', refreshToken);
// 사용자 데이터베이스에 리프레시 토큰 저장
req.user.refreshToken = refreshToken;
await req.user.save();
// 쿠키에 토큰 저장
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
maxAge: 15 * 60 * 1000 // 15분
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7일
});
//res.redirect(`http://localhost:3000/login#token=${token}`);
res.redirect('https://localhost:3000/');
console.log(`구글 로그인 성공: ${req.user.email}`);
}
);
//-------------------------------------------------
// 로그아웃
app.get('/logout', async(req, res) => {
// req.logout((err) => {
// if (err) { return next(err); }
// res.redirect('/');
// });
//res.clearCookie('token'); //쿠키삭제
const refreshToken = req.cookies.refreshToken;
// 리프레시 토큰 무효화
if (refreshToken) {
try {
const decoded = jwt.verify(refreshToken, refreshTokenSecretKey);
const user = await User.findById(decoded.userId);
if (user) {
user.refreshToken = null;
await user.save();
}
} catch (err) {
console.error('리프레시 토큰 검증 오류:', err);
}
}
// 쿠키 삭제
res.clearCookie('accessToken');
res.clearCookie('refreshToken');
res.redirect('/');
});
// 토큰 재발급 엔드포인트
app.post('/token', async (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(401);
try {
const decoded = jwt.verify(refreshToken, refreshTokenSecretKey);
const user = await User.findById(decoded.userId);
if (!user || user.refreshToken !== refreshToken) {
return res.sendStatus(403);
}
const newAccessToken = jwt.sign(
{ userId: user._id, email: user.email, name: user.name },
secretKey,
{ expiresIn: '15m' }
);
res.cookie('accessToken', newAccessToken, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
maxAge: 15 * 60 * 1000 // 15분
});
res.status(200).json({ message: '토큰 재발급 성공' });
} catch (error) {
console.error('토큰 재발급 오류:', error);
res.sendStatus(403);
}
});
// 대시보드
app.get('/dashboard', authenticateToken, (req, res) => {
//const hasPassword = !!req.user.password; // 비밀번호가 있는지 확인
//const userName = req.user.name || req.user.email || 'Guest';
res.send(`
<h1>Welcome, ${userName}}</h1>
<a href="/conf_name/change">사용할 이름 변경</a>
<br>
${hasPassword ? '<a href="/conf_password/change">비밀번호 변경</a>' : ''}
<br>
<a href="/logout">Logout</a>
`);
});
// // 홈
// app.get('/', (req, res) => {
// res.send(`
// <h1>Google OAuth Login</h1>
// <a href="/googlelogin">Login with Google</a>
// `);
// });
//signup 경로
app.get('/signup', (req, res) => {
res.sendFile(__dirname + '/public/signup.html'); // login.html 파일 경로 설정
});
//session
// app.get('/conf_password', (req, res) => {
// if (req.isAuthenticated && req.isAuthenticated()) { // 로그인 여부 확인
// res.sendFile(__dirname + '/public/conf_password.html');
// }
// });
//jwt
app.get('/conf_password', authenticateToken, (req, res) => {
res.sendFile(__dirname + '/public/conf_password.html');
});
//홈 - index.html
app.use(express.static('public'));
//일반 로그인
app.post('/login', async (req, res) => {
const { email, password } = req.body;
console.log('로그인 요청:', email, password); // 추가된 로그
try {
// 사용자가 존재하는지 확인
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ message: '사용자를 찾을 수 없습니다.' });
}
//password 정보 없는 유저 -> 구글 로그인 사용자
if (!user.password) {
return res.status(400).json({ message: '사용자를 찾을 수 없습니다.' });
}
// 비밀번호 검증
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ message: '비밀번호가 일치하지 않습니다.' });
}
// 로그인 성공 시 사용자 세션에 저장 - 세션 로그인
// req.login(user, (err) => {
// if (err) {
// console.error('로그인 세션 저장 오류:', err);
// return res.status(500).json({ message: '로그인 세션 저장 중 오류가 발생했습니다.' });
// }
// res.status(200).json({ message: '로그인 성공', user: {
// _id: user._id,
// email: user.email,
// name: user.name
// } });
// console.log('로그인 성공: ', user);
//jwt---------------------------------------
const accessToken = jwt.sign(
{ userId: user._id, email: user.email, name: user.name },
secretKey,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user._id },
refreshTokenSecretKey,
{ expiresIn: '7d' }
);
// 사용자 데이터베이스에 리프레시 토큰 저장
user.refreshToken = refreshToken;
await user.save();
//https 환경에서만
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true, // 프로덕션 환경에서만 secure 플래그 활성화
sameSite: 'Strict', // CSRF 방지를 위해 sameSite 옵션 설정
maxAge: 3600000 // 1시간 (토큰 만료 시간과 동일하게 설정)
});
res.status(200).json({
message: '로그인 성공',
accessToken: accessToken,
user: {
_id: user._id,
email: user.email,
name: user.name
}
});
} catch (error) {
console.error('로그인 오류:', error);
res.status(500).json({ message: '서버 오류가 발생했습니다.' });
}
});
//웹사이트 내 회원가입
app.post('/signup', async (req, res) => {
const { email, password, name } = req.body;
try {
// 존재하는 사용자 확인
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ message: '이미 존재하는 사용자입니다.' });
}
// 비밀번호 암호화
const hashedPassword = await bcrypt.hash(password, 10);
// 새 사용자 생성 및 저장
user = new User({
email,
password: hashedPassword, // 암호화된 비밀번호 저장
name,
});
await user.save();
// if((!user.password)||(!user.email)||(!user.name)){
// res.status(500).json({ message: '요청 정보가 누락되었습니다.'});
// }
res.status(201).json({ message: '회원가입 성공' });
} catch (error) {
console.error('회원가입 오류:', error);
res.status(500).json({ message: '서버 오류가 발생했습니다.' });
}
});
app.get('/auth/status', authenticateToken, (req, res) => {
res.status(200).json({
isAuthenticated: true,
user: {
_id: req.user.userId,
email: req.user.email,
name: req.user.name
}
});
});
// app.listen(process.env.PORT, () => {
// console.log(`Server is running on http://localhost:${process.env.PORT}`);
// });
// HTTPS 강제 적용 미들웨어 (선택 사항)
app.use((req, res, next) => {
if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
next();
} else {
res.redirect(`https://${req.headers.host}${req.url}`);
}
});
https.createServer(sslOptions, app).listen(process.env.PORT || 8000, () => {
console.log(`HTTPS 서버가 실행 중입니다: https://localhost:${process.env.PORT || 8000}`);
});
// HTTP 서버 실행 (선택 사항)
// http.createServer(app).listen(8080, () => {
// console.log('HTTP 서버가 실행 중입니다: http://localhost:8080');
// });