diff --git a/src/index.ts b/src/index.ts index 8427111..80c01a1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,8 @@ import { application } from './application.js' import { HOST, PORT } from './tools/configurations/index.js' -const address = await application.listen(PORT, HOST) +const address = await application.listen({ + port: PORT, + host: HOST +}) console.log('\u001B[36m%s\u001B[0m', `🚀 Server listening at ${address}`) diff --git a/src/services/uploads/guilds/delete.ts b/src/services/uploads/guilds/delete.ts new file mode 100644 index 0000000..22b78dd --- /dev/null +++ b/src/services/uploads/guilds/delete.ts @@ -0,0 +1,48 @@ +import { FastifyPluginAsync, FastifySchema } from 'fastify' +import { Type } from '@sinclair/typebox' + +import { fastifyErrors } from '../../../models/utils.js' +import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js' +import { + DeleteParameters, + deleteParameters, + deleteUploadedFile +} from '../../../tools/utils/deleteUploadedFile.js' + +export const deleteServiceSchema: FastifySchema = { + tags: ['guilds'] as string[], + security: [ + { + apiKeyAuth: [] + } + ] as Array<{ [key: string]: [] }>, + params: deleteParameters, + response: { + 200: Type.String(), + 400: fastifyErrors[400], + 404: fastifyErrors[404], + 500: fastifyErrors[500] + } +} as const + +export const deleteGuildsUploadsService: FastifyPluginAsync = async ( + fastify +) => { + await fastify.register(verifyAPIKey) + + await fastify.route<{ + Params: DeleteParameters + }>({ + method: 'DELETE', + url: '/uploads/guilds/:file', + schema: deleteServiceSchema, + handler: async (request, reply) => { + return await deleteUploadedFile({ + fastify, + request, + reply, + folder: 'guilds' + }) + } + }) +} diff --git a/src/services/uploads/index.ts b/src/services/uploads/index.ts index 281348b..27d34dc 100644 --- a/src/services/uploads/index.ts +++ b/src/services/uploads/index.ts @@ -1,19 +1,25 @@ import { FastifyPluginAsync } from 'fastify' +import { deleteGuildsUploadsService } from './guilds/delete.js' import { getGuildsUploadsService } from './guilds/get.js' import { postGuildsUploadsIconService } from './guilds/post.js' +import { deleteMessagesUploadsService } from './messages/delete.js' import { getMessagesUploadsService } from './messages/get.js' import { postMessagesUploadsService } from './messages/post.js' +import { deleteUsersUploadsService } from './users/delete.js' import { getUsersUploadsService } from './users/get.js' import { postUsersUploadsLogoService } from './users/post.js' export const uploadsService: FastifyPluginAsync = async (fastify) => { + await fastify.register(deleteGuildsUploadsService) await fastify.register(getGuildsUploadsService) await fastify.register(postGuildsUploadsIconService) + await fastify.register(deleteMessagesUploadsService) await fastify.register(getMessagesUploadsService) await fastify.register(postMessagesUploadsService) + await fastify.register(deleteUsersUploadsService) await fastify.register(getUsersUploadsService) await fastify.register(postUsersUploadsLogoService) } diff --git a/src/services/uploads/messages/delete.ts b/src/services/uploads/messages/delete.ts new file mode 100644 index 0000000..3e64271 --- /dev/null +++ b/src/services/uploads/messages/delete.ts @@ -0,0 +1,48 @@ +import { FastifyPluginAsync, FastifySchema } from 'fastify' +import { Type } from '@sinclair/typebox' + +import { fastifyErrors } from '../../../models/utils.js' +import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js' +import { + DeleteParameters, + deleteParameters, + deleteUploadedFile +} from '../../../tools/utils/deleteUploadedFile.js' + +export const deleteServiceSchema: FastifySchema = { + tags: ['messages'] as string[], + security: [ + { + apiKeyAuth: [] + } + ] as Array<{ [key: string]: [] }>, + params: deleteParameters, + response: { + 200: Type.String(), + 400: fastifyErrors[400], + 404: fastifyErrors[404], + 500: fastifyErrors[500] + } +} as const + +export const deleteMessagesUploadsService: FastifyPluginAsync = async ( + fastify +) => { + await fastify.register(verifyAPIKey) + + await fastify.route<{ + Params: DeleteParameters + }>({ + method: 'DELETE', + url: '/uploads/messages/:file', + schema: deleteServiceSchema, + handler: async (request, reply) => { + return await deleteUploadedFile({ + fastify, + request, + reply, + folder: 'messages' + }) + } + }) +} diff --git a/src/services/uploads/users/delete.ts b/src/services/uploads/users/delete.ts new file mode 100644 index 0000000..1adc366 --- /dev/null +++ b/src/services/uploads/users/delete.ts @@ -0,0 +1,48 @@ +import { FastifyPluginAsync, FastifySchema } from 'fastify' +import { Type } from '@sinclair/typebox' + +import { fastifyErrors } from '../../../models/utils.js' +import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js' +import { + DeleteParameters, + deleteParameters, + deleteUploadedFile +} from '../../../tools/utils/deleteUploadedFile.js' + +export const deleteServiceSchema: FastifySchema = { + tags: ['users'] as string[], + security: [ + { + apiKeyAuth: [] + } + ] as Array<{ [key: string]: [] }>, + params: deleteParameters, + response: { + 200: Type.String(), + 400: fastifyErrors[400], + 404: fastifyErrors[404], + 500: fastifyErrors[500] + } +} as const + +export const deleteUsersUploadsService: FastifyPluginAsync = async ( + fastify +) => { + await fastify.register(verifyAPIKey) + + await fastify.route<{ + Params: DeleteParameters + }>({ + method: 'DELETE', + url: '/uploads/users/:file', + schema: deleteServiceSchema, + handler: async (request, reply) => { + return await deleteUploadedFile({ + fastify, + request, + reply, + folder: 'users' + }) + } + }) +} diff --git a/src/tools/utils/deleteUploadedFile.ts b/src/tools/utils/deleteUploadedFile.ts new file mode 100644 index 0000000..6fff882 --- /dev/null +++ b/src/tools/utils/deleteUploadedFile.ts @@ -0,0 +1,51 @@ +import { IncomingMessage, Server, ServerResponse } from 'node:http' +import { fileURLToPath } from 'node:url' +import fs from 'node:fs' + +import { Static, Type } from '@sinclair/typebox' +import { + FastifyInstance, + FastifyLoggerInstance, + FastifyReply, + FastifyRequest, + FastifyTypeProviderDefault +} from 'fastify' + +import { isExistingFile } from './isExistingFile.js' + +export const deleteParameters = Type.Object({ + file: Type.String() +}) + +export type DeleteParameters = Static + +export interface DeleteUploadedFileOptions { + folder: 'guilds' | 'messages' | 'users' + fastify: FastifyInstance< + Server, + IncomingMessage, + ServerResponse, + FastifyLoggerInstance, + FastifyTypeProviderDefault + > + request: FastifyRequest<{ Params: DeleteParameters }> + reply: FastifyReply +} + +export const deleteUploadedFile = async ( + options: DeleteUploadedFileOptions +): Promise => { + const { request, fastify, reply, folder } = options + if (request.apiKey == null) { + throw fastify.httpErrors.forbidden() + } + const { file } = request.params + const fileURL = new URL(`../../../uploads/${folder}/${file}`, import.meta.url) + const filePath = fileURLToPath(fileURL) + if (!(await isExistingFile(filePath))) { + throw fastify.httpErrors.notFound('File does not exist') + } + await fs.promises.rm(filePath, { force: true }) + reply.statusCode = 200 + return file +} diff --git a/src/tools/utils/isExistingFile.ts b/src/tools/utils/isExistingFile.ts new file mode 100644 index 0000000..1d4f2d9 --- /dev/null +++ b/src/tools/utils/isExistingFile.ts @@ -0,0 +1,10 @@ +import fs from 'node:fs' + +export const isExistingFile = async (path: string): Promise => { + try { + await fs.promises.access(path, fs.constants.F_OK) + return true + } catch { + return false + } +}