feat(scripts): add delete dead uploaded files

This commit is contained in:
Divlo 2022-03-05 18:49:38 +00:00
parent 1bcee76324
commit 9e6bf25c83
No known key found for this signature in database
GPG Key ID: 8F9478F220CE65E9
8 changed files with 438 additions and 427 deletions

View File

@ -21,7 +21,7 @@ All work on **Thream/api** happens directly on [GitHub](https://github.com/Threa
- **Please first discuss** the change you wish to make via issues.
- Ensure your code respect `eslint` and `prettier`.
- Ensure your code respect linting.
- Make sure your **code passes the tests**.
@ -70,6 +70,7 @@ git commit -m "fix(services): should emit events to connected users"
├── prisma
└── src
├── models
├── scripts
├── services
├── tools
└── typings
@ -81,6 +82,7 @@ git commit -m "fix(services): should emit events to connected users"
- `prisma` : contains the prisma schema and migrations
- `src` : all source files
- `models` : models that represent tables in database as JSON schema
- `scripts` : scripts
- `services` : all REST API endpoints
- `tools` : configs and utilities
- `typings` : types gloablly used in the project

View File

@ -1,15 +1,15 @@
FROM node:16.13.2 AS dependencies
FROM node:16.14.0 AS dependencies
WORKDIR /usr/src/app
COPY ./package*.json ./
RUN npm install
FROM node:16.13.2 AS builder
FROM node:16.14.0 AS builder
WORKDIR /usr/src/app
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
COPY ./ ./
RUN npm run prisma:generate && npm run build
FROM node:16.13.2 AS runner
FROM node:16.14.0 AS runner
WORKDIR /usr/src/app
ENV NODE_ENV=production
COPY --from=builder /usr/src/app/node_modules ./node_modules

View File

@ -16,9 +16,7 @@
## 📜 About
Thream's application programming interface to stay close with your friends and communities.
This project was bootstrapped with [create-fullstack-app](https://github.com/Divlo/create-fullstack-app).
Thream's Application Programming Interface (API) to stay close with your friends and communities.
## ⚙️ Getting Started

695
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"start": "cross-env NODE_ENV=production node build/index.js",
"dev": "concurrently -k -n \"TypeScript,Node\" -p \"[{name}]\" -c \"blue,green\" \"swc ./src --out-dir ./build --watch\" \"cross-env NODE_ENV=development nodemon build/index.js\"",
"generate": "plop",
"scripts:delete-dead-uploaded-files": "node build/scripts/delete-dead-uploaded-files.js",
"lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint \"**/*.md\" --dot --ignore-path \".gitignore\"",
@ -46,7 +47,7 @@
"fastify-rate-limit": "5.7.2",
"fastify-sensible": "3.1.2",
"fastify-static": "4.5.0",
"fastify-swagger": "4.15.0",
"fastify-swagger": "4.17.0",
"fastify-url-data": "3.0.3",
"http-errors": "2.0.0",
"jsonwebtoken": "8.5.1",
@ -60,7 +61,7 @@
"@commitlint/config-conventional": "16.2.1",
"@saithodev/semantic-release-backmerge": "2.1.2",
"@swc/cli": "0.1.55",
"@swc/core": "1.2.146",
"@swc/core": "1.2.148",
"@swc/jest": "0.2.20",
"@types/bcryptjs": "2.4.2",
"@types/busboy": "1.3.0",
@ -76,7 +77,7 @@
"cross-env": "7.0.3",
"editorconfig-checker": "4.0.2",
"eslint": "8.10.0",
"eslint-config-prettier": "8.4.0",
"eslint-config-prettier": "8.5.0",
"eslint-config-conventions": "1.1.0",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-prettier": "4.0.0",
@ -86,7 +87,7 @@
"jest": "27.5.1",
"jest-mock-extended": "2.0.4",
"jest-ts-webcompat-resolver": "1.0.0",
"lint-staged": "12.3.4",
"lint-staged": "12.3.5",
"markdownlint-cli": "0.31.1",
"nodemon": "2.0.15",
"plop": "3.0.5",

View File

@ -3,13 +3,15 @@ import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
import prisma from '../tools/database/prisma.js'
jest.mock('nodemailer', () => ({
jest.mock('nodemailer', () => {
return {
createTransport: () => {
return {
sendMail: jest.fn(async () => {})
}
}
}))
}
})
jest.mock('../tools/database/prisma.js', () => ({
__esModule: true,

View File

@ -0,0 +1,58 @@
import fs from 'node:fs'
import prisma from '../tools/database/prisma.js'
import { UPLOADS_URL } from '../tools/configurations/index.js'
const getPathStoredInDatabaseFromFile = (
file: string,
folderInUploadsFolder: string
): string => {
return `/uploads/${folderInUploadsFolder}/${file}`
}
const deleteDeadUploadedFiles = async (
folderInUploadsFolder: string,
getElementInDatabase: (file: string) => Promise<unknown | null>
): Promise<void> => {
const UPLOADS_FILES_URL = new URL(`./${folderInUploadsFolder}`, UPLOADS_URL)
const files = await fs.promises.readdir(UPLOADS_FILES_URL)
for (const file of files) {
if (file !== '.gitkeep') {
const pathStoredInDatabase = getPathStoredInDatabaseFromFile(
file,
folderInUploadsFolder
)
const element = await getElementInDatabase(pathStoredInDatabase)
if (element == null) {
const fileURL = new URL(
`./${folderInUploadsFolder}/${file}`,
UPLOADS_URL
)
await fs.promises.rm(fileURL)
}
}
}
}
const main = async (): Promise<void> => {
await deleteDeadUploadedFiles('guilds', async (icon: string) => {
return await prisma.guild.findFirst({
where: { icon }
})
})
await deleteDeadUploadedFiles('messages', async (value: string) => {
return await prisma.message.findFirst({
where: { type: 'file', value }
})
})
await deleteDeadUploadedFiles('users', async (logo: string) => {
return await prisma.user.findFirst({
where: { logo }
})
})
}
main().catch((error) => {
console.error(error)
process.exit(1)
})

View File

@ -0,0 +1,77 @@
import httpErrors from 'http-errors'
import jwt from 'jsonwebtoken'
import { userExample } from '../../../models/User.js'
import { prismaMock } from '../../../__test__/setup.js'
import { getUserWithBearerToken } from '../authenticateUser.js'
const { Unauthorized, Forbidden, BadRequest } = httpErrors
describe('tools/plugins/authenticateUser - getUserWithBearerToken', () => {
afterEach(() => {
jest.clearAllMocks()
})
it('shoulds succeeds with the right information', async () => {
prismaMock.user.findUnique.mockResolvedValue(userExample)
const currentStrategy = 'local'
jwt.verify = jest.fn<any, any[]>((() => {
return { id: userExample.id, currentStrategy }
}) as any)
const userWithBearerToken = await getUserWithBearerToken('Bearer token')
expect(userWithBearerToken.current.id).toEqual(userExample.id)
expect(userWithBearerToken.current.name).toEqual(userExample.name)
expect(userWithBearerToken.accessToken).toEqual('token')
expect(userWithBearerToken.currentStrategy).toEqual(currentStrategy)
})
it('shoulds throws `Unauthorized` if `bearerToken` is not a string', async () => {
await expect(
async () => await getUserWithBearerToken(undefined)
).rejects.toThrow(Unauthorized)
})
it('shoulds throws `Unauthorized` if `bearerToken` is not to the right format: `"Bearer token"`', async () => {
await expect(
async () => await getUserWithBearerToken('Bearer')
).rejects.toThrow(Unauthorized)
await expect(async () => await getUserWithBearerToken('')).rejects.toThrow(
Unauthorized
)
await expect(
async () => await getUserWithBearerToken('Bearer token token2')
).rejects.toThrow(Unauthorized)
})
it('shoulds throws `Forbidden` if invalid `bearerToken` by `jwt.verify`', async () => {
jwt.verify = jest.fn<any, any[]>((() => {
throw new Error('Invalid token')
}) as any)
await expect(
async () => await getUserWithBearerToken('Bearer token')
).rejects.toThrow(Forbidden)
})
it("shoulds throws `Forbidden` if the user doesn't exist", async () => {
prismaMock.user.findUnique.mockResolvedValue(null)
jwt.verify = jest.fn<any, any[]>((() => {
return { id: userExample.id }
}) as any)
await expect(
async () => await getUserWithBearerToken('Bearer token')
).rejects.toThrow(Forbidden)
})
it('shoulds throws `BadRequest` if the user account is not confirmed', async () => {
prismaMock.user.findUnique.mockResolvedValue({
...userExample,
isConfirmed: false
})
jwt.verify = jest.fn<any, any[]>((() => {
return { id: userExample.id, currentStrategy: 'local' }
}) as any)
await expect(
async () => await getUserWithBearerToken('Bearer token')
).rejects.toThrow(BadRequest)
})
})