diff --git a/src/components/edit/ImagesCard.jsx b/src/components/edit/ImagesCard.jsx index 372ab75..935d9fc 100644 --- a/src/components/edit/ImagesCard.jsx +++ b/src/components/edit/ImagesCard.jsx @@ -78,7 +78,7 @@ function ImagesCard ({ values, errors, handleChange, user }) { values.background = `${user}.png` - return fetch('/api/account/background-upload', { + return fetch('/api/account/background', { method: 'POST', body: formData }) @@ -94,7 +94,7 @@ function ImagesCard ({ values, errors, handleChange, user }) { Background Preview diff --git a/src/pages/api/account/background-upload.js b/src/pages/api/account/background-upload.js deleted file mode 100644 index e33e388..0000000 --- a/src/pages/api/account/background-upload.js +++ /dev/null @@ -1,106 +0,0 @@ -import { IncomingForm } from 'formidable'; -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; -import { ncWithSession } from '@/lib/routing'; -import HTTP_CODE from '@/lib/constants/httpStatusCodes'; -import { saveFile, saveFileBuffer } from '@/lib/utils/fileUtils' -import { CACHE } from '@/lib/constants/filePaths'; -import prisma from '@/lib/db'; -import { makeBanner } from '@/lib/riitag/banner'; -import logger from '@/lib/logger'; - -async function uploadBackground(request, response) { - if (request.socket.bytesRead > 2_107_638) { - return response - .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE) - .send({ error: 'Request entity too large.' }); - } - const username = request.session?.username; - - console.log('saving.. 1') - - if (!username) { - return response - .status(HTTP_CODE.UNAUTHORIZED) - .json({ error: 'Unauthorized' }); - } - - const data = await new Promise((resolve, reject) => { - const form = new IncomingForm(); - - form.parse(request, (error, fields, files) => { - if (error) { - return reject(error); - } - return resolve({ fields, files }); - }); - }).catch((error) => { - logger.error(error); - return response - .status(HTTP_CODE.BAD_REQUEST) - .send({ error: 'Invalid data' }); - }); - - const { file } = data.files; - - console.log('saving.. 2') - - if (file.mimetype !== 'image/png') { - return response - .status(HTTP_CODE.BAD_REQUEST) - .send({ error: 'Invalid data' }); - } - - // Hard cap of 2MBs for custom backgrounds - if (file.size > 2_000_000) { - return response - .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE) - .send({ error: 'Request entity too large.' }); - } - - console.log('saving.. 2.5') - - let user = await prisma.user.findFirst({ - where: { - username, - }, - select: { - username: true, - }, - }); - - const filepath = path.resolve(CACHE.BACKGROUNDS, `${user.username}.png`); - - console.log('saving.. 3') - - try { - await saveFileBuffer(filepath, await readFile(file.filepath)); - - user = await prisma.user.update({ - where: { - username, - }, - data: { - background: `${user.username}.png`, - }, - }); - } catch (error) { - logger.error(error); - return response - .status(HTTP_CODE.BAD_REQUEST) - .send({ error: 'Invalid data' }); - } - - await makeBanner(user); - return response.status(HTTP_CODE.OK).send(); -} - -const handler = ncWithSession().post(uploadBackground); - -export const config = { - api: { - bodyParser: false, - }, -}; - -export default handler; diff --git a/src/pages/api/account/background.ts b/src/pages/api/account/background.ts new file mode 100644 index 0000000..59b2445 --- /dev/null +++ b/src/pages/api/account/background.ts @@ -0,0 +1,112 @@ +import { IncomingForm, Fields, Files } from 'formidable' +import { readFile } from 'node:fs/promises' +import path from 'node:path' +import { ncWithSession } from '@/lib/routing' +import HTTP_CODE from '@/lib/constants/httpStatusCodes' +import { saveFileBuffer } from '@/lib/utils/fileUtils' +import { CACHE } from '@/lib/constants/filePaths' +import prisma from '@/lib/db' +import { makeBanner } from '@/lib/riitag/banner' +import logger from '@/lib/logger' +import { Request, Response } from 'express' +import { setFileHeaders } from '../../../lib/utils/utils' +import fs from 'node:fs' + +async function backgroundPost (request: Request, response: Response) { + if (request.socket.bytesRead > 2_107_638) { + return response + .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE) + .send({ error: 'Request entity too large.' }) + } + + // @ts-ignore + const username: string = request.session?.username + + if (!username) { + return response + .status(HTTP_CODE.UNAUTHORIZED) + .json({ error: 'Unauthorized' }) + } + + const data: unknown = await new Promise((resolve, reject): void => { + const form = new IncomingForm() + + form.parse(request, (error, fields: Fields, files: Files): void => { + if (error) return reject(error) + return resolve({ fields, files }) + }) + }) + .catch((error) => { + logger.error(error) + return response + .status(HTTP_CODE.BAD_REQUEST) + .send({ error: 'Invalid data' }) + }) + + // @ts-ignore + const { file } = data.files + + if (file.mimetype !== 'image/png') { + return response + .status(HTTP_CODE.BAD_REQUEST) + .send({ error: 'Invalid data' }) + } + + // Hard cap of 2MBs for custom backgrounds + if (file.size > 2_000_000) { + return response + .status(HTTP_CODE.REQUEST_ENTITY_TOO_LARGE) + .send({ error: 'Request entity too large.' }) + } + + let user: {username: string} = await prisma.user.findFirst({ + where: { + username + }, + select: { + username: true + } + }) + + const filepath: string = path.resolve(CACHE.BACKGROUNDS, `${user.username}.png`) + await saveFileBuffer(filepath, await readFile(file.filepath)) + + user = await prisma.user.update({ + where: { + username + }, + data: { + background: `${user.username}.png` + } + }) + + await makeBanner(user) + return response.status(HTTP_CODE.OK).send() +} + +async function backgroundGet (request: Request, response: Response) { + // @ts-ignore + const username = request.session?.username + + if (!username) { + return response + .status(HTTP_CODE.UNAUTHORIZED) + .json({ error: 'Unauthorized' }) + } + + response.setHeader('Content-Type', 'image/png') + setFileHeaders(response, `${username}.png`) + return response + .status(HTTP_CODE.OK) + .send(await fs.promises.readFile(path.resolve(CACHE.BACKGROUNDS, username + '.png'))) +} + +const handler = ncWithSession().post(backgroundPost).get(backgroundGet) + +export const config = { + api: { + bodyParser: false + } +} + +export default handler diff --git a/src/pages/api/account/uploaded-background.js b/src/pages/api/account/uploaded-background.js index 0461248..093a648 100644 --- a/src/pages/api/account/uploaded-background.js +++ b/src/pages/api/account/uploaded-background.js @@ -6,19 +6,7 @@ import { setFileHeaders } from '@/lib/utils/utils'; import { CACHE } from '@/lib/constants/filePaths'; async function getMyUploadedBackground(request, response) { - const username = request.session?.username; - if (!username) { - return response - .status(HTTP_CODE.UNAUTHORIZED) - .json({ error: 'Unauthorized' }); - } - - response.setHeader('Content-Type', 'image/png'); - setFileHeaders(response, `${username}.png`); - return response - .status(HTTP_CODE.OK) - .send(await fs.promises.readFile(path.resolve(CACHE.BACKGROUNDS, username + ".png"))); } const handler = ncWithSession().get(getMyUploadedBackground); diff --git a/src/pages/edit.jsx b/src/pages/edit.jsx index fdce3be..125d105 100644 --- a/src/pages/edit.jsx +++ b/src/pages/edit.jsx @@ -155,12 +155,12 @@ function EditPage ({ tagInfo, user, language }) { render ({ data, toastProps }) { if (data.status !== 200) { toastProps.type = 'error' - return 'An error occured, please try again later' + return 'An error occurred, please try again later' } return 'Saved!' } }, - error: 'An error occured, please try again later.' + error: 'An error occurred, please try again later.' } ) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d55eb30 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "module": "esnext", + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +}