feat(scripts): add delete dead uploaded files
This commit is contained in:
parent
1bcee76324
commit
9e6bf25c83
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
695
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -3,13 +3,15 @@ import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
|
||||
|
||||
import prisma from '../tools/database/prisma.js'
|
||||
|
||||
jest.mock('nodemailer', () => ({
|
||||
createTransport: () => {
|
||||
return {
|
||||
sendMail: jest.fn(async () => {})
|
||||
jest.mock('nodemailer', () => {
|
||||
return {
|
||||
createTransport: () => {
|
||||
return {
|
||||
sendMail: jest.fn(async () => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
jest.mock('../tools/database/prisma.js', () => ({
|
||||
__esModule: true,
|
||||
|
58
src/scripts/delete-dead-uploaded-files.ts
Normal file
58
src/scripts/delete-dead-uploaded-files.ts
Normal 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)
|
||||
})
|
77
src/tools/plugins/__test__/authenticateUser.test.ts
Normal file
77
src/tools/plugins/__test__/authenticateUser.test.ts
Normal 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)
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user