Skip to content

Commit

Permalink
Merge pull request #160 from boostcampwm-2024/feature/be/#158-registeโ€ฆ
Browse files Browse the repository at this point in the history
โ€ฆrUser

[BE][Feat] #159 : ํšŒ์›๊ฐ€์ž… api ๊ตฌํ˜„, [BE][Refactor] swagger ๋ฌธ์„œ tag ์ถ”๊ฐ€, jsdoc ์ถ”๊ฐ€
  • Loading branch information
happyhyep authored Nov 14, 2024
2 parents c2ab9cd + 1d6c50a commit d09b862
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 22 deletions.
28 changes: 26 additions & 2 deletions backend/src/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { loginUser } from '../services/authService.js';
import { loginUser, registerUser } from '../services/authService.js';

export const login = async (req, res) => {
/**
* @description ๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const loginController = async (req, res) => {
const { id, password } = req.body;

try {
Expand All @@ -18,3 +21,24 @@ export const login = async (req, res) => {
return res.status(500).json({ success: false, message: 'Server error occurred' });
}
};

/**
* @description ํšŒ์›๊ฐ€์ž… ์ปจํŠธ๋กค๋Ÿฌ
*/
export const registerUserController = async (req, res) => {
try {
const { id, name, password, email } = req.body;
const newUser = await registerUser(id, name, password, email);
return res.status(201).json({
success: true,
message: 'Login successfully',
data: newUser,
});
} catch (error) {
if (error.message === 'User ID already exists') {
return res.status(409).json({ error: 'User ID already exists' });
}
console.error('User registration error:', error);
res.status(500).json({ error: 'Server error' });
}
};
15 changes: 15 additions & 0 deletions backend/src/controllers/channelController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
getUserChannels,
} from '../services/channelService.js';

