Select Git revision
S3File.java
userController.js 13.71 KiB
const argon2 = require('argon2');
const jwt = require('jsonwebtoken');
const AccountConfig = require('../../config/account');
const sequelize = require('../../config/database');
const User = require('../models/User');
const Profile = require('../models/Profile');
const Crew = require('../models/Crew');
const Event = require('../models/Event');
const UserCrew = require('../models/UserCrew');
const EventParticipants = require('../models/EventParticipants');
const {buildFullImageUrl, removeHostFromImageUrl} = require('./imageController');
const authTokenExpiryTime = '1h';
const listItemsPerPage = 10;
async function hashPassword(password) {
return await argon2.hash(password, {
type: argon2.argon2id,
});
}
async function verifyPassword(hash, plainPassword) {
return await argon2.verify(hash, plainPassword);
}
// JWT 생성
function generateToken(payload) {
return jwt.sign(
payload,
AccountConfig.JWT_SECRET_KEY,
{
expiresIn: authTokenExpiryTime,
},
);
}
exports.signUp = async (req, res) => {
const {name, email, password} = req.body;
if (!name) {
return res.status(400).json({error: '이름을 입력하세요.'});
}
if (!email) {
return res.status(400).json({error: '이메일을 입력하세요.'});
}
if (!password) {
return res.status(400).json({error: '비밀번호를 입력하세요.'});
}
// Check if the user already exists
const user = await User.findOne({
where: {email: email},
});
if (user) {
return res.status(409).json({error: '이미 등록된 사용자입니다.'});
}
const profile = req.body.profile || {};
if (!profile.regionID) {
return res.status(400).json({error: '지역을 입력하세요.'});
}
if (!profile.sportTypeID) {
return res.status(400).json({error: '운동 종목을 입력하세요.'});
}
try {
const tx = await sequelize.transaction();
try {
const user = await User.create({
name,
email,
password: await hashPassword(password),
}, {transaction: tx});
const userProfile = await Profile.create({
profileImage: profile.profileImage,
regionID: profile.regionID,
job: profile.job || null,
birthDate: profile.birthDate || null,
experience: profile.experience || null,
introduction: profile.introduction || null,
sportTypeID: profile.sportTypeID,
userID: user.userID,
}, {transaction: tx});
await tx.commit();
// 생성된 사용자 정보 반환
res.status(201).json({
userID: user.userID,
name: user.name,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
profile: {
...userProfile.toJSON(),
profileImage: buildFullImageUrl(userProfile.profileImage),
},
});
} catch (error) {
await tx.rollback();
console.error('사용자 생성 중 오류:', error);
res.status(500).json({error: '사용자 생성 중 오류가 발생했습니다.'});
}
} catch (error) {
console.error('사용자 생성 중 오류:', error);
res.status(500).json({error: '사용자 생성 중 오류가 발생했습니다.'});
}
}
exports.verifyEmail = async (req, res) => {
const {email} = req.body;
if (!email) {
return res.status(400).json({error: '이메일을 입력하세요.'});
}
const user = await User.findOne({
where: {email: email},
});
if (user) {
return res.status(409).json({
error: '이미 등록된 사용자입니다.'
});
}
res.status(200).json({
message: '사용 가능한 이메일입니다.'
});
}
exports.login = async (req, res) => {
try {
const {email, password} = req.body;
if (!email) {
return res.status(400).json({error: '이메일을 입력하세요.'});
}
if (!password) {
return res.status(400).json({error: '비밀번호를 입력하세요.'});
}
// 이메일로 사용자 조회
const user = await User.findOne({
where: {email: email},
});
// 사용자가 없을 경우
if (!user) {
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
// 비밀번호 검증
const isPasswordMatch = await verifyPassword(user.password, password);
// 비밀번호가 일치하지 않을 경우
if (!isPasswordMatch) {
return res.status(401).json({error: '비밀번호가 일치하지 않습니다.'});
}
// JWT 토큰 생성
const token = await generateToken({
userID: user.userID,
email: user.email,
});
// 토큰 반환
res.status(200).json({
"auth_token": token,
});
} catch (error) {
console.error('로그인 중 오류:', error);
res.status(500).json({error: '로그인 중 오류가 발생했습니다.'});
}
}
exports.getUserById = async (req, res) => {
try {
const userID = req.params.id;
const user = await User.findByPk(userID, {
include: [{model: Profile}],
attributes: ['userID', 'name', 'email', 'createdAt', 'updatedAt'],
});
if (!user) {
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
res.status(200).json({
userID: user.userID,
name: user.name,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
profile: {
...user.Profile.toJSON(),
profileImage: buildFullImageUrl(user.Profile.profileImage),
},
});
} catch (error) {
console.error('사용자 조회 중 오류:', error);
res.status(500).json({error: '사용자 조회 중 오류가 발생했습니다.'});
}
}
exports.updateUser = async (req, res) => {
// TODO: verify the user by auth token with middleware. #14
const userID = req.user.userID;
if (parseInt(req.params.id) !== parseInt(userID)) {
return res.status(403).json({
error: '사용자 정보를 업데이트할 권한이 없습니다.'
});
}
const tx = await sequelize.transaction();
const {password, profile} = req.body;
try {
const user = await User.findByPk(userID, {transaction: tx});
if (!user) {
await tx.rollback();
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
if (password) user.password = await hashPassword(password);
await user.save({transaction: tx});
let userProfile;
if (profile) {
userProfile = await user.getProfile({transaction: tx});
if (userProfile) {
// TODO: Save the image to storage and save the URL to the database
if (profile.profileImage) userProfile.profileImage = removeHostFromImageUrl(profile.profileImage);
if (profile.regionID) userProfile.regionID = profile.regionID;
if (profile.job) userProfile.job = profile.job;
if (profile.birthDate) userProfile.birthDate = profile.birthDate;
if (profile.experience) userProfile.experience = profile.experience;
if (profile.introduction) userProfile.introduction = profile.introduction;
if (profile.sportTypeID) userProfile.sportTypeID = profile.sportTypeID;
await userProfile.save({transaction: tx});
}
}
await tx.commit();
res.status(200).json({
userID: user.userID,
name: user.name,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
profile: {
...userProfile.toJSON(),
profileImage: buildFullImageUrl(userProfile.profileImage),
},
});
} catch (error) {
await tx.rollback();
console.error('사용자 업데이트 중 오류:', error);
res.status(500).json({error: '사용자 업데이트 중 오류가 발생했습니다.'});
}
}
exports.deleteUser = async (req, res) => {
const userID = req.user.userID;
if (parseInt(req.params.id) !== parseInt(userID)) {
return res.status(403).json({
error: '사용자 정보를 삭제할 권한이 없습니다.'
});
}
const tx = await sequelize.transaction();
try {
const user = await User.findByPk(userID, {transaction: tx});
if (!user) {
await tx.rollback();
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
const userProfile = await user.getProfile({transaction: tx});
if (userProfile) {
await userProfile.destroy({transaction: tx});
}
await user.destroy({transaction: tx});
await tx.commit();
res.status(204).json({message: '사용자가 삭제되었습니다.'});
} catch (error) {
await tx.rollback();
console.error('사용자 삭제 중 오류:', error);
res.status(500).json({error: '사용자 삭제 중 오류가 발생했습니다.'});
}
}
exports.listUserCrews = async (req, res) => {
const userID = req.user.userID;
const page = req.query.page || 0;
try {
const user = await User.findByPk(userID);
if (!user) {
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
const crews = await UserCrew.findAll({
where: { userID: userID },
include: [
{
model: Crew,
attributes: [
'crewID',
'name',
'createdDate',
'regionID',
'sportTypeId',
'capacity',
'fee_krw',
'description',
],
include: [
{
model: UserCrew,
attributes: [],
},
],
},
],
attributes: {
include: [
[
sequelize.literal(`(
SELECT COUNT(*)
FROM UserCrew AS uc
WHERE uc.crewID = UserCrew.crewID
)`),
'memberCount',
],
],
},
limit: listItemsPerPage,
offset: parseInt(page, 10) * listItemsPerPage,
order: [[sequelize.literal('crewID'), 'DESC']],
});
// NOTE: cache it.
const joinedCrewCount = await UserCrew.count({
where: {userID: userID},
});
res.status(200).json({
'crews': crews,
'total': joinedCrewCount,
'per_page': listItemsPerPage,
});
} catch (error) {
console.error('사용자 크루 조회 중 오류:', error);
res.status(500).json({error: '사용자 크루 조회 중 오류가 발생했습니다.'});
}
}
exports.listUserEvents = async (req, res) => {
const userID = req.user.userID;
const page = req.query.page || 0;
console.log(userID);
try {
const user = await User.findByPk(userID);
if (!user) {
return res.status(404).json({error: '사용자를 찾을 수 없습니다.'});
}
const events = await EventParticipants.findAll({
where: { userID: userID },
include: [
{
model: Event,
attributes: [
'eventID',
'name',
'eventDate',
'regionID',
'sportTypeId',
'capacity',
'feeCondition',
'createdDate',
],
include: [
{
model: EventParticipants,
attributes: [],
},
],
},
],
attributes: {
include: [
[
sequelize.literal(`(
SELECT COUNT(*)
FROM EventParticipants AS ep
WHERE ep.eventID = EventParticipants.eventID
)`),
'participantCount',
],
],
},
limit: listItemsPerPage,
offset: parseInt(page, 10) * listItemsPerPage,
order: [[sequelize.literal('eventID'), 'DESC']],
});
const joinedEventCount = await EventParticipants.count({
where: {userID: userID},
});
res.status(200).json({
'events': events,
'total': joinedEventCount,
'per_page': listItemsPerPage,
});
} catch (error) {
console.error('사용자 이벤트 조회 중 오류:', error);
res.status(500).json({error: '사용자 이벤트 조회 중 오류가 발생했습니다.'});
}
}