Skip to content

Commit

Permalink
Allow your own backgrounds to be uploaded
Browse files Browse the repository at this point in the history
  • Loading branch information
larsenv committed Oct 19, 2023
1 parent 3bd142f commit 96e304b
Show file tree
Hide file tree
Showing 11 changed files with 2,961 additions and 1,743 deletions.
4,475 changes: 2,770 additions & 1,705 deletions package-lock.json

Large diffs are not rendered by default.

50 changes: 25 additions & 25 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,28 @@ model sys {
}

model user {
id Int @id @default(autoincrement())
username String @unique @db.VarChar(50)
role String @default("user") @db.VarChar(25)
name_on_riitag String? @db.VarChar(255)
image String? @db.VarChar(255)
randkey String? @unique @db.VarChar(200)
coins Int @default(0) @db.UnsignedInt
cover_region String @default("EN") @db.VarChar(6)
cover_type String @default("cover3D") @db.VarChar(10)
comment String? @db.VarChar(50)
overlay String @default("overlay1") @db.VarChar(20)
background String @default("riiconnect241.png") @db.VarChar(120)
flag String @default("rc24") @db.VarChar(20)
coin String @default("mario") @db.VarChar(20)
font String @default("default") @db.VarChar(50)
show_avatar Boolean @default(false)
show_mii Boolean @default(false)
mii_type String @default("guest") @db.VarChar(10)
mii_data String? @db.VarChar(8192)
cmoc_entry_no String? @db.VarChar(12)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
accounts account[]
playlog playlog[]
}
id Int @id @default(autoincrement())
username String @unique @db.VarChar(50)
role String @default("user") @db.VarChar(25)
name_on_riitag String? @db.VarChar(255)
image String? @db.VarChar(255)
randkey String? @unique @db.VarChar(200)
coins Int @default(0) @db.UnsignedInt
cover_region String @default("EN") @db.VarChar(6)
cover_type String @default("cover3D") @db.VarChar(10)
comment String? @db.VarChar(50)
overlay String @default("overlay1") @db.VarChar(20)
background String @default("riiconnect241.png") @db.VarChar(120)
flag String @default("rc24") @db.VarChar(20)
coin String @default("mario") @db.VarChar(20)
font String @default("default") @db.VarChar(50)
show_avatar Boolean @default(false)
show_mii Boolean @default(false)
mii_type String @default("guest") @db.VarChar(10)
mii_data String? @db.VarChar(8192)
cmoc_entry_no String? @db.VarChar(12)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
accounts account[]
playlog playlog[]
}
29 changes: 27 additions & 2 deletions src/components/edit/ImagesCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const backgrounds = BACKGROUNDS.map((background) => ({
label: background,
}));