/**
* @description ์ฑ„๋„ ์ƒ์„ฑ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const createChannelController = async (req, res) => {
try {
const { name, host_id, guests } = req.body;
Expand All @@ -26,6 +29,9 @@ export const createChannelController = async (req, res) => {
}
};

/**
* @description ์ฑ„๋„์— ๊ฒŒ์ŠคํŠธ ์ถ”๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const addGuestController = async (req, res) => {
try {
const { channelId } = req.params;
Expand Down Expand Up @@ -53,6 +59,9 @@ export const addGuestController = async (req, res) => {
}
};

/**
* @description ์ฑ„๋„ ์ •๋ณด ์กฐํšŒ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const getChannelInfoController = async (req, res) => {
const { id } = req.params;

Expand All @@ -72,6 +81,9 @@ export const getChannelInfoController = async (req, res) => {
}
};

/**
* @description ์ฑ„๋„์— ํŠน์ • ๊ฒŒ์ŠคํŠธ ์ •๋ณด ์กฐํšŒ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const getChannelGuestInfoController = async (req, res) => {
const { channelId, guestId } = req.params;
try {
Expand All @@ -91,6 +103,9 @@ export const getChannelGuestInfoController = async (req, res) => {
}
};

/**
* @description ์‚ฌ์šฉ์ž์˜ ์ฑ„๋„ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ์ปจํŠธ๋กค๋Ÿฌ
*/
export const getUserChannelsController = async (req, res) => {
const { userId } = req.params;

Expand Down
17 changes: 0 additions & 17 deletions backend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import express from 'express';
import swaggerUi from 'swagger-ui-express';
import http from 'http';
import { specs } from '../swaggerConfig.js';
import { pool } from './db/db.js';
import { PORT } from './constants/constants.js';
import { initializeWebSocketServer } from './websocketServer.js';
import { authRouter } from './routes/authRouter.js';
Expand All @@ -16,22 +15,6 @@ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
app.use('/api/auth', authRouter);
app.use('/api/channel', channelRouter);

// TODO: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ˆ์‹œ
app.get('/guests', async (req, res) => {
try {
const result = await pool.query('SELECT * FROM guest');
res.json(result.rows);
} catch (err) {
console.error(err);
res.status(500).send('์„œ๋ฒ„ ์˜ค๋ฅ˜');
}
});

// TODO: ์˜ˆ์ œ ๋ผ์šฐํ„ฐ (์ถ”๊ฐ€ ์˜ˆ์ •์ธ ๋ผ์šฐํ„ฐ์˜ ์ฃผ์„์„ Swagger ์ฃผ์„ ํ˜•์‹์œผ๋กœ ๋ฌธ์„œํ™”)
app.get('/example', (req, res) => {
res.send('Hello World');
});

// HTTP ์„œ๋ฒ„ ์ƒ์„ฑ
const server = http.createServer(app);

Expand Down
40 changes: 40 additions & 0 deletions backend/src/repositories/userRepository.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
import { pool } from '../db/db.js';

/**
* @description ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ƒ์„ฑ
* @param {string} id - ์‚ฌ์šฉ์ž id
* @returns {object} id๋กœ ์ฐพ์€ ์‚ฌ์šฉ์ž ์ •๋ณด
*/
export const findUserById = async id => {
const result = await pool.query('SELECT * FROM "main"."user" WHERE id = $1', [id]);
return result.rows[0];
};

/**
* @description ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ์—ฌ๋ถ€ ํ™•์ธ
* @param {string} id - ์‚ฌ์šฉ์ž ID
* @returns {boolean} ์ค‘๋ณต ์—ฌ๋ถ€
*/
export const isUserIdDuplicate = async id => {
const query = `
SELECT 1 FROM "main"."user"
WHERE id = $1;
`;
const result = await pool.query(query, [id]);

return result.rows.length > 0;
};

/**
* @description ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ƒ์„ฑ
* @param {string} id - ์‚ฌ์šฉ์ž ID
* @param {string} name - ์‚ฌ์šฉ์ž ์ด๋ฆ„
* @param {string} password - ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
* @param {string} email - ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ
* @returns {object} ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด
*/
export const createUserInDB = async (id, name, password, email) => {
const query = `
INSERT INTO "main"."user" (id, name, password, email)
VALUES ($1, $2, $3, $4)
RETURNING id, name, email;
`;
const values = [id, name, password, email];
const result = await pool.query(query, values);

return result.rows[0];
};
42 changes: 40 additions & 2 deletions backend/src/routes/authRouter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express';
import { body } from 'express-validator';
import { login } from '../controllers/authController.js';
import { loginController, registerUserController } from '../controllers/authController.js';
import { validationMiddleware } from '../middleware/validationMiddleware.js';

export const authRouter = express.Router();
Expand All @@ -11,6 +11,7 @@ export const authRouter = express.Router();
* post:
* summary: ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ API
* description: ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ID์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ†ตํ•ด ์ธ์ฆ ํ›„ ํ† ํฐ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* tags: [Auth]
* requestBody:
* required: true
* description: ๋กœ๊ทธ์ธ์„ ์œ„ํ•œ ID์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ body
Expand Down Expand Up @@ -41,5 +42,42 @@ authRouter.post(
.withMessage('Password must be at least 6 characters long'),
],
validationMiddleware,
login,
loginController,
);

