Compare commits

..

No commits in common. "develop" and "v1.1.4" have entirely different histories.

40 changed files with 3733 additions and 3823 deletions

View File

@ -1,7 +0,0 @@
.*
!.npmrc
!.swcrc
build
coverage
node_modules
Dockerfile

View File

@ -1,4 +1,3 @@
COMPOSE_PROJECT_NAME=thream-file-uploads-api
API_KEY=apiKeySecret API_KEY=apiKeySecret
API_URL=http://localhost:8000 API_URL=http://localhost:8000
HOST=0.0.0.0 HOST=0.0.0.0

View File

@ -1,10 +1,12 @@
{ {
"extends": ["conventions", "prettier"], "extends": ["conventions", "prettier"],
"plugins": ["prettier", "import", "unicorn"], "plugins": ["prettier", "import", "unicorn"],
"parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"project": "./tsconfig.json" "project": "./tsconfig.json"
}, },
"env": {
"node": true
},
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"import/extensions": ["error", "always"], "import/extensions": ["error", "always"],

View File

@ -1,8 +1,8 @@
--- ---
name: "🐛 Bug Report" name: '🐛 Bug Report'
about: "Report an unexpected problem or unintended behavior." about: 'Report an unexpected problem or unintended behavior.'
title: "[Bug]" title: '[Bug]'
labels: "bug" labels: 'bug'
--- ---
<!-- <!--

View File

@ -1,8 +1,8 @@
--- ---
name: "📜 Documentation" name: '📜 Documentation'
about: "Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...)." about: 'Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...).'
title: "[Documentation]" title: '[Documentation]'
labels: "documentation" labels: 'documentation'
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: "✨ Feature Request" name: '✨ Feature Request'
about: "Suggest a new feature idea." about: 'Suggest a new feature idea.'
title: "[Feature]" title: '[Feature]'
labels: "feature request" labels: 'feature request'
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: "🔧 Improvement" name: '🔧 Improvement'
about: "Improve structure/format/performance/refactor/tests of the code." about: 'Improve structure/format/performance/refactor/tests of the code.'
title: "[Improvement]" title: '[Improvement]'
labels: "improvement" labels: 'improvement'
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: "🙋 Question" name: '🙋 Question'
about: "Further information is requested." about: 'Further information is requested.'
title: "[Question]" title: '[Question]'
labels: "question" labels: 'question'
--- ---
### Question ### Question

View File

@ -1,4 +1,4 @@
name: "Build" name: 'Build'
on: on:
push: push:
@ -8,20 +8,20 @@ on:
jobs: jobs:
build: build:
runs-on: "ubuntu-latest" runs-on: 'ubuntu-latest'
steps: steps:
- uses: "actions/checkout@v4.0.0" - uses: 'actions/checkout@v3.5.2'
- name: "Setup Node.js" - name: 'Setup Node.js'
uses: "actions/setup-node@v3.8.1" uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: "20.x" node-version: '18.x'
cache: "npm" cache: 'npm'
- name: "Install dependencies" - name: 'Install dependencies'
run: "npm clean-install" run: 'npm clean-install'
- name: "Build" - name: 'Build'
run: "npm run build" run: 'npm run build'
- run: "npm run build:typescript" - run: 'npm run build:typescript'

View File

@ -1,4 +1,4 @@
name: "Lint" name: 'Lint'
on: on:
push: push:
@ -8,35 +8,35 @@ on:
jobs: jobs:
lint: lint:
runs-on: "ubuntu-latest" runs-on: 'ubuntu-latest'
steps: steps:
- uses: "actions/checkout@v4.0.0" - uses: 'actions/checkout@v3.5.2'
- name: "Setup Node.js" - name: 'Setup Node.js'
uses: "actions/setup-node@v3.8.1" uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: "20.x" node-version: '18.x'
cache: "npm" cache: 'npm'
- name: "Install dependencies" - name: 'Install dependencies'
run: "npm clean-install" run: 'npm clean-install'
- name: "lint:commit" - name: 'lint:commit'
run: 'npm run lint:commit -- --to "${{ github.sha }}"' run: 'npm run lint:commit -- --to "${{ github.sha }}"'
- name: "lint:editorconfig" - name: 'lint:editorconfig'
run: "npm run lint:editorconfig" run: 'npm run lint:editorconfig'
- name: "lint:markdown" - name: 'lint:markdown'
run: "npm run lint:markdown" run: 'npm run lint:markdown'
- name: "lint:eslint" - name: 'lint:eslint'
run: "npm run lint:eslint" run: 'npm run lint:eslint'
- name: "lint:prettier" - name: 'lint:prettier'
run: "npm run lint:prettier" run: 'npm run lint:prettier'
- name: "lint:dotenv" - name: 'lint:dotenv'
uses: "dotenv-linter/action-dotenv-linter@v2.18.0" uses: 'dotenv-linter/action-dotenv-linter@v2'
with: with:
github_token: ${{ secrets.github_token }} github_token: ${{ secrets.github_token }}

View File

@ -1,4 +1,4 @@
name: "Release" name: 'Release'
on: on:
push: push:
@ -6,36 +6,36 @@ on:
jobs: jobs:
release: release:
runs-on: "ubuntu-latest" runs-on: 'ubuntu-latest'
steps: steps:
- uses: "actions/checkout@v4.0.0" - uses: 'actions/checkout@v3.5.2'
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: "Import GPG key" - name: 'Import GPG key'
uses: "crazy-max/ghaction-import-gpg@v6.0.0" uses: 'crazy-max/ghaction-import-gpg@v5.3.0'
with: with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
git_user_signingkey: true git_user_signingkey: true
git_commit_gpgsign: true git_commit_gpgsign: true
- name: "Setup Node.js" - name: 'Setup Node.js'
uses: "actions/setup-node@v3.8.1" uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: "20.x" node-version: '18.x'
cache: "npm" cache: 'npm'
- name: "Install dependencies" - name: 'Install dependencies'
run: "npm clean-install" run: 'npm clean-install'
- name: "Build" - name: 'Build'
run: "npm run build" run: 'npm run build'
- run: "npm run build:typescript" - run: 'npm run build:typescript'
- name: "Release" - name: 'Release'
run: "npm run release" run: 'npm run release'
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
GIT_COMMITTER_NAME: ${{ secrets.GIT_NAME }} GIT_COMMITTER_NAME: ${{ secrets.GIT_NAME }}

View File

@ -1,3 +1,6 @@
{ {
"semi": false "singleQuote": true,
"jsxSingleQuote": true,
"semi": false,
"trailingComma": "none"
} }

15
.swcrc
View File

@ -1,13 +1,22 @@
{ {
"sourceMaps": true,
"jsc": { "jsc": {
"parser": { "parser": {
"syntax": "typescript", "syntax": "typescript",
"decorators": true,
"dynamicImport": true "dynamicImport": true
}, },
"target": "esnext" "transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2022",
"loose": true
}, },
"module": { "module": {
"type": "es6" "type": "es6",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
} }
} }

View File

@ -5,7 +5,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": "explicit" "source.fixAll": true
}, },
"eslint.options": { "ignorePath": ".gitignore" } "eslint.options": { "ignorePath": ".gitignore" }
} }

View File

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
<contact@theoludwig.fr>. <contact@divlo.fr>.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View File

@ -1,26 +0,0 @@
FROM node:20.9.0 AS builder-dependencies
WORKDIR /usr/src/application
COPY ./package*.json ./
RUN npm clean-install
FROM node:20.9.0 AS runner-dependencies
WORKDIR /usr/src/application
ENV NODE_ENV=production
COPY ./package*.json ./
RUN npm clean-install --omit=dev --ignore-scripts
FROM node:20.9.0 AS builder
WORKDIR /usr/src/application
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
COPY ./ ./
RUN npm run build
FROM gcr.io/distroless/nodejs20-debian12:latest AS runner
WORKDIR /usr/src/application
ENV NODE_ENV=production
ENV NODE_OPTIONS=--enable-source-maps
COPY --from=runner-dependencies /usr/src/application/node_modules ./node_modules
COPY --from=builder /usr/src/application/package.json ./package.json
COPY --from=builder /usr/src/application/build ./build
COPY --from=builder /usr/src/application/uploads ./uploads
CMD ["./build/index.js"]

View File

@ -1,8 +1,4 @@
<h1 align="center"><a href="https://file-uploads-api.thream.theoludwig.fr/documentation">Thream/file-uploads-api</a></h1> <h1 align="center"><a href="https://file-uploads-api.thream.divlo.fr/documentation">Thream/file-uploads-api</a></h1>
<p align="center">
<strong>⚠️ This project is not maintained anymore, you can still use the code as you wish and fork it to maintain it yourself.</strong>
</p>
<p align="center"> <p align="center">
<a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" /></a> <a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" /></a>
@ -31,7 +27,7 @@ Thream's application programming interface (API) to upload files.
```sh ```sh
# Clone the repository # Clone the repository
git clone git@github.com:Thream/file-uploads-api.git git clone https://github.com/Thream/file-uploads-api.git
# Go to the project root # Go to the project root
cd file-uploads-api cd file-uploads-api
@ -54,13 +50,7 @@ npm run dev
#### Services started #### Services started
- `file-uploads-api`: <http://127.0.0.1:8000> - `file-uploads-api`: <http://localhost:8000>
### Production environment (with [Docker](https://www.docker.com/))
```sh
docker compose up --build
```
## 💡 Contributing ## 💡 Contributing

View File

@ -1,11 +0,0 @@
services:
thream-file-uploads-api:
container_name: ${COMPOSE_PROJECT_NAME}
image: "thream-file-uploads-api"
restart: "unless-stopped"
network_mode: "host"
build:
context: "./"
env_file: ".env"
volumes:
- "./uploads:/usr/src/application/uploads"

6679
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,9 @@
{ {
"name": "@thream/file-uploads-api", "name": "@thream/file-uploads-api",
"version": "1.1.8", "version": "1.1.4",
"description": "Thream's application programming interface to upload files.", "description": "Thream's application programming interface to upload files.",
"private": true, "private": true,
"type": "module", "type": "module",
"imports": {
"#src/*": "./build/*"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/Thream/file-uploads-api" "url": "https://github.com/Thream/file-uploads-api"
@ -19,63 +16,63 @@
"build": "rimraf ./build && swc ./src --out-dir ./build", "build": "rimraf ./build && swc ./src --out-dir ./build",
"build:dev": "swc ./src --out-dir ./build --watch", "build:dev": "swc ./src --out-dir ./build --watch",
"build:typescript": "tsc", "build:typescript": "tsc",
"start": "node --enable-source-maps build/index.js", "start": "node build/index.js",
"dev": "concurrently -k -n \"TypeScript,Node\" -p \"[{name}]\" -c \"blue,green\" \"npm run build:dev\" \"cross-env NODE_ENV=development node --watch --enable-source-maps build/index.js\"", "dev": "concurrently -k -n \"TypeScript,Node\" -p \"[{name}]\" -c \"blue,green\" \"npm run build:dev\" \"cross-env NODE_ENV=development nodemon build/index.js\"",
"lint:commit": "commitlint", "lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint-cli2", "lint:markdown": "markdownlint-cli2",
"lint:eslint": "eslint . --max-warnings 0 --report-unused-disable-directives --ignore-path .gitignore", "lint:eslint": "eslint \".\" --ignore-path \".gitignore\"",
"lint:prettier": "prettier . --check", "lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"release": "semantic-release", "release": "semantic-release",
"postinstall": "husky install" "postinstall": "husky install"
}, },
"dependencies": { "dependencies": {
"@fastify/cors": "8.4.0", "@fastify/cors": "8.2.1",
"@fastify/helmet": "11.1.1", "@fastify/helmet": "10.1.1",
"@fastify/multipart": "8.0.0", "@fastify/multipart": "7.6.0",
"@fastify/rate-limit": "8.0.3", "@fastify/rate-limit": "8.0.0",
"@fastify/sensible": "5.5.0", "@fastify/sensible": "5.2.0",
"@fastify/static": "6.11.2", "@fastify/static": "6.10.1",
"@fastify/swagger": "8.12.0", "@fastify/swagger": "8.3.1",
"@fastify/swagger-ui": "1.10.1", "@fastify/swagger-ui": "1.8.1",
"@sinclair/typebox": "0.31.18", "@sinclair/typebox": "0.28.10",
"dotenv": "16.3.1", "dotenv": "16.0.3",
"fastify": "4.24.3", "fastify": "4.17.0",
"fastify-plugin": "4.5.1", "fastify-plugin": "4.5.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"read-pkg": "8.1.0" "read-pkg": "8.0.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "18.4.3", "@commitlint/cli": "17.6.3",
"@commitlint/config-conventional": "18.4.3", "@commitlint/config-conventional": "17.6.3",
"@saithodev/semantic-release-backmerge": "4.0.1", "@saithodev/semantic-release-backmerge": "3.2.0",
"@semantic-release/git": "10.0.1", "@semantic-release/git": "10.0.1",
"@swc/cli": "0.1.62", "@swc/cli": "0.1.62",
"@swc/core": "1.3.94", "@swc/core": "1.3.57",
"@tsconfig/strictest": "2.0.2", "@tsconfig/strictest": "2.0.1",
"@types/busboy": "1.5.2", "@types/busboy": "1.5.0",
"@types/http-errors": "2.0.3", "@types/http-errors": "2.0.1",
"@types/node": "20.8.7", "@types/node": "20.1.4",
"@typescript-eslint/eslint-plugin": "6.9.0", "@typescript-eslint/eslint-plugin": "5.59.5",
"chokidar": "3.5.3", "concurrently": "8.0.1",
"concurrently": "8.2.2",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"editorconfig-checker": "5.1.1", "editorconfig-checker": "5.0.1",
"eslint": "8.52.0", "eslint": "8.40.0",
"eslint-config-conventions": "12.0.0", "eslint-config-conventions": "9.0.0",
"eslint-config-prettier": "9.0.0", "eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.29.0", "eslint-plugin-import": "2.27.5",
"eslint-plugin-prettier": "5.0.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "48.0.1", "eslint-plugin-unicorn": "47.0.0",
"husky": "8.0.3", "husky": "8.0.3",
"lint-staged": "15.0.2", "lint-staged": "13.2.2",
"markdownlint-cli2": "0.10.0", "markdownlint-cli2": "0.7.1",
"markdownlint-rule-relative-links": "2.1.0", "markdownlint-rule-relative-links": "1.2.0",
"prettier": "3.0.3", "nodemon": "2.0.22",
"rimraf": "5.0.5", "prettier": "2.8.8",
"semantic-release": "22.0.12", "rimraf": "5.0.0",
"typescript": "5.2.2" "semantic-release": "21.0.2",
"typescript": "5.0.4"
} }
} }

View File

@ -1,32 +1,32 @@
import { fileURLToPath } from "node:url" import { fileURLToPath } from 'node:url'
import dotenv from "dotenv" import dotenv from 'dotenv'
import fastify from "fastify" import fastify from 'fastify'
import fastifyCors from "@fastify/cors" import fastifyCors from '@fastify/cors'
import fastifySwagger from "@fastify/swagger" import fastifySwagger from '@fastify/swagger'
import fastifySwaggerUI from "@fastify/swagger-ui" import fastifySwaggerUI from '@fastify/swagger-ui'
import fastifyHelmet from "@fastify/helmet" import fastifyHelmet from '@fastify/helmet'
import fastifyRateLimit from "@fastify/rate-limit" import fastifyRateLimit from '@fastify/rate-limit'
import fastifySensible from "@fastify/sensible" import fastifySensible from '@fastify/sensible'
import fastifyStatic from "@fastify/static" import fastifyStatic from '@fastify/static'
import { readPackage } from "read-pkg" import { readPackage } from 'read-pkg'
import { services } from "#src/services/index.js" import { services } from './services/index.js'
import { UPLOADS_URL } from "#src/tools/configurations.js" import { UPLOADS_URL } from './tools/configurations.js'
dotenv.config() dotenv.config()
const packageJSON = await readPackage() const packageJSON = await readPackage()
export const application = fastify({ export const application = fastify({
logger: process.env["NODE_ENV"] === "development", logger: process.env['NODE_ENV'] === 'development',
ajv: { ajv: {
customOptions: { customOptions: {
strict: "log", strict: 'log',
keywords: ["kind", "modifier"], keywords: ['kind', 'modifier'],
formats: { formats: {
full: true, full: true
}, }
}, }
}, }
}) })
await application.register(fastifyCors) await application.register(fastifyCors)
@ -34,34 +34,34 @@ await application.register(fastifySensible)
await application.register(fastifyHelmet) await application.register(fastifyHelmet)
await application.register(fastifyRateLimit, { await application.register(fastifyRateLimit, {
max: 200, max: 200,
timeWindow: "1 minute", timeWindow: '1 minute'
}) })
await application.register(fastifyStatic, { await application.register(fastifyStatic, {
root: fileURLToPath(UPLOADS_URL), root: fileURLToPath(UPLOADS_URL),
prefix: "/uploads/", prefix: '/uploads/'
}) })
await application.register(fastifySwagger, { await application.register(fastifySwagger, {
openapi: { openapi: {
info: { info: {
title: packageJSON.name, title: packageJSON.name,
description: packageJSON.description, description: packageJSON.description,
version: packageJSON.version, version: packageJSON.version
}, },
tags: [{ name: "users" }, { name: "guilds" }, { name: "messages" }], tags: [{ name: 'users' }, { name: 'guilds' }, { name: 'messages' }],
components: { components: {
securitySchemes: { securitySchemes: {
apiKeyAuth: { apiKeyAuth: {
type: "apiKey", type: 'apiKey',
name: "X-API-KEY", name: 'X-API-KEY',
in: "header", in: 'header'
}, }
}, }
}, }
}, },
hideUntagged: true, hideUntagged: true
}) })
await application.register(fastifySwaggerUI, { await application.register(fastifySwaggerUI, {
routePrefix: "/documentation", routePrefix: '/documentation',
staticCSP: true, staticCSP: true
}) })
await application.register(services) await application.register(services)