function ImagesCard({ values, errors, handleChange }) {
function ImagesCard({ values, errors, handleChange, username }) {
return (
<Card className="mb-3" bg="secondary" text="white">
<Card.Header as="h5">Images</Card.Header>
Expand Down Expand Up @@ -67,12 +67,37 @@ function ImagesCard({ values, errors, handleChange }) {
{errors.background}
</Alert>
)}
<br></br>
<br></br>
<p>Upload Background</p>
<Form.Control
id="fileInput"
accept=".png"
name="file"
type="file"
onChange={(event) => {
const formData = new FormData();
formData.append('file', event.currentTarget.files[0]);

values.background = username + ".png";

return fetch('/api/account/background-upload', {
method: 'POST',
body: formData,
});
}}
/>
<p>
<small className="text-muted">
Please ensure that your image is 1200x450 and is in PNG format.
</small>
</p>
</Col>
<Col md={7}>
<img
alt="Background Preview"
className="img-thumbnail mx-auto d-block"
src={`/img/background/${values.background}`}
src={!Number.isNaN(Number(values.background.replace(/.*\//, '').replace(/\.png$/, ''))) ? `api/account/uploaded-background` : `/img/background/${values.background}`}
/>
</Col>
</Row>
Expand Down
2 changes: 1 addition & 1 deletion src/components/edit/SelectModal.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { Button, Card, Col, Modal, Row } from 'react-bootstrap';
import { Button, Card, Col, Modal, Row, Form } from 'react-bootstrap';
import { useState } from 'react';
import { Field } from 'formik';

Expand Down
1 change: 1 addition & 0 deletions src/lib/constants/filePaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const CACHE = Object.freeze({
MIIS: path.resolve(CACHE_PATH, 'mii', 'user'),
TAGS: path.resolve(CACHE_PATH, 'tags'),
WADS: path.resolve(CACHE_PATH, 'wads'),
BACKGROUNDS: path.resolve(CACHE_PATH, 'user_backgrounds')
});

export const PUBLIC = Object.freeze({
Expand Down
3 changes: 2 additions & 1 deletion src/lib/riitag/banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ export async function makeBanner(user) {
const context = canvas.getContext('2d');

// Background image
const bgPath = path.resolve(PUBLIC.BACKGROUND, user.background);
const bgPath = path.resolve(!Number.isNaN(Number(user.background.replace(/.*\//, '').replace(/\.png$/, ''))) ? CACHE.BACKGROUNDS : PUBLIC.BACKGROUND, user.background);
console.log(bgPath);
if (!(await exists(bgPath))) {
throw new Error(`Background ${user.background} does not exist`);
}
Expand Down
13 changes: 8 additions & 5 deletions src/lib/utils/fileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ export async function saveFile(filepath, file) {
await fs.promises.mkdir(path.dirname(filepath), { recursive: true });
}

console.log(file)

const fileStream = fs.createWriteStream(filepath);

await new Promise((resolve, reject) => {
file.pipe(fileStream);
file.on('error', reject);
fileStream.on('finish', resolve);
});
try {
await fs.promises.writeFile(filepath, file);
logger.info('File saved successfully');
} catch (error) {
logger.error('Error saving the file:', error);
}
}
99 changes: 99 additions & 0 deletions src/pages/api/account/background-upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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 } 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;

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;

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 = await prisma.user.findFirst({
where: {
username,
},
select: {
username: true,
},
});

const filepath = path.resolve(CACHE.BACKGROUNDS, `${user.username}.png`);

try {
await saveFile(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' });
}

makeBanner(user);

return response.status(HTTP_CODE.OK).send();
}

const handler = ncWithSession().post(uploadBackground);

export const config = {
api: {
bodyParser: false,
},
};

export default handler;
2 changes: 0 additions & 2 deletions src/pages/api/account/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ async function updateTagSettings(request, response) {
isBlank(coverRegion) ||
isBlank(coverType) ||
isBlank(overlay) ||
isBlank(background) ||
isBlank(flag) ||
isBlank(coin) ||
isBlank(font) ||
Expand All @@ -48,7 +47,6 @@ async function updateTagSettings(request, response) {
!isValidCoverType(coverType) ||
!isValidCoverRegion(coverRegion) ||
!isValidOverlay(overlay) ||
BACKGROUNDS.includes(background) === false ||
!isValidFlag(flag) ||
!isValidCoin(coin) ||
!isValidFont(font) ||
Expand Down
26 changes: 26 additions & 0 deletions src/pages/api/account/uploaded-background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import fs from 'node:fs';
import path from 'node:path';
import { ncWithSession } from '@/lib/routing';
import HTTP_CODE from '@/lib/constants/httpStatusCodes';
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);

export default handler;
4 changes: 2 additions & 2 deletions src/pages/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const getServerSideProps = withSession(async ({ req }) => {
username,
},
select: {
username: true,
name_on_riitag: true,
cover_region: true,
cover_type: true,
Expand Down Expand Up @@ -102,8 +103,6 @@ function EditPage({ tagInfo }) {

if (!values.background) {
errors.background = 'Required';
} else if (BACKGROUNDS.includes(values.background) === false) {
errors.background = 'Invalid Background';
}

if (!values.flag) {
Expand Down Expand Up @@ -194,6 +193,7 @@ function EditPage({ tagInfo }) {
values={values}
errors={errors}
handleChange={handleChange}
username={tagInfo.username}
/>
</Col>
</Row>
Expand Down

0 comments on commit 96e304b

Please sign in to comment.