diff --git a/packages/api/server/controllers/auth.http b/packages/api/server/controllers/http/auth.http similarity index 76% rename from packages/api/server/controllers/auth.http rename to packages/api/server/controllers/http/auth.http index b6ae7ee..7d831f5 100644 --- a/packages/api/server/controllers/auth.http +++ b/packages/api/server/controllers/http/auth.http @@ -1,18 +1,19 @@ +### POST /logout POST http://localhost:3000/api/auth/logout Content-Type: application/json {"refreshToken": "244246b8-4024-4044-bd71-6fa00731ad6f"} -### +### POST /refresh POST http://localhost:3000/api/auth/refresh Content-Type: application/json {"refreshToken": "244246b8-4024-4044-bd71-6fa00731ad6f"} -### +### POST /login POST http://localhost:3000/api/auth/login Content-Type: application/json {"email": "wonho@workly.page", "password": "test1234"} -### +> {% client.global.set("accessToken", response.body.accessToken); %} diff --git a/packages/api/server/controllers/http/team.http b/packages/api/server/controllers/http/team.http new file mode 100644 index 0000000..05fd738 --- /dev/null +++ b/packages/api/server/controllers/http/team.http @@ -0,0 +1,23 @@ +### GET /{id}/members +GET http://localhost:3000/api/teams/1/members +Accept: application/json +Authorization: Bearer {{accessToken}} + +### POST /{id}/join +POST http://localhost:3000/api/teams/2/join +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{"userId": 2, "teamId": 1} + +### GET /{name} +GET http://localhost:3000/api/teams/Appetis +Accept: application/json +Authorization: Bearer {{accessToken}} + +### POST / +POST http://localhost:3000/api/teams +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{"name": "Appetis", "userId": 1} diff --git a/packages/api/server/controllers/http/user.http b/packages/api/server/controllers/http/user.http new file mode 100644 index 0000000..a88e6ea --- /dev/null +++ b/packages/api/server/controllers/http/user.http @@ -0,0 +1,93 @@ +### POST /{id}/avatar +POST http://localhost:3000/api/users/1/avatar +Content-Type: multipart/form-data; boundary=WebAppBoundary +Authorization: Bearer {{accessToken}} +#Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM + +--WebAppBoundary +Content-Disposition: form-data; name="avatar"; filename="avatar.png" + +< /Users/wonho/Downloads/avatar.png +--WebAppBoundary-- + +### POST /{id}/profile +POST http://localhost:3000/api/users/1/profile +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "name": "Wonho Lee", + "department": "IT", + "position": "Developer", + "phone": "111-333-2222", + "phone_ext": "333", + "status": "OF" +} + +### POST /{id}/verify +POST http://localhost:3000/api/users/2/verify +Content-Type: application/json + +{"code": "278645"} + +### POST / +POST http://localhost:3000/api/users +Content-Type: application/json + +{"email": "test1@test.com", "password": "test1234"} + +### GET / +GET http://localhost:3000/api/users +Accept: application/json +Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM + +### GET /{id} +GET http://localhost:3000/api/users/1 +Accept: application/json +Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM + +### POST /{id}/name +POST http://localhost:3000/api/users/1/name +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "name": "Wono" +} + +### POST /{id}/department +POST http://localhost:3000/api/users/1/department +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "department": "IT1" +} + +### POST /{id}/position +POST http://localhost:3000/api/users/1/position +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "position": "Developer1" +} + +### POST /{id}/phone +POST http://localhost:3000/api/users/1/phone +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "phone": "999-999-9999" +} + +### POST /{id}/phone-ext +POST http://localhost:3000/api/users/1/phone-ext +Content-Type: application/json +Authorization: Bearer {{accessToken}} + +{ + "phoneExt": "999" +} + diff --git a/packages/api/server/controllers/team.http b/packages/api/server/controllers/team.http deleted file mode 100644 index 21514a2..0000000 --- a/packages/api/server/controllers/team.http +++ /dev/null @@ -1,24 +0,0 @@ -GET http://localhost:3000/api/teams/1/members -Accept: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjMzMzE4MTE1LCJleHAiOjE2MzMzMTk5MTUsImlzcyI6IndvcmtseSJ9.ybITMvGOrBqggiR9lgFjXPn0eGHHzene7tS5k91pi7k - -### -POST http://localhost:3000/api/teams/2/join -Content-Type: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjMzMzE4MTE1LCJleHAiOjE2MzMzMTk5MTUsImlzcyI6IndvcmtseSJ9.ybITMvGOrBqggiR9lgFjXPn0eGHHzene7tS5k91pi7k - -{"userId": 2, "teamId": 1} - -### -GET http://localhost:3000/api/teams/Appetis -Accept: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjMzMzE4MTE1LCJleHAiOjE2MzMzMTk5MTUsImlzcyI6IndvcmtseSJ9.ybITMvGOrBqggiR9lgFjXPn0eGHHzene7tS5k91pi7k - -### -POST http://localhost:3000/api/teams -Content-Type: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjMzNTcxNjU4LCJleHAiOjE2MzM1NzM0NTgsImlzcyI6IndvcmtseSJ9.JCg3q2hg1bQg682MoV9QEQDo2P9pK9ljtRLqeykgnh4 - -{"name": "Appetis", "userId": 1} - -### diff --git a/packages/api/server/controllers/user.http b/packages/api/server/controllers/user.http deleted file mode 100644 index df9d6f1..0000000 --- a/packages/api/server/controllers/user.http +++ /dev/null @@ -1,53 +0,0 @@ -POST http://localhost:3000/api/users/1/avatar -Content-Type: multipart/form-data; boundary=WebAppBoundary -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM - ---WebAppBoundary -Content-Disposition: form-data; name="avatar"; filename="avatar.png" - -< /Users/wonho/Downloads/avatar.png ---WebAppBoundary-- - -### -POST http://localhost:3000/api/users/1/profile -Content-Type: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM - -{ - "name": "Wonho Lee", - "department": "IT", - "position": "Developer", - "phone": "111-333-2222", - "phone_ext": "333", - "status": "OF" -} - -### -POST http://localhost:3000/api/users/2/verify -Content-Type: application/json - -{"code": "278645"} - -### -POST http://localhost:3000/api/users -Content-Type: application/json - -{"email": "test1@test.com", "password": "test1234"} - -### -GET http://localhost:3000/api/users -Accept: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM - -### -GET http://localhost:3000/api/users/1 -Accept: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM - -### - -GET http://localhost:3000/api/users/1/status -Accept: application/json -Authorization: Bareer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjM2NDMzMDEzLCJleHAiOjE2MzY0MzQ4MTMsImlzcyI6IndvcmtseSJ9.TNpDA_0h4eRVcWkSWf_yxQ0L1jnVJaXdvRFmPKTduNM - -### \ No newline at end of file diff --git a/packages/api/server/controllers/user.js b/packages/api/server/controllers/user.js index 947c915..532525f 100644 --- a/packages/api/server/controllers/user.js +++ b/packages/api/server/controllers/user.js @@ -24,9 +24,9 @@ const verifyStatus = async (user, userVerification) => { await userVerification.update({ status: 'VE' }); }; -const createOrUpdateUserProfile = async (userId, data) => { +const createOrUpdateUserProfile = async (user, data) => { const profileData = { - UserId: userId, + UserId: user.id, name: data.name, department: data.department, position: data.position, @@ -35,10 +35,12 @@ const createOrUpdateUserProfile = async (userId, data) => { status: data.status, }; - let profile = await userService.getUserProfile(userId); + let profile = await userService.getUserProfile(user.id); + if (profile) { profile = await profile.update(profileData); } else { + profileData.initial = user.email.substring(0, 2).toUpperCase(); profile = await Profile.create(profileData); } @@ -191,7 +193,7 @@ exports.verify = async (req, res) => { exports.updateProfile = async (req, res) => { try { - const userId = req.params.id; + const userId = parseInt(req.params.id, 10); const user = await userService.getUserWithProfileById(userId); if (!user) { return res.status(400).json({ @@ -200,7 +202,7 @@ exports.updateProfile = async (req, res) => { }); } - const profile = await createOrUpdateUserProfile(userId, req.body); + const profile = await createOrUpdateUserProfile(user, req.body); return res.status(200).json({ code: 200, @@ -268,3 +270,148 @@ exports.updateAvatar = async (req, res) => { }); } }; + +exports.updateName = async (req, res) => { + const { name } = req.body; + const userId = parseInt(req.params.id, 10); + + try { + const profile = await userService.getUserProfile(userId); + if (!profile) { + return res.status(400).json({ + code: 400, + message: 'Cannot find the user profile', + }); + } + + await profile.update({ name }); + + return res.status(200).json({ + code: 200, + message: 'Updated the name', + name, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ + code: 500, + message: 'Server error', + }); + } +}; + +exports.updateDepartment = async (req, res) => { + const { department } = req.body; + const userId = parseInt(req.params.id, 10); + + try { + const profile = await userService.getUserProfile(userId); + if (!profile) { + return res.status(400).json({ + code: 400, + message: 'Cannot find the user profile', + }); + } + + await profile.update({ department }); + + return res.status(200).json({ + code: 200, + message: 'Updated the position', + department, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ + code: 500, + message: 'Server error', + }); + } +}; + +exports.updatePosition = async (req, res) => { + const { position } = req.body; + const userId = parseInt(req.params.id, 10); + + try { + const profile = await userService.getUserProfile(userId); + if (!profile) { + return res.status(400).json({ + code: 400, + message: 'Cannot find the user profile', + }); + } + + await profile.update({ position }); + + return res.status(200).json({ + code: 200, + message: 'Updated the position', + position, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ + code: 500, + message: 'Server error', + }); + } +}; + +exports.updatePhone = async (req, res) => { + const { phone } = req.body; + const userId = parseInt(req.params.id, 10); + + try { + const profile = await userService.getUserProfile(userId); + if (!profile) { + return res.status(400).json({ + code: 400, + message: 'Cannot find the user profile', + }); + } + + await profile.update({ phone }); + + return res.status(200).json({ + code: 200, + message: 'Updated the phone', + phone, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ + code: 500, + message: 'Server error', + }); + } +}; + +exports.updatePhoneExt = async (req, res) => { + const { phoneExt } = req.body; + const userId = parseInt(req.params.id, 10); + + try { + const profile = await userService.getUserProfile(userId); + if (!profile) { + return res.status(400).json({ + code: 400, + message: 'Cannot find the user profile', + }); + } + + await profile.update({ phoneExt }); + + return res.status(200).json({ + code: 200, + message: 'Updated the phone_ext', + phoneExt, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ + code: 500, + message: 'Server error', + }); + } +}; diff --git a/packages/api/server/models/color.js b/packages/api/server/models/color.js new file mode 100644 index 0000000..7548f69 --- /dev/null +++ b/packages/api/server/models/color.js @@ -0,0 +1,21 @@ +module.exports = (sequelize, DataTypes) => { + const Color = sequelize.define( + 'Color', + { + color: { + type: DataTypes.STRING(7), + allowNull: false, + }, + }, + { + charset: 'utf8', + collate: 'utf8_general_ci', + }, + ); + + Color.associate = db => { + db.Color.hasMany(db.Profile); + }; + + return Color; +}; diff --git a/packages/api/server/models/index.js b/packages/api/server/models/index.js index ad4fd25..61d4dd4 100644 --- a/packages/api/server/models/index.js +++ b/packages/api/server/models/index.js @@ -14,6 +14,7 @@ db.UserTeam = require('./user_team')(sequelize, Sequelize); db.Verification = require('./verification')(sequelize, Sequelize); db.Code = require('./code')(sequelize, Sequelize); db.Profile = require('./profile')(sequelize, Sequelize); +db.Color = require('./color')(sequelize, Sequelize); db.Dashboard = require('./dashboard')(sequelize, Sequelize); db.Link = require('./link')(sequelize, Sequelize); db.Event = require('./event')(sequelize, Sequelize); diff --git a/packages/api/server/models/profile.js b/packages/api/server/models/profile.js index 9910504..6ce1d7e 100644 --- a/packages/api/server/models/profile.js +++ b/packages/api/server/models/profile.js @@ -6,6 +6,10 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING(50), allowNull: false, }, + initial: { + type: DataTypes.STRING(2), + allowNull: false, + }, avatar: { type: DataTypes.STRING(200), }, @@ -38,6 +42,7 @@ module.exports = (sequelize, DataTypes) => { Profile.associate = db => { db.Profile.belongsTo(db.User); + db.Profile.belongsTo(db.Color); }; return Profile; diff --git a/packages/api/server/routes/user.js b/packages/api/server/routes/user.js index e4a8ae1..7768fbd 100644 --- a/packages/api/server/routes/user.js +++ b/packages/api/server/routes/user.js @@ -10,6 +10,11 @@ module.exports = app => { router.post('/users/:id/verify', userController.verify); router.post('/users/:id/profile', verifyToken, userController.updateProfile); router.post('/users/:id/avatar', verifyToken, upload.single('avatar'), userController.updateAvatar); + router.post('/users/:id/name', verifyToken, userController.updateName); + router.post('/users/:id/department', verifyToken, userController.updateDepartment); + router.post('/users/:id/position', verifyToken, userController.updatePosition); + router.post('/users/:id/phone', verifyToken, userController.updatePhone); + router.post('/users/:id/phone-ext', verifyToken, userController.updatePhoneExt); app.use('/api', router); }; diff --git a/packages/api/server/seeders/0.1.0.6-color.js b/packages/api/server/seeders/0.1.0.6-color.js new file mode 100644 index 0000000..6cf346c --- /dev/null +++ b/packages/api/server/seeders/0.1.0.6-color.js @@ -0,0 +1,85 @@ +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.bulkInsert('Colors', [ + { + color: '#042F31', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#4DA5AA', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#E6F2F3', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#FAFAFA', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#151515', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#1B5289', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#3B2B98', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#883955', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#BB6811', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#ECE9EA', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#E6F2F3', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#D7EBFF', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#E1DFEF', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#F0D0E0', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + color: '#FFDFBE', + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('Colors', null, {}); + }, +}; diff --git a/packages/api/server/seeders/0.1.0.3-profile.js b/packages/api/server/seeders/0.1.0.7-profile.js similarity index 92% rename from packages/api/server/seeders/0.1.0.3-profile.js rename to packages/api/server/seeders/0.1.0.7-profile.js index f150cc0..cd59268 100644 --- a/packages/api/server/seeders/0.1.0.3-profile.js +++ b/packages/api/server/seeders/0.1.0.7-profile.js @@ -6,7 +6,9 @@ module.exports = { { id: 1, UserId: 1, + ColorId: 12, name: 'Wonho Lee', + initial: 'WL', avatar: '', department: 'IT', position: 'Developer', @@ -19,7 +21,9 @@ module.exports = { { id: 2, UserId: 2, + ColorId: 13, name: 'Hyewon Kim', + initial: 'HK', avatar: '', department: 'IT', position: 'Developer', diff --git a/packages/api/server/services/user.service.js b/packages/api/server/services/user.service.js index 15a1eb4..7d5ce3b 100644 --- a/packages/api/server/services/user.service.js +++ b/packages/api/server/services/user.service.js @@ -1,4 +1,4 @@ -const { Code, User, Profile, Team } = require('../models'); +const { Code, User, Profile, Color, Team } = require('../models'); const getStatusNameByCode = async statusCode => { const code = await Code.findOne({ @@ -11,11 +11,27 @@ const getStatusNameByCode = async statusCode => { return code.name; }; +const getColorById = async colorId => { + const color = await Color.findOne({ + where: { + id: colorId, + }, + attributes: ['color'], + }); + + return color.color; +}; + const setProfileStatusName = async profile => { const statusName = await getStatusNameByCode(profile.status); profile.setDataValue('statusName', statusName); }; +const setColor = async profile => { + const color = await getColorById(profile.ColorId); + profile.setDataValue('color', color); +}; + exports.addProfileStatusName = async profile => { await setProfileStatusName(profile); }; @@ -66,12 +82,13 @@ exports.getUserWithProfileById = async id => { }, include: { model: Profile, - attributes: ['name', 'avatar', 'department', 'position', 'phone', 'phone_ext', 'status'], + attributes: ['name', 'initial', 'avatar', 'department', 'position', 'phone', 'phone_ext', 'status', 'ColorId'], }, }); if (user && user.Profile) { await setProfileStatusName(user.Profile); + await setColor(user.Profile); } return user;