Skip to content
Snippets Groups Projects
Select Git revision
  • d2170286fdb7620475279eb4187fde682fe5e943
  • main default protected
  • issue-28
3 results

userController.js

Blame
  • 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: '사용자 이벤트 조회 중 오류가 발생했습니다.'});
        }
    }