feat: migrate from express to fastify

This commit is contained in:
Divlo
2021-10-24 04:18:18 +02:00
parent 714cc643ba
commit b77e602358
281 changed files with 19768 additions and 22895 deletions

View File

@ -1,33 +0,0 @@
/channels/{channelId}/messages:
get:
security:
- bearerAuth: []
tags:
- 'messages'
summary: 'GET all the messages of a channel'
parameters:
- name: 'channelId'
in: 'path'
required: true
allOf:
- $ref: '#/definitions/PaginateModelParameters'
responses:
allOf:
- $ref: '#/definitions/UnauthorizedError'
- $ref: '#/definitions/ForbiddenError'
- $ref: '#/definitions/BadRequestError'
- $ref: '#/definitions/NotFoundError'
- '200':
content:
application/json:
schema:
allOf:
- $ref: '#/definitions/PaginateModel'
type: 'object'
properties:
rows:
type: 'array'
items:
allOf:
- $ref: '#/definitions/Message'
- $ref: '#/definitions/User'

View File

@ -1,44 +0,0 @@
/channels/{channelId}/messages:
post:
security:
- bearerAuth: []
tags:
- 'messages'
summary: 'Create a new message'
parameters:
- name: 'channelId'
in: 'path'
required: true
requestBody:
content:
multipart/form-data:
schema:
type: 'object'
properties:
value:
type: 'string'
minLength: 1
maxLength: 50_000
type:
allOf:
- $ref: '#/definitions/MessageType'
file:
type: 'string'
format: 'binary'
responses:
allOf:
- $ref: '#/definitions/UnauthorizedError'
- $ref: '#/definitions/ForbiddenError'
- $ref: '#/definitions/BadRequestError'
- $ref: '#/definitions/NotFoundError'
- $ref: '#/definitions/PayloadTooLargeError'
- '201':
content:
application/json:
schema:
type: 'object'
properties:
message:
allOf:
- $ref: '#/definitions/Message'
- $ref: '#/definitions/User'

View File

