236 lines
6.9 KiB
JavaScript
236 lines
6.9 KiB
JavaScript
/**
|
|
* 인증 컨트롤러
|
|
* 사용자 인증 관련 기능을 처리하는 컨트롤러
|
|
*/
|
|
const jwt = require('jsonwebtoken');
|
|
const { User, Member } = require('../models');
|
|
const { Op } = require('sequelize');
|
|
require('dotenv').config();
|
|
|
|
// 로그인
|
|
exports.login = async (req, res) => {
|
|
try {
|
|
const { username, password } = req.body;
|
|
|
|
// 사용자명 필수 확인
|
|
if (!username) {
|
|
return res.status(400).json({ message: '사용자명은 필수 항목입니다.' });
|
|
}
|
|
|
|
// 사용자 조회
|
|
const user = await User.findOne({ where: { username } });
|
|
|
|
// 사용자가 존재하지 않는 경우
|
|
if (!user) {
|
|
return res.status(401).json({ message: '사용자명 또는 비밀번호가 올바르지 않습니다.' });
|
|
}
|
|
|
|
// 비밀번호 검증
|
|
const isPasswordValid = await user.validatePassword(password);
|
|
|
|
if (!isPasswordValid) {
|
|
return res.status(401).json({ message: '사용자명 또는 비밀번호가 올바르지 않습니다.' });
|
|
}
|
|
|
|
// 마지막 로그인 시간 업데이트
|
|
await user.update({ lastLogin: new Date() });
|
|
|
|
// JWT 토큰 생성
|
|
const token = jwt.sign(
|
|
{
|
|
id: user.id,
|
|
username: user.username,
|
|
email: user.email,
|
|
role: user.role,
|
|
name: user.name
|
|
},
|
|
process.env.JWT_SECRET || 'your-secret-key',
|
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '1d' }
|
|
);
|
|
|
|
// 모임장/운영진으로 참여 중인 클럽 ID 조회
|
|
const clubMembers = await Member.findAll({
|
|
where: {
|
|
userId: user.id,
|
|
memberType: {
|
|
[Op.in]: ['manager', 'owner']
|
|
}
|
|
},
|
|
attributes: ['clubId']
|
|
});
|
|
// console.log(`clubMembers: ${JSON.stringify(clubMembers)}`);
|
|
|
|
// 모임장/운영진 클럽 ID 배열 생성
|
|
const clubManagerIds = clubMembers.map(cm => cm.clubId);
|
|
let clubId = null;
|
|
if(clubManagerIds.length > 0) {
|
|
clubId = clubManagerIds[0];
|
|
}
|
|
|
|
// 사용자 정보에서 비밀번호 제외
|
|
const userData = {
|
|
id: user.id,
|
|
name: user.name,
|
|
username: user.username,
|
|
email: user.email,
|
|
role: user.role,
|
|
status: user.status,
|
|
clubId: clubId
|
|
};
|
|
// console.log(`userData: ${JSON.stringify(userData)}`);
|
|
|
|
res.json({
|
|
token,
|
|
user: userData
|
|
});
|
|
} catch (error) {
|
|
console.error('로그인 오류:', error);
|
|
res.status(500).json({ message: '로그인 중 오류가 발생했습니다.' });
|
|
}
|
|
};
|
|
|
|
// 회원가입
|
|
exports.register = async (req, res) => {
|
|
try {
|
|
const { name, email, username, password, role, status } = req.body;
|
|
|
|
// 필수 필드 검증
|
|
if (!name || !email || !username || !password) {
|
|
return res.status(400).json({ message: '이름, 이메일, 사용자명, 비밀번호는 필수 항목입니다.' });
|
|
}
|
|
|
|
// 이메일 중복 확인
|
|
const existingEmail = await User.findOne({ where: { email } });
|
|
|
|
if (existingEmail) {
|
|
return res.status(400).json({ message: '이미 등록된 이메일입니다.' });
|
|
}
|
|
|
|
// 사용자명 중복 확인
|
|
const existingUsername = await User.findOne({ where: { username } });
|
|
|
|
if (existingUsername) {
|
|
return res.status(400).json({ message: '이미 사용 중인 사용자명입니다.' });
|
|
}
|
|
|
|
// 사용자 생성
|
|
const newUser = await User.create({
|
|
name,
|
|
email,
|
|
username,
|
|
password,
|
|
role: role || 'user',
|
|
status: status || 'active'
|
|
});
|
|
|
|
// 민감한 정보 제외
|
|
const userResponse = {
|
|
id: newUser.id,
|
|
name: newUser.name,
|
|
username: newUser.username,
|
|
email: newUser.email,
|
|
role: newUser.role,
|
|
status: newUser.status,
|
|
createdAt: newUser.createdAt
|
|
};
|
|
|
|
res.status(201).json(userResponse);
|
|
} catch (error) {
|
|
console.error('회원가입 오류:', error);
|
|
res.status(500).json({ message: '회원가입 중 오류가 발생했습니다.' });
|
|
}
|
|
};
|
|
|
|
// 현재 사용자 정보 조회
|
|
exports.getCurrentUser = async (req, res) => {
|
|
try {
|
|
const userId = req.user.id;
|
|
|
|
const user = await User.findByPk(userId, {
|
|
attributes: { exclude: ['password'] }
|
|
});
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
|
|
}
|
|
|
|
res.json(user);
|
|
} catch (error) {
|
|
console.error('사용자 정보 조회 오류:', error);
|
|
res.status(500).json({ message: '사용자 정보를 조회하는 중 오류가 발생했습니다.' });
|
|
}
|
|
};
|
|
|
|
// 프로필 업데이트
|
|
exports.updateProfile = async (req, res) => {
|
|
try {
|
|
const { id, name, email, password, currentPassword } = req.body;
|
|
|
|
// 사용자 조회
|
|
const user = await User.findByPk(id);
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ message: '사용자를 찾을 수 없습니다.' });
|
|
}
|
|
|
|
// 비밀번호 변경이 요청된 경우
|
|
if (password && currentPassword) {
|
|
// 현재 비밀번호 검증
|
|
const isPasswordValid = await user.validatePassword(currentPassword);
|
|
if (!isPasswordValid) {
|
|
return res.status(401).json({ message: '현재 비밀번호가 올바르지 않습니다.' });
|
|
}
|
|
}
|
|
|
|
// 이메일이 변경된 경우 중복 확인
|
|
if (email && email !== user.getDataValue('email')) {
|
|
// 이메일 유효성 검사 (암호화 전)
|
|
if (!/^\S+@\S+\.\S+$/.test(email)) {
|
|
return res.status(400).json({ message: '유효한 이메일 형식이 아닙니다.' });
|
|
}
|
|
|
|
// 이메일 중복 확인 (암호화된 값으로 비교할 수 없으므로 복호화하여 비교)
|
|
const allUsers = await User.findAll();
|
|
const existingUser = allUsers.find(u => u.email === email && u.id !== id);
|
|
|
|
if (existingUser) {
|
|
return res.status(400).json({ message: '이미 사용 중인 이메일입니다.' });
|
|
}
|
|
|
|
// 해당 사용자의 Members 테이블 레코드도 함께 업데이트
|
|
await Member.update(
|
|
{ email, name },
|
|
{ where: { userId: id } }
|
|
);
|
|
}
|
|
|
|
// 프로필 업데이트
|
|
const updateData = {
|
|
name: name || user.name
|
|
// username은 변경하지 않음 - 로그인 식별자로 사용되기 때문
|
|
};
|
|
|
|
// 이메일은 별도로 처리 (암호화되어 있기 때문에)
|
|
if (email) {
|
|
updateData.email = email;
|
|
}
|
|
|
|
// 비밀번호가 제공된 경우에만 업데이트
|
|
if (password && currentPassword) {
|
|
updateData.password = password;
|
|
}
|
|
|
|
await user.update(updateData);
|
|
|
|
// 업데이트된 사용자 정보 반환 (비밀번호 제외)
|
|
const updatedUser = await User.findByPk(id, {
|
|
attributes: { exclude: ['password'] }
|
|
});
|
|
|
|
res.json(updatedUser);
|
|
} catch (error) {
|
|
console.error('프로필 업데이트 오류:', error);
|
|
res.status(500).json({ message: '프로필 업데이트 중 오류가 발생했습니다.' });
|
|
}
|
|
};
|