View File

@ -1,8 +1,8 @@
import { application } from "#src/application.js" import { application } from './application.js'
import { HOST, PORT } from "#src/tools/configurations.js" import { HOST, PORT } from './tools/configurations.js'
const address = await application.listen({ const address = await application.listen({
port: PORT, port: PORT,
host: HOST, host: HOST
}) })
console.log(`Server listening at ${address}`) console.log('\u001B[36m%s\u001B[0m', `🚀 Server listening at ${address}`)

View File

@ -1,36 +1,36 @@
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
export const fastifyErrorsSchema = { export const fastifyErrorsSchema = {
400: { 400: {
statusCode: Type.Literal(400), statusCode: Type.Literal(400),
error: Type.Literal("Bad Request"), error: Type.Literal('Bad Request'),
message: Type.String(), message: Type.String()
}, },
401: { 401: {
statusCode: Type.Literal(401), statusCode: Type.Literal(401),
error: Type.Literal("Unauthorized"), error: Type.Literal('Unauthorized'),
message: Type.Literal("Unauthorized"), message: Type.Literal('Unauthorized')
}, },
403: { 403: {
statusCode: Type.Literal(403), statusCode: Type.Literal(403),
error: Type.Literal("Forbidden"), error: Type.Literal('Forbidden'),
message: Type.Literal("Forbidden"), message: Type.Literal('Forbidden')
}, },
404: { 404: {
statusCode: Type.Literal(404), statusCode: Type.Literal(404),
error: Type.Literal("Not Found"), error: Type.Literal('Not Found'),
message: Type.Literal("Not Found"), message: Type.Literal('Not Found')
}, },
431: { 431: {
statusCode: Type.Literal(431), statusCode: Type.Literal(431),
error: Type.Literal("Request Header Fields Too Large"), error: Type.Literal('Request Header Fields Too Large'),
message: Type.String(), message: Type.String()
}, },
500: { 500: {
statusCode: Type.Literal(500), statusCode: Type.Literal(500),
error: Type.Literal("Internal Server Error"), error: Type.Literal('Internal Server Error'),
message: Type.Literal("Something went wrong"), message: Type.Literal('Something went wrong')
}, }
} }
export const fastifyErrors = { export const fastifyErrors = {
@ -39,5 +39,5 @@ export const fastifyErrors = {
403: Type.Object(fastifyErrorsSchema[403]), 403: Type.Object(fastifyErrorsSchema[403]),
404: Type.Object(fastifyErrorsSchema[404]), 404: Type.Object(fastifyErrorsSchema[404]),
431: Type.Object(fastifyErrorsSchema[431]), 431: Type.Object(fastifyErrorsSchema[431]),
500: Type.Object(fastifyErrorsSchema[500]), 500: Type.Object(fastifyErrorsSchema[500])
} }

View File

@ -1,6 +1,6 @@
import type { FastifyPluginAsync } from "fastify" import type { FastifyPluginAsync } from 'fastify'
import { uploadsService } from "#src/services/uploads/index.js" import { uploadsService } from './uploads/index.js'
export const services: FastifyPluginAsync = async (fastify) => { export const services: FastifyPluginAsync = async (fastify) => {
await fastify.register(uploadsService) await fastify.register(uploadsService)

View File

@ -1,48 +1,48 @@
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
import type { DeleteParameters } from "#src/tools/utils/deleteUploadedFile.js" import type { DeleteParameters } from '../../../tools/utils/deleteUploadedFile.js'
import { import {
deleteParameters, deleteParameters,
deleteUploadedFile, deleteUploadedFile
} from "#src/tools/utils/deleteUploadedFile.js" } from '../../../tools/utils/deleteUploadedFile.js'
export const deleteServiceSchema: FastifySchema = { export const deleteServiceSchema: FastifySchema = {
tags: ["guilds"] as string[], tags: ['guilds'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
params: deleteParameters, params: deleteParameters,
response: { response: {
200: Type.String(), 200: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
404: fastifyErrors[404], 404: fastifyErrors[404],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const deleteGuildsUploadsService: FastifyPluginAsync = async ( export const deleteGuildsUploadsService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
await fastify.route<{ await fastify.route<{
Params: DeleteParameters Params: DeleteParameters
}>({ }>({
method: "DELETE", method: 'DELETE',
url: "/uploads/guilds/:file", url: '/uploads/guilds/:file',
schema: deleteServiceSchema, schema: deleteServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
return await deleteUploadedFile({ return await deleteUploadedFile({
fastify, fastify,
request, request,
reply, reply,
folder: "guilds", folder: 'guilds'
}) })
}, }
}) })
} }

View File

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

View File

@ -1,44 +1,44 @@
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from "@fastify/multipart" import fastifyMultipart from '@fastify/multipart'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import { uploadFile } from "#src/tools/utils/uploadFile.js" import { uploadFile } from '../../../tools/utils/uploadFile.js'
import { import {
MAXIMUM_IMAGE_SIZE, MAXIMUM_IMAGE_SIZE,
SUPPORTED_IMAGE_MIMETYPE, SUPPORTED_IMAGE_MIMETYPE
} from "#src/tools/configurations.js" } from '../../../tools/configurations.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
const postServiceSchema: FastifySchema = { const postServiceSchema: FastifySchema = {
description: "Uploads guild icon", description: 'Uploads guild icon',
tags: ["guilds"] as string[], tags: ['guilds'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
consumes: ["multipart/form-data"] as string[], consumes: ['multipart/form-data'] as string[],
produces: ["application/json"] as string[], produces: ['application/json'] as string[],
response: { response: {
201: Type.String(), 201: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
401: fastifyErrors[401], 401: fastifyErrors[401],
403: fastifyErrors[403], 403: fastifyErrors[403],
431: fastifyErrors[431], 431: fastifyErrors[431],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const postGuildsUploadsIconService: FastifyPluginAsync = async ( export const postGuildsUploadsIconService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(fastifyMultipart) await fastify.register(fastifyMultipart)
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
fastify.route({ fastify.route({
method: "POST", method: 'POST',
url: "/uploads/guilds", url: '/uploads/guilds',
schema: postServiceSchema, schema: postServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
if (request.apiKey == null) { if (request.apiKey == null) {
@ -47,12 +47,12 @@ export const postGuildsUploadsIconService: FastifyPluginAsync = async (
const file = await uploadFile({ const file = await uploadFile({
fastify, fastify,
request, request,
folderInUploadsFolder: "guilds", folderInUploadsFolder: 'guilds',
maximumFileSize: MAXIMUM_IMAGE_SIZE, maximumFileSize: MAXIMUM_IMAGE_SIZE,
supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE, supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE
}) })
reply.statusCode = 201 reply.statusCode = 201
return file.pathToStoreInDatabase return file.pathToStoreInDatabase
}, }
}) })
} }

View File

@ -1,14 +1,14 @@
import type { FastifyPluginAsync } from "fastify" import type { FastifyPluginAsync } from 'fastify'
import { deleteGuildsUploadsService } from "#src/services/uploads/guilds/delete.js" import { deleteGuildsUploadsService } from './guilds/delete.js'
import { getGuildsUploadsService } from "#src/services/uploads/guilds/get.js" import { getGuildsUploadsService } from './guilds/get.js'
import { postGuildsUploadsIconService } from "#src/services/uploads/guilds/post.js" import { postGuildsUploadsIconService } from './guilds/post.js'
import { deleteMessagesUploadsService } from "#src/services/uploads/messages/delete.js" import { deleteMessagesUploadsService } from './messages/delete.js'
import { getMessagesUploadsService } from "#src/services/uploads/messages/get.js" import { getMessagesUploadsService } from './messages/get.js'
import { postMessagesUploadsService } from "#src/services/uploads/messages/post.js" import { postMessagesUploadsService } from './messages/post.js'
import { deleteUsersUploadsService } from "#src/services/uploads/users/delete.js" import { deleteUsersUploadsService } from './users/delete.js'
import { getUsersUploadsService } from "#src/services/uploads/users/get.js" import { getUsersUploadsService } from './users/get.js'
import { postUsersUploadsLogoService } from "#src/services/uploads/users/post.js" import { postUsersUploadsLogoService } from './users/post.js'
export const uploadsService: FastifyPluginAsync = async (fastify) => { export const uploadsService: FastifyPluginAsync = async (fastify) => {
await fastify.register(deleteGuildsUploadsService) await fastify.register(deleteGuildsUploadsService)

View File

@ -1,48 +1,48 @@
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
import type { DeleteParameters } from "#src/tools/utils/deleteUploadedFile.js" import type { DeleteParameters } from '../../../tools/utils/deleteUploadedFile.js'
import { import {
deleteParameters, deleteParameters,
deleteUploadedFile, deleteUploadedFile
} from "#src/tools/utils/deleteUploadedFile.js" } from '../../../tools/utils/deleteUploadedFile.js'
export const deleteServiceSchema: FastifySchema = { export const deleteServiceSchema: FastifySchema = {
tags: ["messages"] as string[], tags: ['messages'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
params: deleteParameters, params: deleteParameters,
response: { response: {
200: Type.String(), 200: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
404: fastifyErrors[404], 404: fastifyErrors[404],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const deleteMessagesUploadsService: FastifyPluginAsync = async ( export const deleteMessagesUploadsService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
await fastify.route<{ await fastify.route<{
Params: DeleteParameters Params: DeleteParameters
}>({ }>({
method: "DELETE", method: 'DELETE',
url: "/uploads/messages/:file", url: '/uploads/messages/:file',
schema: deleteServiceSchema, schema: deleteServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
return await deleteUploadedFile({ return await deleteUploadedFile({
fastify, fastify,
request, request,
reply, reply,
folder: "messages", folder: 'messages'
}) })
}, }
}) })
} }

View File

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

View File

@ -1,41 +1,41 @@
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from "@fastify/multipart" import fastifyMultipart from '@fastify/multipart'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import { uploadFile } from "#src/tools/utils/uploadFile.js" import { uploadFile } from '../../../tools/utils/uploadFile.js'
import { MAXIMUM_IMAGE_SIZE } from "#src/tools/configurations.js" import { MAXIMUM_IMAGE_SIZE } from '../../../tools/configurations.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
const postServiceSchema: FastifySchema = { const postServiceSchema: FastifySchema = {
description: "Uploads message file", description: 'Uploads message file',
tags: ["messages"] as string[], tags: ['messages'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
consumes: ["multipart/form-data"] as string[], consumes: ['multipart/form-data'] as string[],
produces: ["application/json"] as string[], produces: ['application/json'] as string[],
response: { response: {
201: Type.String(), 201: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
401: fastifyErrors[401], 401: fastifyErrors[401],
403: fastifyErrors[403], 403: fastifyErrors[403],
431: fastifyErrors[431], 431: fastifyErrors[431],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const postMessagesUploadsService: FastifyPluginAsync = async ( export const postMessagesUploadsService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(fastifyMultipart) await fastify.register(fastifyMultipart)
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
fastify.route({ fastify.route({
method: "POST", method: 'POST',
url: "/uploads/messages", url: '/uploads/messages',
schema: postServiceSchema, schema: postServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
if (request.apiKey == null) { if (request.apiKey == null) {
@ -44,11 +44,11 @@ export const postMessagesUploadsService: FastifyPluginAsync = async (
const file = await uploadFile({ const file = await uploadFile({
fastify, fastify,
request, request,
folderInUploadsFolder: "messages", folderInUploadsFolder: 'messages',
maximumFileSize: MAXIMUM_IMAGE_SIZE, maximumFileSize: MAXIMUM_IMAGE_SIZE
}) })
reply.statusCode = 201 reply.statusCode = 201
return file.pathToStoreInDatabase return file.pathToStoreInDatabase
}, }
}) })
} }

View File

@ -1,48 +1,48 @@
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
import type { DeleteParameters } from "#src/tools/utils/deleteUploadedFile.js" import type { DeleteParameters } from '../../../tools/utils/deleteUploadedFile.js'
import { import {
deleteParameters, deleteParameters,
deleteUploadedFile, deleteUploadedFile
} from "#src/tools/utils/deleteUploadedFile.js" } from '../../../tools/utils/deleteUploadedFile.js'
export const deleteServiceSchema: FastifySchema = { export const deleteServiceSchema: FastifySchema = {
tags: ["users"] as string[], tags: ['users'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
params: deleteParameters, params: deleteParameters,
response: { response: {
200: Type.String(), 200: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
404: fastifyErrors[404], 404: fastifyErrors[404],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const deleteUsersUploadsService: FastifyPluginAsync = async ( export const deleteUsersUploadsService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
await fastify.route<{ await fastify.route<{
Params: DeleteParameters Params: DeleteParameters
}>({ }>({
method: "DELETE", method: 'DELETE',
url: "/uploads/users/:file", url: '/uploads/users/:file',
schema: deleteServiceSchema, schema: deleteServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
return await deleteUploadedFile({ return await deleteUploadedFile({
fastify, fastify,
request, request,
reply, reply,
folder: "users", folder: 'users'
}) })
}, }
}) })
} }

View File

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

View File

@ -1,44 +1,44 @@
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import type { FastifyPluginAsync, FastifySchema } from "fastify" import type { FastifyPluginAsync, FastifySchema } from 'fastify'
import fastifyMultipart from "@fastify/multipart" import fastifyMultipart from '@fastify/multipart'
import { fastifyErrors } from "#src/models/utils.js" import { fastifyErrors } from '../../../models/utils.js'
import { uploadFile } from "#src/tools/utils/uploadFile.js" import { uploadFile } from '../../../tools/utils/uploadFile.js'
import { import {
MAXIMUM_IMAGE_SIZE, MAXIMUM_IMAGE_SIZE,
SUPPORTED_IMAGE_MIMETYPE, SUPPORTED_IMAGE_MIMETYPE
} from "#src/tools/configurations.js" } from '../../../tools/configurations.js'
import verifyAPIKey from "#src/tools/plugins/verifyAPIKey.js" import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
const postServiceSchema: FastifySchema = { const postServiceSchema: FastifySchema = {
description: "Uploads user logo", description: 'Uploads user logo',
tags: ["users"] as string[], tags: ['users'] as string[],
security: [ security: [
{ {
apiKeyAuth: [], apiKeyAuth: []
}, }
] as Array<{ [key: string]: [] }>, ] as Array<{ [key: string]: [] }>,
consumes: ["multipart/form-data"] as string[], consumes: ['multipart/form-data'] as string[],
produces: ["application/json"] as string[], produces: ['application/json'] as string[],
response: { response: {
201: Type.String(), 201: Type.String(),
400: fastifyErrors[400], 400: fastifyErrors[400],
401: fastifyErrors[401], 401: fastifyErrors[401],
403: fastifyErrors[403], 403: fastifyErrors[403],
431: fastifyErrors[431], 431: fastifyErrors[431],
500: fastifyErrors[500], 500: fastifyErrors[500]
}, }
} as const } as const
export const postUsersUploadsLogoService: FastifyPluginAsync = async ( export const postUsersUploadsLogoService: FastifyPluginAsync = async (
fastify, fastify
) => { ) => {
await fastify.register(fastifyMultipart) await fastify.register(fastifyMultipart)
await fastify.register(verifyAPIKey) await fastify.register(verifyAPIKey)
fastify.route({ fastify.route({
method: "POST", method: 'POST',
url: "/uploads/users", url: '/uploads/users',
schema: postServiceSchema, schema: postServiceSchema,
handler: async (request, reply) => { handler: async (request, reply) => {
if (request.apiKey == null) { if (request.apiKey == null) {
@ -47,12 +47,12 @@ export const postUsersUploadsLogoService: FastifyPluginAsync = async (
const file = await uploadFile({ const file = await uploadFile({
fastify, fastify,
request, request,
folderInUploadsFolder: "users", folderInUploadsFolder: 'users',
maximumFileSize: MAXIMUM_IMAGE_SIZE, maximumFileSize: MAXIMUM_IMAGE_SIZE,
supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE, supportedFileMimetype: SUPPORTED_IMAGE_MIMETYPE
}) })
reply.statusCode = 201 reply.statusCode = 201
return file.pathToStoreInDatabase return file.pathToStoreInDatabase
}, }
}) })
} }

View File

@ -1,23 +1,23 @@
import { URL } from "node:url" import { URL } from 'node:url'
import dotenv from "dotenv" import dotenv from 'dotenv'
dotenv.config() dotenv.config()
export const PORT = Number.parseInt(process.env["PORT"] ?? "8000", 10) export const PORT = parseInt(process.env['PORT'] ?? '8000', 10)
export const HOST = process.env["HOST"] ?? "0.0.0.0" export const HOST = process.env['HOST'] ?? '0.0.0.0'
export const API_URL = process.env["API_URL"] ?? `http://${HOST}:${PORT}` export const API_URL = process.env['API_URL'] ?? `http://${HOST}:${PORT}`
export const API_KEY = process.env["API_KEY"] ?? "apiKeySecret" export const API_KEY = process.env['API_KEY'] ?? 'apiKeySecret'
export const SRC_URL = new URL("../", import.meta.url) export const SRC_URL = new URL('../', import.meta.url)
export const ROOT_URL = new URL("../", SRC_URL) export const ROOT_URL = new URL('../', SRC_URL)
export const UPLOADS_URL = new URL("./uploads/", ROOT_URL) export const UPLOADS_URL = new URL('./uploads/', ROOT_URL)
export const SUPPORTED_IMAGE_MIMETYPE = [ export const SUPPORTED_IMAGE_MIMETYPE = [
"image/png", 'image/png',
"image/jpg", 'image/jpg',
"image/jpeg", 'image/jpeg',
"image/gif", 'image/gif'
] ]
/** in megabytes */ /** in megabytes */

View File

@ -1,11 +1,11 @@
import fastifyPlugin from "fastify-plugin" import fastifyPlugin from 'fastify-plugin'
import httpErrors from "http-errors" import httpErrors from 'http-errors'
import { API_KEY } from "#src/tools/configurations.js" import { API_KEY } from '../configurations.js'
const { Unauthorized, Forbidden } = httpErrors const { Unauthorized, Forbidden } = httpErrors
declare module "fastify" { declare module 'fastify' {
export interface FastifyRequest { export interface FastifyRequest {
apiKey?: string apiKey?: string
} }
@ -13,10 +13,10 @@ declare module "fastify" {
export default fastifyPlugin( export default fastifyPlugin(
async (fastify) => { async (fastify) => {
fastify.decorateRequest("apiKey", undefined) await fastify.decorateRequest('apiKey', null)
fastify.addHook("onRequest", async (request) => { await fastify.addHook('onRequest', async (request) => {
const apiKey = request.headers["x-api-key"] const apiKey = request.headers['x-api-key']
if (apiKey == null || typeof apiKey !== "string") { if (apiKey == null || typeof apiKey !== 'string') {
throw new Unauthorized() throw new Unauthorized()
} }
if (apiKey !== API_KEY) { if (apiKey !== API_KEY) {
@ -25,5 +25,5 @@ export default fastifyPlugin(
request.apiKey = apiKey request.apiKey = apiKey
}) })
}, },
{ fastify: "4.x" }, { fastify: '4.x' }
) )

View File

@ -1,27 +1,27 @@
import type { IncomingMessage, Server, ServerResponse } from "node:http" import type { IncomingMessage, Server, ServerResponse } from 'node:http'
import { fileURLToPath } from "node:url" import { fileURLToPath } from 'node:url'
import fs from "node:fs" import fs from 'node:fs'
import type { Static } from "@sinclair/typebox" import type { Static } from '@sinclair/typebox'
import { Type } from "@sinclair/typebox" import { Type } from '@sinclair/typebox'
import type { import type {
FastifyBaseLogger, FastifyBaseLogger,
FastifyInstance, FastifyInstance,
FastifyReply, FastifyReply,
FastifyRequest, FastifyRequest,
FastifyTypeProviderDefault, FastifyTypeProviderDefault
} from "fastify" } from 'fastify'
import { isExistingFile } from "#src/tools/utils/isExistingFile.js" import { isExistingFile } from './isExistingFile.js'
export const deleteParameters = Type.Object({ export const deleteParameters = Type.Object({
file: Type.String(), file: Type.String()
}) })
export type DeleteParameters = Static<typeof deleteParameters> export type DeleteParameters = Static<typeof deleteParameters>
export interface DeleteUploadedFileOptions { export interface DeleteUploadedFileOptions {
folder: "guilds" | "messages" | "users" folder: 'guilds' | 'messages' | 'users'
fastify: FastifyInstance< fastify: FastifyInstance<
Server<typeof IncomingMessage, typeof ServerResponse>, Server<typeof IncomingMessage, typeof ServerResponse>,
IncomingMessage, IncomingMessage,
@ -34,7 +34,7 @@ export interface DeleteUploadedFileOptions {
} }
export const deleteUploadedFile = async ( export const deleteUploadedFile = async (
options: DeleteUploadedFileOptions, options: DeleteUploadedFileOptions
): Promise<string> => { ): Promise<string> => {
const { request, fastify, reply, folder } = options const { request, fastify, reply, folder } = options
if (request.apiKey == null) { if (request.apiKey == null) {
@ -44,7 +44,7 @@ export const deleteUploadedFile = async (
const fileURL = new URL(`../../../uploads/${folder}/${file}`, import.meta.url) const fileURL = new URL(`../../../uploads/${folder}/${file}`, import.meta.url)
const filePath = fileURLToPath(fileURL) const filePath = fileURLToPath(fileURL)
if (!(await isExistingFile(filePath))) { if (!(await isExistingFile(filePath))) {
throw fastify.httpErrors.notFound("File does not exist") throw fastify.httpErrors.notFound('File does not exist')
} }
await fs.promises.rm(filePath, { force: true }) await fs.promises.rm(filePath, { force: true })
reply.statusCode = 200 reply.statusCode = 200

View File

@ -1,4 +1,4 @@
import fs from "node:fs" import fs from 'node:fs'
export const isExistingFile = async (path: string): Promise<boolean> => { export const isExistingFile = async (path: string): Promise<boolean> => {
try { try {

View File

@ -1,14 +1,14 @@
import fs from "node:fs" import fs from 'node:fs'
import { URL } from "node:url" import { URL } from 'node:url'
import { randomUUID } from "node:crypto" import { randomUUID } from 'node:crypto'
import type { FastifyInstance, FastifyRequest } from "fastify" import type { FastifyInstance, FastifyRequest } from 'fastify'
import type { SavedMultipartFile } from "@fastify/multipart" import type { SavedMultipartFile } from '@fastify/multipart'
import { API_URL, ROOT_URL } from "#src/tools/configurations.js" import { API_URL, ROOT_URL } from '../configurations.js'
export interface UploadFileOptions { export interface UploadFileOptions {
folderInUploadsFolder: "guilds" | "messages" | "users" folderInUploadsFolder: 'guilds' | 'messages' | 'users'
request: FastifyRequest request: FastifyRequest
fastify: FastifyInstance fastify: FastifyInstance
@ -24,45 +24,44 @@ export interface UploadFileResult {
} }
export const uploadFile = async ( export const uploadFile = async (
options: UploadFileOptions, options: UploadFileOptions
): Promise<UploadFileResult> => { ): Promise<UploadFileResult> => {
const { const {
fastify, fastify,
request, request,
folderInUploadsFolder, folderInUploadsFolder,
maximumFileSize, maximumFileSize,
supportedFileMimetype, supportedFileMimetype
} = options } = options
let files: SavedMultipartFile[] = [] let files: SavedMultipartFile[] = []
try { try {
files = await request.saveRequestFiles({ files = await request.saveRequestFiles({
limits: { limits: {
files: 1, files: 1,
fileSize: maximumFileSize * 1024 * 1024, fileSize: maximumFileSize * 1024 * 1024
}, }
}) })
} catch (error) { } catch (error) {
console.error(error)
throw fastify.httpErrors.requestHeaderFieldsTooLarge( throw fastify.httpErrors.requestHeaderFieldsTooLarge(
`File should be less than ${maximumFileSize}mb.`, `File should be less than ${maximumFileSize}mb.`
) )
} }
const file = files[0] const file = files[0]
if (files.length !== 1 || file == null) { if (files.length !== 1 || file == null) {
throw fastify.httpErrors.badRequest("You must upload at most one file.") throw fastify.httpErrors.badRequest('You must upload at most one file.')
} }
if ( if (
supportedFileMimetype != null && supportedFileMimetype != null &&
!supportedFileMimetype.includes(file.mimetype) !supportedFileMimetype.includes(file.mimetype)
) { ) {
throw fastify.httpErrors.badRequest( throw fastify.httpErrors.badRequest(
`The file must have a valid type (${supportedFileMimetype.join(", ")}).`, `The file must have a valid type (${supportedFileMimetype.join(', ')}).`
) )
} }
const splitedMimetype = file.mimetype.split("/") const splitedMimetype = file.mimetype.split('/')
const fileExtension = splitedMimetype[1] const fileExtension = splitedMimetype[1]
if (fileExtension == null) { if (fileExtension == null) {
throw fastify.httpErrors.badRequest("The file extension is not valid.") throw fastify.httpErrors.badRequest('The file extension is not valid.')
} }
const filePath = `uploads/${folderInUploadsFolder}/${randomUUID()}.${fileExtension}` const filePath = `uploads/${folderInUploadsFolder}/${randomUUID()}.${fileExtension}`
const fileURL = new URL(filePath, ROOT_URL) const fileURL = new URL(filePath, ROOT_URL)

View File

@ -2,15 +2,11 @@
"extends": "@tsconfig/strictest/tsconfig.json", "extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext",
"lib": ["ESNext"], "lib": ["ESNext"],
"module": "NodeNext", "moduleResolution": "node",
"moduleResolution": "NodeNext",
"outDir": "./build", "outDir": "./build",
"rootDir": "./src", "rootDir": "./src",
"baseUrl": ".",
"paths": {
"#src/*": ["./src/*"]
},
"noEmit": true, "noEmit": true,
"exactOptionalPropertyTypes": false, "exactOptionalPropertyTypes": false,
"checkJs": false "checkJs": false