@ -1,23 +0,0 @@
import request from 'supertest'
import application from '../../../../../application'
import { createMessages } from '../../../../messages/__test__/utils/createMessages'
describe('GET /channels/:channelId/messages', () => {
it('should get all the messages of the channel', async () => {
const messages = ['Hello world!', 'some random message']
const result = await createMessages(messages)
const response = await request(application)
.get(`/channels/${result.channelId}/messages`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send()
.expect(200)
expect(response.body.hasMore).toBeFalsy()
expect(response.body.totalItems).toEqual(messages.length)
expect(response.body.rows[0].value).toEqual(messages[0])
expect(response.body.rows[1].value).toEqual(messages[1])
expect(response.body.rows[1].user).not.toBeNull()
expect(response.body.rows[1].user.id).toEqual(result.user.id)
expect(response.body.rows[1].user.password).not.toBeDefined()
})
})

View File

@ -1,69 +0,0 @@
import request from 'supertest'
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
import { formatErrors } from '../../../../../__test__/utils/formatErrors'
import application from '../../../../../application'
import { createChannels } from '../../../__test__/utils/createChannel'
const channel1 = { name: 'general1', description: 'testing' }
describe('POST /channels/:channelId/messages', () => {
it('succeeds and create the message', async () => {
const value = 'my awesome message'
const result = await createChannels([channel1])
expect(result.channels.length).toEqual(1)
const channel = result.channels[0]
const response = await request(application)
.post(`/channels/${channel.id as number}/messages`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send({ value, type: 'text' })
.expect(201)
expect(response.body.message).not.toBeNull()
expect(response.body.message.value).toEqual(value)
expect(response.body.message.type).toEqual('text')
expect(response.body.message.user).not.toBeNull()
expect(response.body.message.user.id).toEqual(result.user.id)
})
it('fails with empty message', async () => {
const result = await createChannels([channel1])
expect(result.channels.length).toEqual(1)
const channel = result.channels[0]
const response1 = await request(application)
.post(`/channels/${channel.id as number}/messages`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send({ type: 'text' })
.expect(400)
const response2 = await request(application)
.post(`/channels/${channel.id as number}/messages`)
.set('Authorization', `${result.user.type} ${result.user.accessToken}`)
.send({ type: 'file' })
.expect(400)
expect(response1.body.errors.length).toEqual(1)
expect(response2.body.errors.length).toEqual(1)
})
it("fails if the channel doesn't exist", async () => {
const userToken = await authenticateUserTest()
const response = await request(application)
.post('/channels/2/messages')
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
.send({ type: 'text', value: 'awesome' })
.expect(404)
expect(response.body.errors.length).toEqual(1)
})
it('fails if the user is not in the guild with this channel', async () => {
const result = await createChannels([channel1])
const channel = result.channels[0]
const userToken = await authenticateUserTest()
const response = await request(application)
.post(`/channels/${channel.id as number}/messages`)
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
.send({ value: 'some random message', type: 'text' })
.expect(404)
const errors = formatErrors(response.body.errors)
expect(errors.length).toEqual(1)
expect(errors).toEqual(expect.arrayContaining(['Not Found']))
})
})

View File

@ -1,60 +0,0 @@
import { Request, Response, Router } from 'express'
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
import Channel from '../../../../models/Channel'
import Member from '../../../../models/Member'
import Message from '../../../../models/Message'
import { paginateModel } from '../../../../tools/database/paginateModel'
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
import User from '../../../../models/User'
export const getMessagesRouter = Router()
getMessagesRouter.get(
'/channels/:channelId/messages',
authenticateUser,
async (req: Request, res: Response) => {
if (req.user == null) {
throw new ForbiddenError()
}
const { itemsPerPage, page } = req.query as {
itemsPerPage: string
page: string
}
const { channelId } = req.params as { channelId: string }
const user = req.user.current
const channel = await Channel.findOne({ where: { id: channelId } })
if (channel == null) {
throw new NotFoundError()
}
const member = await Member.findOne({
where: { userId: user.id, guildId: channel.guildId }
})
if (member == null) {
throw new NotFoundError()
}
member.lastVisitedChannelId = channel.id
await member.save()
const result = await paginateModel({
Model: Message,
queryOptions: { itemsPerPage, page },
findOptions: {
order: [['createdAt', 'DESC']],
include: [{ model: Member, include: [User] }],
where: {
channelId: channel.id
}
}
})
return res.status(200).json({
hasMore: result.hasMore,
totalItems: result.totalItems,
itemsPerPage: result.itemsPerPage,
page: result.page,
rows: result.rows.reverse().map((row: any) => {
return { ...row.toJSON(), user: row.member.user.toJSON() }
})
})
}
)

View File

@ -1,9 +0,0 @@
import { Router } from 'express'
import { postMessagesRouter } from './post'
import { getMessagesRouter } from './get'
export const messagesChannelsRouter = Router()
messagesChannelsRouter.use('/', postMessagesRouter)
messagesChannelsRouter.use('/', getMessagesRouter)

View File

@ -1,122 +0,0 @@
import { Request, Response, Router } from 'express'
import { body } from 'express-validator'
import fileUpload from 'express-fileupload'
import { v4 as uuidv4 } from 'uuid'
import path from 'path'
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
import { validateRequest } from '../../../../tools/middlewares/validateRequest'
import Channel from '../../../../models/Channel'
import Member from '../../../../models/Member'
import Message, { MessageType, messageTypes } from '../../../../models/Message'
import {
commonErrorsMessages,
fileUploadOptions,
messagesFilePath,
tempPath
} from '../../../../tools/configurations/constants'
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
import { onlyPossibleValuesValidation } from '../../../../tools/validations/onlyPossibleValuesValidation'
import { deleteAllFilesInDirectory } from '../../../../tools/utils/deleteFiles'
import { PayloadTooLargeError } from '../../../../tools/errors/PayloadTooLargeError'
import { BadRequestError } from '../../../../tools/errors/BadRequestError'
import { emitToMembers } from '../../../../tools/socket/emitEvents'
export const errorsMessages = {
type: {
shouldNotBeEmpty: 'Type should not be empty'
}
}
export const postMessagesRouter = Router()
postMessagesRouter.post(
'/channels/:channelId/messages',
authenticateUser,
fileUpload(fileUploadOptions),
[
body('value')
.optional({ nullable: true })
.trim()
.escape()
.isLength({ min: 1, max: 50_000 })
.withMessage(
commonErrorsMessages.charactersLength('value', { min: 1, max: 50_000 })
),
body('type')
.notEmpty()
.withMessage(errorsMessages.type.shouldNotBeEmpty)
.trim()
.isString()
.custom(async (type: MessageType) => {
return await onlyPossibleValuesValidation(messageTypes, 'type', type)
})
],
validateRequest,
async (req: Request, res: Response) => {
if (req.user == null) {
throw new ForbiddenError()
}
const user = req.user.current
const { value, type } = req.body as {
value?: string
type: MessageType
}
const file = req.files?.file
const { channelId } = req.params as { channelId: string }
const channel = await Channel.findOne({
where: { id: channelId, type: 'text' }
})
if (channel == null) {
throw new NotFoundError()
}
const member = await Member.findOne({
where: { userId: user.id, guildId: channel.guildId }
})
if (member == null) {
throw new NotFoundError()
}
if (
(type === 'file' && file == null) ||
(type === 'text' && value == null)
) {
throw new BadRequestError("You can't send an empty message")
}
let filename: string | null = null
let mimetype = 'text/plain'
if (
value == null &&
type === 'file' &&
file != null &&
!Array.isArray(file)
) {
if (file.truncated) {
await deleteAllFilesInDirectory(tempPath)
throw new PayloadTooLargeError(
commonErrorsMessages.tooLargeFile('file')
)
}
mimetype = file.mimetype
const splitedMimetype = mimetype.split('/')
const fileExtension = splitedMimetype[1]
filename = `${uuidv4()}.${fileExtension}`
await file.mv(path.join(messagesFilePath.filePath, filename))
await deleteAllFilesInDirectory(tempPath)
}
const messageCreated = await Message.create({
value: filename != null ? `${messagesFilePath.name}/${filename}` : value,
type,
mimetype,
memberId: member.id,
channelId: channel.id
})
const message = { ...messageCreated.toJSON(), user: req.user.current }
await emitToMembers({
event: 'messages',
guildId: member.guildId,
payload: { action: 'create', item: message }
})
return res.status(201).json({ message })
}
)