-
Notifications
You must be signed in to change notification settings - Fork 275
add task solution #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
add task solution #100
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { ApiError } from '../exeptions/api.error.js'; | ||
| import { Message } from '../models/message.js'; | ||
| import { messageService } from '../services/message.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
|
|
||
| const getAllMessages = async (req, res, next) => { | ||
| try { | ||
| const { roomId } = req.params; | ||
|
|
||
| const allMessages = await Message.findAll({ where: { roomId } }); | ||
|
|
||
| res.send(allMessages); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const createMessage = async (req, res, next) => { | ||
| try { | ||
| const { text } = req.body; | ||
| const { roomId } = req.params; | ||
|
|
||
| if (!text) { | ||
| throw ApiError.badRequest('Enter the message'); | ||
| } | ||
|
|
||
| const user = await userService.getUser(req); | ||
|
|
||
| await messageService.createMessage(user, text, roomId); | ||
|
|
||
| res.status(201).send(text); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| export const messageController = { | ||
| getAllMessages, | ||
| createMessage, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { ApiError } from '../exeptions/api.error.js'; | ||
| import { jwtService } from '../services/jwt.service.js'; | ||
| import { tokenService } from '../services/token.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
| import { userController } from './user.controller.js'; | ||
|
|
||
| const refresh = async (req, res, next) => { | ||
| try { | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| const userData = await jwtService.verifyRefresh(refreshToken); | ||
| const token = await tokenService.getByToken(refreshToken); | ||
|
|
||
| if (!userData || !token) { | ||
| throw ApiError.unauthorized(); | ||
| } | ||
|
|
||
| const user = await userService.findByUserName(userData.username); | ||
|
|
||
| userController.generateToken(res, user); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| export const refreshController = { | ||
| refresh, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| import { ApiError } from '../exeptions/api.error.js'; | ||
| import { Room } from '../models/room.js'; | ||
| import { roomService } from '../services/room.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
|
|
||
| const getAllRooms = async (req, res, next) => { | ||
| try { | ||
| const rooms = await Room.findAll(); | ||
|
|
||
| res.send(rooms); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const getRoomById = async (req, res, next) => { | ||
| try { | ||
| const { roomId } = req.params; | ||
|
|
||
| const room = await roomService.getRoomById(roomId); | ||
|
|
||
| if (!room) { | ||
| throw ApiError.notFound(); | ||
| } | ||
|
|
||
| res.send(room); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const createRoom = async (req, res, next) => { | ||
| try { | ||
| const { title, participants } = req.body; | ||
|
|
||
| if (!title) { | ||
| throw ApiError.badRequest('Enter the title'); | ||
| } | ||
|
|
||
| const user = await userService.getUser(req); | ||
|
|
||
| await roomService.createRoom(user, title, participants); | ||
|
|
||
| res.status(201).send({ message: 'Room created' }); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const updateRoom = async (req, res, next) => { | ||
| try { | ||
| const { roomId } = req.params; | ||
| const { title, participants } = req.body; | ||
|
|
||
| const room = await roomService.getRoomById(roomId); | ||
|
|
||
| if (!room) { | ||
| throw ApiError.notFound(); | ||
| } | ||
|
|
||
| if (!Array.isArray(participants)) { | ||
| throw ApiError.badRequest('Participants shoude be an array'); | ||
| } | ||
|
|
||
| const user = await userService.getUser(req); | ||
|
|
||
| await roomService.updateRoom(user, room, title, participants); | ||
|
|
||
| const updatedRoom = await roomService.getRoomById(roomId); | ||
|
|
||
| res.send(updatedRoom); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const deleteRoom = async (req, res, next) => { | ||
| try { | ||
| const { roomId } = req.params; | ||
|
|
||
| const room = await roomService.getRoomById(roomId); | ||
|
|
||
| if (!room) { | ||
| throw ApiError.notFound(); | ||
| } | ||
|
|
||
| await roomService.deleteRoom(roomId); | ||
|
|
||
| res.status(204).send({ message: 'Room deleted' }); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| export const roomController = { | ||
| createRoom, | ||
| updateRoom, | ||
| deleteRoom, | ||
| getAllRooms, | ||
| getRoomById, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { ApiError } from '../exeptions/api.error.js'; | ||
| import { jwtService } from '../services/jwt.service.js'; | ||
| import { tokenService } from '../services/token.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
|
|
||
| const createUser = async (req, res, next) => { | ||
| try { | ||
| const { username } = req.body; | ||
|
|
||
| if (!username) { | ||
| throw ApiError.badRequest('Enter the username'); | ||
| } | ||
|
|
||
| await userService.createUser(username); | ||
|
|
||
| const user = await userService.findByUserName(username); | ||
|
|
||
| await generateToken(res, user); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
|
|
||
| const generateToken = async (res, user) => { | ||
| const normalizedUser = userService.normalize(user); | ||
|
|
||
| const accessToken = jwtService.sign(normalizedUser); | ||
| const refreshAccessToken = jwtService.signRefresh(normalizedUser); | ||
|
|
||
| await tokenService.save(normalizedUser.id, refreshAccessToken); | ||
|
|
||
| res.cookie('refreshToken', refreshAccessToken, { | ||
| maxAge: 30 * 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
|
|
||
| res.send({ user: normalizedUser, accessToken }); | ||
| }; | ||
|
|
||
| export const userController = { | ||
| createUser, | ||
| generateToken, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| export class ApiError extends Error { | ||
| constructor({ message, status, errors = {} }) { | ||
| super(message); | ||
|
|
||
| this.status = status; | ||
| this.errors = errors; | ||
| } | ||
|
|
||
| static badRequest(message, errors) { | ||
| return new ApiError({ | ||
| message, | ||
| errors, | ||
| status: 400, | ||
| }); | ||
| } | ||
|
|
||
| static unauthorized(errors) { | ||
| return new ApiError({ | ||
| message: 'unauthorized user', | ||
| errors, | ||
| status: 401, | ||
| }); | ||
| } | ||
|
|
||
| static notFound(errors) { | ||
| return new ApiError({ | ||
| message: 'not found', | ||
| errors, | ||
| status: 404, | ||
| }); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,41 @@ | ||
| 'use strict'; | ||
|
|
||
| import 'dotenv/config'; | ||
| import express from 'express'; | ||
| import cors from 'cors'; | ||
| import { WebSocketServer } from 'ws'; | ||
| import { userRouter } from './routes/user.route.js'; | ||
| import { errorMiddleware } from './middlewares/errorMiddleware.js'; | ||
| import { roomsRouter } from './routes/rooms.route.js'; | ||
| import { messageRouter } from './routes/message.route.js'; | ||
| import { emmiter } from './utils/emmiter.js'; | ||
| import cookieParser from 'cookie-parser'; | ||
| import { refreshRouter } from './routes/refresh.route.js'; | ||
|
|
||
| const PORT = process.env.PORT || 3005; | ||
|
|
||
| const app = express(); | ||
|
|
||
| app.use(cors()); | ||
| app.use(express.json()); | ||
| app.use(cookieParser()); | ||
|
|
||
| app.use('/user', userRouter); | ||
| app.use('/rooms', roomsRouter); | ||
| app.use('/messages', messageRouter); | ||
| app.use('/refresh', refreshRouter); | ||
|
|
||
| app.use(errorMiddleware); | ||
|
|
||
| const server = app.listen(PORT, () => { | ||
| // eslint-disable-next-line no-console | ||
| console.log('server is running'); | ||
| }); | ||
|
|
||
| const wss = new WebSocketServer({ server }); | ||
|
|
||
| emmiter.on('message', (message) => { | ||
| for (const client of wss.clients) { | ||
| client.send(JSON.stringify(message)); | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { jwtService } from '../services/jwt.service.js'; | ||
|
|
||
| export const authMiddleware = (req, res, next) => { | ||
| const authorization = req.headers['authorization'] || ''; | ||
| const [, token] = authorization.split(' '); | ||
|
|
||
| if (!authorization || !token) { | ||
| res.sendStatus(401); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| const userData = jwtService.verify(token); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential issue: After verifying the token, the decoded user data ( |
||
|
|
||
| if (!userData) { | ||
| res.sendStatus(401); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| req.user = userData; | ||
|
|
||
| next(); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { ApiError } from '../exeptions/api.error.js'; | ||
|
|
||
| export const errorMiddleware = (error, req, res, next) => { | ||
| if (error instanceof ApiError) { | ||
| res.status(error.status).send({ | ||
| message: error.message, | ||
| errors: error.errors, | ||
| }); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| res.statusCode = 500; | ||
| res.send(error.message); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../utils/db.js'; | ||
| import { User } from './user.js'; | ||
| import { Room } from './room.js'; | ||
|
|
||
| export const Message = client.define( | ||
| 'message', | ||
| { | ||
| text: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| author: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| time: { | ||
| type: DataTypes.DATE, | ||
| defaultValue: DataTypes.NOW, | ||
| allowNull: false, | ||
| }, | ||
| }, | ||
| { | ||
| timestamps: false, | ||
| }, | ||
| ); | ||
|
|
||
| Message.belongsTo(User); | ||
| User.hasMany(Message); | ||
| Message.belongsTo(Room); | ||
| Room.hasMany(Message); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../utils/db.js'; | ||
| import { User } from './user.js'; | ||
|
|
||
| export const Room = client.define('room', { | ||
| title: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| unique: true, | ||
| }, | ||
| participants: { | ||
| type: DataTypes.ARRAY(DataTypes.STRING), | ||
| allowNull: false, | ||
| }, | ||
| }); | ||
|
|
||
| Room.belongsTo(User); | ||
| User.hasMany(Room); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { DataTypes } from 'sequelize'; | ||
| import { client } from '../utils/db.js'; | ||
| import { User } from './user.js'; | ||
|
|
||
| export const Token = client.define('token', { | ||
| refreshToken: { | ||
| type: DataTypes.STRING, | ||
| allowNull: false, | ||
| }, | ||
| }); | ||
|
|
||
| Token.belongsTo(User); | ||
| User.hasOne(Token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue: The message router is used without a base path (
app.use(messageRouter);). This can cause route conflicts and ambiguity. Please specify a base path, e.g.,app.use('/messages', messageRouter);.