/**
* @swagger
* /user/register:
* post:
* summary: "ํšŒ์›๊ฐ€์ž… API"
* description: "์‚ฌ์šฉ์ž๊ฐ€ ํšŒ์›๊ฐ€์ž…์„ ํ†ตํ•ด ๊ณ„์ •์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค."
* tags: [Auth]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RegisterRequest'
* responses:
* "201":
* description: "ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต"
* "400":
* description: "์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์‹คํŒจ"
* "409":
* description: "์ค‘๋ณต๋œ ์‚ฌ์šฉ์ž ID"
* "500":
* description: "์„œ๋ฒ„ ์˜ค๋ฅ˜"
*/
authRouter.post(
'/register',
[
body('id').isLength({ min: 4 }).withMessage('User ID must be at least 4 characters long'),
body('name').notEmpty().withMessage('Name is required'),
body('password')
.isLength({ min: 6 })
.withMessage('Password must be at least 6 characters long'),
body('email').isEmail().withMessage('Valid email is required'),
],
validationMiddleware,
registerUserController,
);
5 changes: 5 additions & 0 deletions backend/src/routes/channelRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const channelRouter = express.Router();
* post:
* summary: '์ƒˆ๋กœ์šด ์ฑ„๋„ ์ƒ์„ฑ API'
* description: '์ฑ„๋„ ์ด๋ฆ„, ์ฃผ์ธ, ๊ฒŒ์ŠคํŠธ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜์—ฌ ์ฑ„๋„์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.'
* tags: [Channel]
* requestBody:
* required: true
* content:
Expand Down Expand Up @@ -51,6 +52,7 @@ channelRouter.post(
* post:
* summary: '๊ฒŒ์ŠคํŠธ ์ถ”๊ฐ€ API'
* description: 'ํŠน์ • ์ฑ„๋„์— ๊ฒŒ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.'
* tags: [Channel]
* parameters:
* - name: 'channelId'
* in: 'path'
Expand Down Expand Up @@ -87,6 +89,7 @@ channelRouter.post(
* get:
* summary: '์ฑ„๋„ ์ •๋ณด ์กฐํšŒ API'
* description: 'ํŠน์ • ์ฑ„๋„์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.'
* tags: [Channel]
* parameters:
* - name: 'id'
* in: 'path'
Expand Down Expand Up @@ -117,6 +120,7 @@ channelRouter.get(
* get:
* summary: '๊ฒŒ์ŠคํŠธ ์ •๋ณด ์กฐํšŒ API'
* description: 'ํŠน์ • ์ฑ„๋„ ๋‚ด์˜ ๊ฒŒ์ŠคํŠธ ์ •๋ณด๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.'
* tags: [Channel]
* parameters:
* - name: 'channelId'
* in: 'path'
Expand Down Expand Up @@ -155,6 +159,7 @@ channelRouter.get(
* get:
* summary: '์‚ฌ์šฉ์ž๊ฐ€ host์ธ ์ฑ„๋„ ๋ชฉ๋ก ๋ฐ˜ํ™˜ API'
* description: 'userId๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ host์ธ ์ฑ„๋„ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.'
* tags: [Channel]
* parameters:
* - in: path
* name: userId
Expand Down
28 changes: 27 additions & 1 deletion backend/src/services/authService.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { findUserById } from '../repositories/userRepository.js';
import { createUserInDB, findUserById, isUserIdDuplicate } from '../repositories/userRepository.js';

/**
* @description ๋กœ๊ทธ์ธ ์„œ๋น„์Šค
* @param {string} name - ์‚ฌ์šฉ์ž ์ด๋ฆ„
* @param {string} password - ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
* @returns {object} ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ๊ณผ id
*/
export const loginUser = async (id, password) => {
const user = await findUserById(id);
if (!user) {
Expand All @@ -17,3 +23,23 @@ export const loginUser = async (id, password) => {
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
return { token, userId: user.id };
};

/**
* @description ํšŒ์›๊ฐ€์ž… ์„œ๋น„์Šค
* @param {string} id - ์‚ฌ์šฉ์ž ID
* @param {string} name - ์‚ฌ์šฉ์ž ์ด๋ฆ„
* @param {string} password - ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ
* @param {string} email - ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ
* @returns {object} ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด
*/
export const registerUser = async (id, name, password, email) => {
const isDuplicate = await isUserIdDuplicate(id);
if (isDuplicate) {
throw new Error('User ID already exists');
}

const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await createUserInDB(id, name, hashedPassword, email);

return newUser;
};
32 changes: 32 additions & 0 deletions backend/src/services/channelService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import {
} from '../repositories/channelRepository.js';
import { addGuestToChannel } from '../repositories/guestRepository.js';

/**
* @description ์ƒˆ๋กœ์šด ์ฑ„๋„์„ ์ƒ์„ฑํ•˜๊ณ  ๊ฒŒ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€
* @param {string} name - ์ฑ„๋„ ์ด๋ฆ„
* @param {string} host_id - ์ฑ„๋„ ํ˜ธ์ŠคํŠธ ID
* @param {Array} guests - ์ถ”๊ฐ€ํ•  ๊ฒŒ์ŠคํŠธ ๋ชฉ๋ก
* @returns {object} ์ƒ์„ฑ๋œ ์ฑ„๋„ ์ •๋ณด
*/
export const createChannelService = async (name, host_id, guests) => {
const channel = await createChannelInDB(name, host_id);

Expand All @@ -28,6 +35,12 @@ export const createChannelService = async (name, host_id, guests) => {
return channel;
};

/**
* @description ํŠน์ • ์ฑ„๋„์— ๊ฒŒ์ŠคํŠธ ์ถ”๊ฐ€
* @param {string} channelId - ์ฑ„๋„ ID
* @param {Array} guests - ์ถ”๊ฐ€ํ•  ๊ฒŒ์ŠคํŠธ ๋ชฉ๋ก
* @returns {object|null} ์ฑ„๋„ ์ •๋ณด (์ฑ„๋„์ด ์—†์„ ๊ฒฝ์šฐ null ๋ฐ˜ํ™˜)
*/
export const addGuestService = async (channelId, guests) => {
const channel = await getChannelInfoByIdInDB(channelId);
if (!channel) return null;
Expand All @@ -50,6 +63,12 @@ export const addGuestService = async (channelId, guests) => {
return channel;
};

/**
* @description ์ฑ„๋„ ID๋กœ ์ฑ„๋„๊ณผ ๊ฒŒ์ŠคํŠธ ์ •๋ณด๋ฅผ ์กฐํšŒ
* @param {string} id - ์ฑ„๋„ ID
* @returns {object} ์ฑ„๋„๊ณผ ๊ฒŒ์ŠคํŠธ ์ •๋ณด
* @throws {Error} ์ฑ„๋„ ์กฐํšŒ ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
*/
export const getChannelByIdService = async id => {
try {
return await getChannelWithGuestsByIdFromDB(id);
Expand All @@ -59,6 +78,13 @@ export const getChannelByIdService = async id => {
}
};

/**
* @description ์ฑ„๋„ ID์™€ ๊ฒŒ์ŠคํŠธ ID๋กœ ํŠน์ • ๊ฒŒ์ŠคํŠธ ์ •๋ณด๋ฅผ ์กฐํšŒ
* @param {string} channelId - ์ฑ„๋„ ID
* @param {string} guestId - ๊ฒŒ์ŠคํŠธ ID
* @returns {object|null} ์ฑ„๋„๊ณผ ํ•ด๋‹น ๊ฒŒ์ŠคํŠธ ์ •๋ณด (์ฑ„๋„ ๋˜๋Š” ๊ฒŒ์ŠคํŠธ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ null ๋ฐ˜ํ™˜)
* @throws {Error} ์กฐํšŒ ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
*/
export const getChannelGuestInfoService = async (channelId, guestId) => {
try {
return await getGuestByChannelAndGuestIdFromDB(channelId, guestId);
Expand All @@ -68,6 +94,12 @@ export const getChannelGuestInfoService = async (channelId, guestId) => {
}
};

/**
* @description ์‚ฌ์šฉ์ž ID๋กœ ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ŠคํŠธ์ธ ์ฑ„๋„ ๋ชฉ๋ก์„ ์กฐํšŒ
* @param {string} userId - ์‚ฌ์šฉ์ž ID
* @returns {Array} ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ŠคํŠธ์ธ ์ฑ„๋„ ๋ชฉ๋ก
* @throws {Error} ์ฑ„๋„ ์กฐํšŒ ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
*/
export const getUserChannels = async userId => {
try {
return await getChannelsByUserIdFromDB(userId);
Expand Down
Loading

0 comments on commit d09b862

Please sign in to comment.