fix: file upload and OAuth2 (#10)

This commit is contained in:
Divlo
2022-04-08 21:36:29 +02:00
committed by GitHub
parent 69c567cb66
commit a4c77fec50
30 changed files with 529 additions and 366 deletions

View File

@ -10,7 +10,6 @@ import { memberSchema } from '../../../../../models/Member.js'
import { userPublicWithoutSettingsSchema } from '../../../../../models/User.js'
import { channelSchema } from '../../../../../models/Channel.js'
import { uploadFile } from '../../../../../tools/utils/uploadFile.js'
import { MAXIMUM_FILE_SIZE } from '../../../../../tools/configurations/index.js'
const parametersSchema = Type.Object({
channelId: channelSchema.id
@ -94,8 +93,7 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async (
const file = await uploadFile({
fastify,
request,
folderInUploadsFolder: 'messages',
maximumFileSize: MAXIMUM_FILE_SIZE
folderInUploadsFolder: 'messages'
})
const message = await prisma.message.create({
data: {

View File

@ -7,10 +7,6 @@ import { fastifyErrors } from '../../../../models/utils.js'
import prisma from '../../../../tools/database/prisma.js'
import { uploadFile } from '../../../../tools/utils/uploadFile.js'
import { guildSchema } from '../../../../models/Guild.js'
import {
MAXIMUM_IMAGE_SIZE,
SUPPORTED_IMAGE_MIMETYPE
} from '../../../../tools/configurations/index.js'
import { channelSchema } from '../../../../models/Channel.js'
const parametersSchema = Type.Object({
@ -67,9 +63,7 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => {
const file = await uploadFile({
fastify,
request,
folderInUploadsFolder: 'guilds',
maximumFileSize: MAXIMUM_IMAGE_SIZE,
supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE
folderInUploadsFolder: 'guilds'
})
await prisma.guild.update({
where: { id: guildId },

View File

@ -2,7 +2,6 @@ import { FastifyPluginAsync } from 'fastify'
import { usersService } from './users/index.js'
import { guildsService } from './guilds/index.js'
import { uploadsService } from './uploads/index.js'
import { channelsService } from './channels/index.js'
import { messagesService } from './messages/index.js'
@ -10,6 +9,5 @@ export const services: FastifyPluginAsync = async (fastify) => {
await fastify.register(channelsService)
await fastify.register(guildsService)
await fastify.register(messagesService)
await fastify.register(uploadsService)
await fastify.register(usersService)
}

View File

@ -1,40 +0,0 @@
import path from 'node:path'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Static, Type } from '@sinclair/typebox'
import { fastifyErrors } from '../../../models/utils.js'
const parameters = Type.Object({
file: Type.String()
})
type Parameters = Static<typeof parameters>
export const getServiceSchema: FastifySchema = {
tags: ['uploads'] as string[],
params: parameters,
response: {
200: {
type: 'string',
format: 'binary'
},
400: fastifyErrors[400],
404: fastifyErrors[404],
500: fastifyErrors[500]
}
} as const
export const getGuildsUploadsService: FastifyPluginAsync = async (fastify) => {
await fastify.route<{
Params: Parameters
}>({
method: 'GET',
url: '/uploads/guilds/:file',
schema: getServiceSchema,
handler: async (request, reply) => {
const { file } = request.params
return await reply.sendFile(path.join('guilds', file))
}
})
}

View File

@ -1,11 +0,0 @@
import { FastifyPluginAsync } from 'fastify'
import { getGuildsUploadsService } from './guilds/get.js'
import { getMessagesUploadsService } from './messages/get.js'
import { getUsersUploadsService } from './users/get.js'
export const uploadsService: FastifyPluginAsync = async (fastify) => {
await fastify.register(getGuildsUploadsService)
await fastify.register(getMessagesUploadsService)
await fastify.register(getUsersUploadsService)
}

View File

@ -1,76 +0,0 @@
import path from 'node:path'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Static, Type } from '@sinclair/typebox'
import { fastifyErrors } from '../../../models/utils.js'
import authenticateUser from '../../../tools/plugins/authenticateUser.js'
import prisma from '../../../tools/database/prisma.js'
const parameters = Type.Object({
file: Type.String()
})
type Parameters = Static<typeof parameters>
export const getServiceSchema: FastifySchema = {
tags: ['uploads'] as string[],
security: [
{
bearerAuth: []
}
] as Array<{ [key: string]: [] }>,
params: parameters,
response: {
200: {
type: 'string',
format: 'binary'
},
400: fastifyErrors[400],
401: fastifyErrors[401],
403: fastifyErrors[403],
404: fastifyErrors[404],
500: fastifyErrors[500]
}
} as const
export const getMessagesUploadsService: FastifyPluginAsync = async (
fastify
) => {
await fastify.register(authenticateUser)
fastify.route<{
Params: Parameters
}>({
method: 'GET',
url: '/uploads/messages/:file',
schema: getServiceSchema,
handler: async (request, reply) => {
if (request.user == null) {
throw fastify.httpErrors.forbidden()
}
const { file } = request.params
const message = await prisma.message.findFirst({
where: { value: `/uploads/messages/${file}` },
include: {
member: {
select: { guildId: true }
}
}
})
if (message == null) {
throw fastify.httpErrors.notFound('Message not found')
}
const member = await prisma.member.findFirst({
where: {
guildId: message.member?.guildId,
userId: request.user.current.id
}
})
if (member == null) {
throw fastify.httpErrors.notFound('Member not found')
}
return await reply.sendFile(path.join('messages', file))
}
})
}

View File

@ -1,40 +0,0 @@
import path from 'node:path'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Static, Type } from '@sinclair/typebox'
import { fastifyErrors } from '../../../models/utils.js'
const parameters = Type.Object({
file: Type.String()
})
type Parameters = Static<typeof parameters>
export const getServiceSchema: FastifySchema = {
tags: ['uploads'] as string[],
params: parameters,
response: {
200: {
type: 'string',
format: 'binary'
},
400: fastifyErrors[400],
404: fastifyErrors[404],
500: fastifyErrors[500]
}
} as const
export const getUsersUploadsService: FastifyPluginAsync = async (fastify) => {
await fastify.route<{
Params: Parameters
}>({
method: 'GET',
url: '/uploads/users/:file',
schema: getServiceSchema,
handler: async (request, reply) => {
const { file } = request.params
return await reply.sendFile(path.join('users', file))
}
})
}

View File

@ -6,10 +6,6 @@ import authenticateUser from '../../../../tools/plugins/authenticateUser.js'
import { fastifyErrors } from '../../../../models/utils.js'
import prisma from '../../../../tools/database/prisma.js'
import { uploadFile } from '../../../../tools/utils/uploadFile.js'
import {
MAXIMUM_IMAGE_SIZE,
SUPPORTED_IMAGE_MIMETYPE
} from '../../../../tools/configurations/index.js'
const putServiceSchema: FastifySchema = {
description: 'Edit the current connected user logo',
@ -51,9 +47,7 @@ export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => {
const file = await uploadFile({
fastify,
request,
folderInUploadsFolder: 'users',
maximumFileSize: MAXIMUM_IMAGE_SIZE,
supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE
folderInUploadsFolder: 'users'
})
await prisma.user.update({
where: { id: request.user.current.id },

View File

@ -20,6 +20,12 @@ import { getCallbackGoogleOAuth2Service } from './oauth2/google/callback/get.js'
import { getSigninGitHubOAuth2Service } from './oauth2/github/signin/get.js'
import { getCallbackGitHubOAuth2Service } from './oauth2/github/callback/get.js'
import { deleteProviderService } from './oauth2/[provider]/delete.js'
import { getCallbackAddStrategyDiscordOAuth2Service } from './oauth2/discord/callback-add-strategy/get.js'
import { getAddStrategyDiscordOAuth2Service } from './oauth2/discord/add-strategy/get.js'
import { getAddStrategyGitHubOAuth2Service } from './oauth2/github/add-strategy/get.js'
import { getCallbackAddStrategyGitHubOAuth2Service } from './oauth2/github/callback-add-strategy/get.js'
import { getCallbackAddStrategyGoogleOAuth2Service } from './oauth2/google/callback-add-strategy/get.js'
import { getAddStrategyGoogleOAuth2Service } from './oauth2/google/add-strategy/get.js'
export const usersService: FastifyPluginAsync = async (fastify) => {
await fastify.register(postSignupUser)
@ -38,12 +44,18 @@ export const usersService: FastifyPluginAsync = async (fastify) => {
await fastify.register(getSigninDiscordOAuth2Service)
await fastify.register(getCallbackDiscordOAuth2Service)
await fastify.register(getCallbackAddStrategyDiscordOAuth2Service)
await fastify.register(getAddStrategyDiscordOAuth2Service)
await fastify.register(getSigninGoogleOAuth2Service)
await fastify.register(getCallbackGoogleOAuth2Service)
await fastify.register(getCallbackAddStrategyGoogleOAuth2Service)
await fastify.register(getAddStrategyGoogleOAuth2Service)
await fastify.register(getSigninGitHubOAuth2Service)
await fastify.register(getCallbackGitHubOAuth2Service)
await fastify.register(getCallbackAddStrategyGitHubOAuth2Service)
await fastify.register(getAddStrategyGitHubOAuth2Service)
await fastify.register(deleteProviderService)
}

View File

@ -0,0 +1,53 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - add-strategy',
tags: ['users'] as string[],
security: [
{
bearerAuth: []
}
] as Array<{ [key: string]: [] }>,
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getAddStrategyDiscordOAuth2Service: FastifyPluginAsync = async (
fastify
) => {
await fastify.register(authenticateUser)
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/discord/add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
if (request.user == null) {
throw fastify.httpErrors.forbidden()
}
const { redirectURI } = request.query
const redirectCallback = `${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}`
const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${DISCORD_CLIENT_ID}&scope=identify&response_type=code&state=${request.user.accessToken}&redirect_uri=${redirectCallback}`
reply.statusCode = 200
return url
}
})
}

View File

@ -0,0 +1,56 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
import { getUserWithBearerToken } from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
code: Type.String(),
state: Type.String(),
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'Discord OAuth2 - callback-add-strategy',
tags: ['users'] as string[],
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getCallbackAddStrategyDiscordOAuth2Service: FastifyPluginAsync =
async (fastify) => {
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/discord/callback-add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
const { redirectURI, code, state: accessToken } = request.query
const userRequest = await getUserWithBearerToken(
`Bearer ${accessToken}`
)
const discordUser = await getDiscordUserData(
code,
`${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}`
)
const message = await discordStrategy.callbackAddStrategy(
{
name: discordUser.username,
id: discordUser.id
},
userRequest
)
return await reply.redirect(buildQueryURL(redirectURI, { message }))
}
})
}

View File

@ -0,0 +1,53 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - add-strategy',
tags: ['users'] as string[],
security: [
{
bearerAuth: []
}
] as Array<{ [key: string]: [] }>,
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getAddStrategyGitHubOAuth2Service: FastifyPluginAsync = async (
fastify
) => {
await fastify.register(authenticateUser)
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/github/add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
if (request.user == null) {
throw fastify.httpErrors.forbidden()
}
const { redirectURI } = request.query
const redirectCallback = `${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}`
const url = `${GITHUB_BASE_URL}/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}`
reply.statusCode = 200
return url
}
})
}

View File

@ -0,0 +1,56 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
import { getUserWithBearerToken } from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
code: Type.String(),
state: Type.String(),
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'GitHub OAuth2 - callback-add-strategy',
tags: ['users'] as string[],
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getCallbackAddStrategyGitHubOAuth2Service: FastifyPluginAsync =
async (fastify) => {
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/github/callback-add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
const { redirectURI, code, state: accessToken } = request.query
const userRequest = await getUserWithBearerToken(
`Bearer ${accessToken}`
)
const githubUser = await getGitHubUserData(
code,
`${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}`
)
const message = await githubStrategy.callbackAddStrategy(
{
name: githubUser.name,
id: githubUser.id
},
userRequest
)
return await reply.redirect(buildQueryURL(redirectURI, { message }))
}
})
}

View File

@ -0,0 +1,53 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js'
import authenticateUser from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - add-strategy',
tags: ['users'] as string[],
security: [
{
bearerAuth: []
}
] as Array<{ [key: string]: [] }>,
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getAddStrategyGoogleOAuth2Service: FastifyPluginAsync = async (
fastify
) => {
await fastify.register(authenticateUser)
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/google/add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
if (request.user == null) {
throw fastify.httpErrors.forbidden()
}
const { redirectURI } = request.query
const redirectCallback = `${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}`
const url = `${GOOGLE_BASE_URL}?client_id=${GOOGLE_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}&response_type=code&scope=profile&access_type=online`
reply.statusCode = 200
return url
}
})
}

View File

@ -0,0 +1,56 @@
import { Static, Type } from '@sinclair/typebox'
import { FastifyPluginAsync, FastifySchema } from 'fastify'
import { API_URL } from '../../../../../tools/configurations/index.js'
import { fastifyErrors } from '../../../../../models/utils.js'
import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js'
import { buildQueryURL } from '../../../../../tools/utils/buildQueryURL.js'
import { getUserWithBearerToken } from '../../../../../tools/plugins/authenticateUser.js'
const querySchema = Type.Object({
code: Type.String(),
state: Type.String(),
redirectURI: Type.String({ format: 'uri-reference' })
})
type QuerySchemaType = Static<typeof querySchema>
const getServiceSchema: FastifySchema = {
description: 'Google OAuth2 - callback-add-strategy',
tags: ['users'] as string[],
querystring: querySchema,
response: {
200: Type.String(),
400: fastifyErrors[400],
500: fastifyErrors[500]
}
} as const
export const getCallbackAddStrategyGoogleOAuth2Service: FastifyPluginAsync =
async (fastify) => {
await fastify.route<{
Querystring: QuerySchemaType
}>({
method: 'GET',
url: '/users/oauth2/google/callback-add-strategy',
schema: getServiceSchema,
handler: async (request, reply) => {
const { redirectURI, code, state: accessToken } = request.query
const userRequest = await getUserWithBearerToken(
`Bearer ${accessToken}`
)
const googleUser = await getGoogleUserData(
code,
`${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}`
)
const message = await googleStrategy.callbackAddStrategy(
{
name: googleUser.name,
id: googleUser.id
},
userRequest
)
return await reply.redirect(buildQueryURL(redirectURI, { message }))
}
})
}