From 6a5a85c7730a10899658a8fd16aa2b6b58fe257e Mon Sep 17 00:00:00 2001 From: MeowcaTheoRange Date: Fri, 30 Jun 2023 20:35:15 -0500 Subject: [PATCH] permissions! --- .prettierrc | 2 +- src/lib/db/crud.ts | 18 +- src/lib/trollcall/convert/flair.ts | 4 +- src/lib/trollcall/convert/troll.ts | 49 +++- src/lib/trollcall/convert/user.ts | 18 +- src/lib/trollcall/flair.ts | 12 +- src/lib/trollcall/perms.ts | 27 +++ src/lib/trollcall/troll.ts | 73 +++++- src/lib/trollcall/user.ts | 45 +++- src/lib/trollcall/utility/merge.ts | 8 +- src/pages/_app.tsx | 1 - src/pages/_error.tsx | 3 + src/pages/api/troll/.../[[...page]]/index.ts | 21 ++ .../api/troll/[user]/.../[[...page]]/index.ts | 28 +++ src/pages/api/troll/[user]/[troll]/index.ts | 73 ++++++ src/pages/api/troll/index.ts | 42 ++++ src/pages/api/user/.../[[...page]]/index.ts | 21 ++ src/pages/api/user/[user]/index.ts | 45 +++- .../api/user/[user]/troll/[troll]/index.ts | 24 -- src/pages/api/user/index.ts | 62 +++-- src/pages/index.tsx | 114 +-------- src/permissions.ts | 24 ++ src/styles/Home.module.css | 229 ------------------ src/styles/globals.css | 107 -------- src/types/assist/color.ts | 178 +++++++------- src/types/assist/extended_zodiac.ts | 4 +- src/types/assist/generics.ts | 6 +- src/types/assist/language.ts | 56 ++--- src/types/assist/mongo.ts | 2 +- src/types/client/quirks.ts | 13 +- src/types/client/troll.ts | 31 ++- src/types/client/user.ts | 6 +- src/types/quirks.ts | 3 +- 33 files changed, 683 insertions(+), 666 deletions(-) create mode 100644 src/lib/trollcall/perms.ts create mode 100644 src/pages/_error.tsx create mode 100644 src/pages/api/troll/.../[[...page]]/index.ts create mode 100644 src/pages/api/troll/[user]/.../[[...page]]/index.ts create mode 100644 src/pages/api/troll/[user]/[troll]/index.ts create mode 100644 src/pages/api/troll/index.ts create mode 100644 src/pages/api/user/.../[[...page]]/index.ts delete mode 100644 src/pages/api/user/[user]/troll/[troll]/index.ts create mode 100644 src/permissions.ts delete mode 100644 src/styles/Home.module.css delete mode 100644 src/styles/globals.css diff --git a/.prettierrc b/.prettierrc index 5e17cae..eddd65c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,7 +2,7 @@ "trailingComma": "none", "tabWidth": 4, "useTabs": false, - "printWidth": 120, + "printWidth": 80, "semi": true, "singleQuote": false, "quoteProps": "preserve", diff --git a/src/lib/db/crud.ts b/src/lib/db/crud.ts index 44d21cf..739516e 100644 --- a/src/lib/db/crud.ts +++ b/src/lib/db/crud.ts @@ -1,14 +1,15 @@ -import { FindCursor, Filter as MDBFilter, ObjectId } from "mongodb"; +import { FindCursor, Filter as MDBFilter, ObjectId, Sort } from "mongodb"; import { mainDB } from "./mongodb"; // Functionally identical to the MongoDB type functions, but this is here if you want to extend TrollCall to another NoSQL database. +// That would be hard, though, as some parts of the codebase touch raw MongoDB concepts. export type WithId = Omit & { _id: ObjectId; }; export type Document = { [key: string]: any }; -export type Filter = MDBFilter; +export type Filter = MDBFilter; export async function createOne(collection: string, doc: any) { const selectedCollection = mainDB.collection(collection); @@ -20,9 +21,13 @@ export async function readOne(collection: string, find: Filter) { return await selectedCollection.findOne(find); } // MongoDB and its finicky type safety -export function readMany(collection: string, find: Filter) { +export function readMany( + collection: string, + find: Filter, + sort: Sort +) { const selectedCollection = mainDB.collection(collection); - return selectedCollection.find(find); + return selectedCollection.find(find).sort(sort); } export async function countMany(collection: string, find: any) { @@ -45,7 +50,10 @@ export async function deleteOne(collection: string, find: any) { return await selectedCollection.findOneAndDelete(find); } -export async function cursorToArray(cursor: FindCursor, func: (input: any) => any = x => x) { +export async function cursorToArray( + cursor: FindCursor, + func: (input: any) => any = x => x +) { let array: T[] = []; while (await cursor.hasNext()) { array.push(await func(await cursor.next())); diff --git a/src/lib/trollcall/convert/flair.ts b/src/lib/trollcall/convert/flair.ts index 5f050fd..0801b67 100644 --- a/src/lib/trollcall/convert/flair.ts +++ b/src/lib/trollcall/convert/flair.ts @@ -1,7 +1,9 @@ import { ClientFlair, ServerFlair } from "@/types/flair"; import { sanitize } from "../utility/merge"; -export async function ServerFlairToClientFlair(serverFlair: ServerFlair): Promise { +export async function ServerFlairToClientFlair( + serverFlair: ServerFlair +): Promise { const sanitizedFlair = sanitize(serverFlair); let clientFlair: ClientFlair = { ...sanitizedFlair diff --git a/src/lib/trollcall/convert/troll.ts b/src/lib/trollcall/convert/troll.ts index ec16573..ced2ee1 100644 --- a/src/lib/trollcall/convert/troll.ts +++ b/src/lib/trollcall/convert/troll.ts @@ -1,23 +1,56 @@ import { Class, TrueSign } from "@/types/assist/extended_zodiac"; +import { SubmitTroll } from "@/types/client/troll"; import { ClientTroll, ServerTroll } from "@/types/troll"; import { getManyFlairs } from "../flair"; -import { getManyUsers } from "../user"; -import { cutArray, sanitize } from "../utility/merge"; +import { cutArray, cutObject, sanitize } from "../utility/merge"; import { ServerFlairToClientFlair } from "./flair"; -import { ServerUserToClientUser } from "./user"; -export async function ServerTrollToClientTroll(serverTroll: ServerTroll): Promise { +export async function ServerTrollToClientTroll( + serverTroll: ServerTroll +): Promise { const sanitizedTroll = sanitize(serverTroll); - const owners = await getManyUsers({ _id: { $in: serverTroll.owners } }, ServerUserToClientUser); - const flairs = await getManyFlairs({ _id: { $in: serverTroll.flairs } }, ServerFlairToClientFlair); + const flairs = await getManyFlairs( + { _id: { $in: serverTroll.flairs } }, + ServerFlairToClientFlair + ); let clientTroll: ClientTroll = { ...sanitizedTroll, trueSign: TrueSign[serverTroll.trueSign], - falseSign: serverTroll.falseSign != null ? TrueSign[serverTroll.falseSign] : null, + falseSign: + serverTroll.falseSign != null + ? TrueSign[serverTroll.falseSign] + : null, class: Class[serverTroll.class], - owners: cutArray(owners), + owners: [], flairs: cutArray(flairs) }; return clientTroll; } + +export function SubmitTrollToServerTroll( + submitTroll: Partial +): Omit, "_id"> { + let serverTroll: Omit, "_id"> = { + ...submitTroll, + quirks: submitTroll.quirks + ? Object.fromEntries(submitTroll.quirks) + : undefined, + owners: [], + flairs: [], + updatedDate: new Date() + }; + return serverTroll; +} + +export function MergeServerTrolls( + submitTroll: ServerTroll, + merge: Partial> +): ServerTroll { + let serverTroll: ServerTroll = { + ...submitTroll, + ...cutObject(merge), + updatedDate: new Date() + }; + return serverTroll; +} diff --git a/src/lib/trollcall/convert/user.ts b/src/lib/trollcall/convert/user.ts index 510b004..da08705 100644 --- a/src/lib/trollcall/convert/user.ts +++ b/src/lib/trollcall/convert/user.ts @@ -6,9 +6,14 @@ import { getManyFlairs } from "../flair"; import { cutArray, cutObject, removeCode, sanitize } from "../utility/merge"; import { ServerFlairToClientFlair } from "./flair"; -export async function ServerUserToClientUser(serverUser: ServerUser): Promise { +export async function ServerUserToClientUser( + serverUser: ServerUser +): Promise { const sanitizedUser = removeCode(sanitize(serverUser)); - const flairs = await getManyFlairs({ _id: { $in: serverUser.flairs } }, ServerFlairToClientFlair); + const flairs = await getManyFlairs( + { _id: { $in: serverUser.flairs } }, + ServerFlairToClientFlair + ); let clientUser: ClientUser = { ...sanitizedUser, trueSign: TrueSign[serverUser.trueSign], @@ -18,7 +23,9 @@ export async function ServerUserToClientUser(serverUser: ServerUser): Promise): Omit, "_id"> { +export function SubmitUserToServerUser( + submitUser: Partial +): Omit, "_id"> { let serverUser: Omit, "_id"> = { ...submitUser, flairs: [], @@ -28,7 +35,10 @@ export function SubmitUserToServerUser(submitUser: Partial): Omit>): ServerUser { +export function MergeServerUsers( + submitUser: ServerUser, + merge: Partial> +): ServerUser { let serverUser: ServerUser = { ...submitUser, ...cutObject(merge), diff --git a/src/lib/trollcall/flair.ts b/src/lib/trollcall/flair.ts index 9a3efb4..3fb521c 100644 --- a/src/lib/trollcall/flair.ts +++ b/src/lib/trollcall/flair.ts @@ -1,13 +1,18 @@ import { ServerFlair } from "@/types/flair"; +import { Sort } from "mongodb"; import { Filter, cursorToArray, readMany, readOne } from "../db/crud"; +const FlairSort: Sort = { updatedDate: -1, _id: -1 }; + /** * A function that returns one ServerFlairs from the database. * @param query A partial Find query. Can contain an ID. * @returns A ServerFlair. */ -export async function getSingleFlair(query: Filter): Promise { +export async function getSingleFlair( + query: Filter +): Promise { const flair = (await readOne("flairs", query)) as ServerFlair | null; return flair; } @@ -23,6 +28,9 @@ export async function getManyFlairs( query: Filter, func?: (input: any) => any ): Promise<(ServerFlair | null)[]> { - const flair = (await cursorToArray(readMany("flairs", query), func)) as (ServerFlair | null)[]; + const flair = (await cursorToArray( + readMany("flairs", query, FlairSort), + func + )) as (ServerFlair | null)[]; return flair; } diff --git a/src/lib/trollcall/perms.ts b/src/lib/trollcall/perms.ts new file mode 100644 index 0000000..6d9d6d1 --- /dev/null +++ b/src/lib/trollcall/perms.ts @@ -0,0 +1,27 @@ +import { Levels, Permissions } from "@/permissions"; +import { ServerUser } from "@/types/user"; + +export function getLevel(user: ServerUser) { + let highestLevel = "USER"; + for (let level of Permissions) { + if (user.flairs.some(oid => level.values.includes(oid.toString()))) + highestLevel = level.name; + } + return highestLevel; +} + +export function compareLevels(level: string, compareLevel: string) { + return Levels[level] > Levels[compareLevel]; +} + +export function compareCredentials( + user: ServerUser, + cookies: Partial<{ + [key: string]: string; + }> +) { + return ( + user.code === cookies.TROLLCALL_CODE && + user.name === cookies.TROLLCALL_NAME + ); +} diff --git a/src/lib/trollcall/troll.ts b/src/lib/trollcall/troll.ts index 9f68b73..39b9699 100644 --- a/src/lib/trollcall/troll.ts +++ b/src/lib/trollcall/troll.ts @@ -1,5 +1,15 @@ import { ServerTroll } from "@/types/troll"; -import { Filter, cursorToArray, readMany, readOne } from "../db/crud"; +import { Sort } from "mongodb"; +import { + Filter, + createOne, + cursorToArray, + readMany, + readOne, + replaceOne +} from "../db/crud"; + +const TrollSort: Sort = { updatedDate: -1, _id: -1 }; /** * A function that returns one ServerTrolls from the database. @@ -7,7 +17,9 @@ import { Filter, cursorToArray, readMany, readOne } from "../db/crud"; * @returns A ServerTroll. */ -export async function getSingleTroll(query: Filter): Promise { +export async function getSingleTroll( + query: Filter +): Promise { const troll = (await readOne("trolls", query)) as ServerTroll | null; return troll; } @@ -19,10 +31,59 @@ export async function getSingleTroll(query: Filter): Promise( query: Filter, - func?: (input: any) => any -): Promise<(ServerTroll | null)[]> { - const troll = (await cursorToArray(readMany("trolls", query), func)) as (ServerTroll | null)[]; + func?: (input: any) => T +): Promise<(Awaited | null)[]> { + const troll = (await cursorToArray( + readMany("trolls", query, TrollSort), + func + )) as (Awaited | null)[]; return troll; } + +/** + * A function that returns many ServerTrolls from the database using a FindCursor, limited by count. + * @param query A partial Find query. Can contain an ID. + * @param func A function to run on every ServerTroll returned. Helps reduce loops. + * @returns An array of ServerTrolls. + */ + +export async function getManyPagedTrolls( + query: Filter, + func?: (input: any) => T, + count: number = 5, + page: number = 0 +): Promise<(Awaited | null)[]> { + const find = readMany("trolls", query, TrollSort) + .limit(count) + .skip(page * count); + const user = (await cursorToArray(find, func)) as (Awaited | null)[]; + return user; +} + +/** + * A function that puts one ServerTroll into the database. + * @param troll A ServerTroll. + * @returns A ServerTroll, or null, depending on if the operation succeeded. + */ + +export async function createTroll( + troll: Omit +): Promise | null> { + const newTroll = await createOne("trolls", troll); + return newTroll.acknowledged ? troll : null; +} + +/** + * A function that changes one database troll with the given params. + * @param troll A ServerTroll. + * @returns A ServerTroll, or null, depending on if the operation succeeded. + */ + +export async function changeTroll( + troll: ServerTroll +): Promise { + const newTroll = await replaceOne("trolls", { _id: troll._id }, troll); + return newTroll.acknowledged ? troll : null; +} diff --git a/src/lib/trollcall/user.ts b/src/lib/trollcall/user.ts index 1a13ddd..1d92c67 100644 --- a/src/lib/trollcall/user.ts +++ b/src/lib/trollcall/user.ts @@ -1,5 +1,15 @@ import { ServerUser } from "@/types/user"; -import { Filter, createOne, cursorToArray, readMany, readOne, replaceOne } from "../db/crud"; +import { Sort } from "mongodb"; +import { + Filter, + createOne, + cursorToArray, + readMany, + readOne, + replaceOne +} from "../db/crud"; + +const UserSort: Sort = { updatedDate: -1, _id: -1 }; /** * A function that returns one ServerUser from the database. @@ -7,7 +17,9 @@ import { Filter, createOne, cursorToArray, readMany, readOne, replaceOne } from * @returns A ServerUser. */ -export async function getSingleUser(query: Filter): Promise { +export async function getSingleUser( + query: Filter +): Promise { const user = (await readOne("users", query)) as ServerUser | null; return user; } @@ -23,7 +35,30 @@ export async function getManyUsers( query: Filter, func?: (input: any) => T ): Promise<(Awaited | null)[]> { - const user = (await cursorToArray(readMany("users", query), func)) as (Awaited | null)[]; + const user = (await cursorToArray( + readMany("users", query, UserSort), + func + )) as (Awaited | null)[]; + return user; +} + +/** + * A function that returns many ServerUsers from the database using a FindCursor, limited by count. + * @param query A partial Find query. Can contain an ID. + * @param func A function to run on every ServerUser returned. Helps reduce loops. + * @returns An array of ServerUsers. + */ + +export async function getManyPagedUsers( + query: Filter, + func?: (input: any) => T, + count: number = 5, + page: number = 0 +): Promise<(Awaited | null)[]> { + const find = readMany("users", query, UserSort) + .limit(count) + .skip(page * count); + const user = (await cursorToArray(find, func)) as (Awaited | null)[]; return user; } @@ -33,7 +68,9 @@ export async function getManyUsers( * @returns A ServerUser, or null, depending on if the operation succeeded. */ -export async function createUser(user: Omit): Promise | null> { +export async function createUser( + user: Omit +): Promise | null> { const newUser = await createOne("users", user); return newUser.acknowledged ? user : null; } diff --git a/src/lib/trollcall/utility/merge.ts b/src/lib/trollcall/utility/merge.ts index f8b5942..4e76ef3 100644 --- a/src/lib/trollcall/utility/merge.ts +++ b/src/lib/trollcall/utility/merge.ts @@ -25,13 +25,17 @@ export function cutObject(object: T) { return cut as T; } -export function sanitize>(serverType: Type): Omit { +export function sanitize>( + serverType: Type +): Omit { const sanitized: Partial = serverType; delete sanitized._id; return sanitized as Omit; } -export function removeCode(serverType: Type): Omit { +export function removeCode( + serverType: Type +): Omit { const sanitized: Partial = serverType; delete sanitized.code; return sanitized as Omit; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 4446905..1fa2d52 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,3 @@ -import "@/styles/globals.css"; import type { AppProps } from "next/app"; import React from "react"; diff --git a/src/pages/_error.tsx b/src/pages/_error.tsx new file mode 100644 index 0000000..55b1990 --- /dev/null +++ b/src/pages/_error.tsx @@ -0,0 +1,3 @@ +export default function ErrorPage() { + return "404"; +} diff --git a/src/pages/api/troll/.../[[...page]]/index.ts b/src/pages/api/troll/.../[[...page]]/index.ts new file mode 100644 index 0000000..c8e60bf --- /dev/null +++ b/src/pages/api/troll/.../[[...page]]/index.ts @@ -0,0 +1,21 @@ +import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll"; +import { getManyPagedTrolls } from "@/lib/trollcall/troll"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { method, query } = req; + const page = query.page ? query.page[0] : 0; + if (method === "GET") { + const trolls = await getManyPagedTrolls( + {}, + ServerTrollToClientTroll, + 5, + page + ); + if (trolls == null) return res.status(404).end(); + res.json(trolls); + } else return res.status(405).end(); +} diff --git a/src/pages/api/troll/[user]/.../[[...page]]/index.ts b/src/pages/api/troll/[user]/.../[[...page]]/index.ts new file mode 100644 index 0000000..3f961fd --- /dev/null +++ b/src/pages/api/troll/[user]/.../[[...page]]/index.ts @@ -0,0 +1,28 @@ +import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll"; +import { getManyPagedTrolls } from "@/lib/trollcall/troll"; +import { getSingleUser } from "@/lib/trollcall/user"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { method, query } = req; + const page = query.page ? query.page[0] : 0; + if (method === "GET") { + const user = await getSingleUser({ + name: query.user + }); + if (user == null) return res.status(404).end(); + const trolls = await getManyPagedTrolls( + { + "owners.0": user._id + }, + ServerTrollToClientTroll, + 5, + page + ); + if (trolls == null) return res.status(404).end(); + res.json(trolls); + } else return res.status(405).end(); +} diff --git a/src/pages/api/troll/[user]/[troll]/index.ts b/src/pages/api/troll/[user]/[troll]/index.ts new file mode 100644 index 0000000..2721e6f --- /dev/null +++ b/src/pages/api/troll/[user]/[troll]/index.ts @@ -0,0 +1,73 @@ +import { + MergeServerTrolls, + ServerTrollToClientTroll, + SubmitTrollToServerTroll +} from "@/lib/trollcall/convert/troll"; +import { + compareCredentials, + compareLevels, + getLevel +} from "@/lib/trollcall/perms"; +import { changeTroll, getSingleTroll } from "@/lib/trollcall/troll"; +import { getSingleUser } from "@/lib/trollcall/user"; +import { SubmitTroll } from "@/types/client/troll"; +import { PartialUserSchema } from "@/types/client/user"; +import { Router } from "express"; +import { NextApiRequest, NextApiResponse } from "next"; + +export const trollRouter = Router(); + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { query, cookies, method, body } = req; + if (method === "GET") { + const user = await getSingleUser({ + name: query.user + }); + if (user == null) return res.status(404).end(); + const troll = await getSingleTroll({ + "name.0": query.troll, + "owners.0": user._id + }); + if (troll == null) return res.status(404).end(); + const serverTroll = await ServerTrollToClientTroll(troll); + res.json(serverTroll); + } else if (method === "PUT") { + let validatedTroll: Partial; + try { + validatedTroll = (await PartialUserSchema.validate( + body + )) as Partial; + } catch (err) { + return res.status(400).send(err); + } + const checkUser = await getSingleUser({ + name: query.user + }); + if (checkUser == null) return res.status(404).end(); + if (!compareCredentials(checkUser, cookies)) { + const thisUser = await getSingleUser({ + name: query.user + }); + if (thisUser == null || !compareCredentials(thisUser, cookies)) + return res.status(403).end(); + if (!compareLevels(getLevel(thisUser), "MODERATOR")) + return res.status(403).end(); + } + const editingTroll = await getSingleTroll({ + "name.0": query.troll, + "owners": checkUser._id + }); + if (editingTroll == null) return res.status(404).end(); + const serverTroll = SubmitTrollToServerTroll(validatedTroll); + const bothTrolls = MergeServerTrolls(editingTroll, serverTroll); + const newTroll = await changeTroll(bothTrolls); + if (newTroll == null) return res.status(503).end(); + res.json(newTroll); + } else return res.status(405).end(); +} + +// 1119731948118605995 +// 1089603977131331584 diff --git a/src/pages/api/troll/index.ts b/src/pages/api/troll/index.ts new file mode 100644 index 0000000..a5b7e28 --- /dev/null +++ b/src/pages/api/troll/index.ts @@ -0,0 +1,42 @@ +import { SubmitTrollToServerTroll } from "@/lib/trollcall/convert/troll"; +import { compareCredentials } from "@/lib/trollcall/perms"; +import { createTroll, getSingleTroll } from "@/lib/trollcall/troll"; +import { getSingleUser } from "@/lib/trollcall/user"; +import { SubmitTrollSchema } from "@/types/client/troll"; +import { ServerTroll } from "@/types/troll"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { body, method, cookies } = req; + if (method === "POST") { + let validatedTroll; + try { + validatedTroll = await SubmitTrollSchema.validate(body); + } catch (err) { + return res.status(400).send(err); + } + const checkUser = await getSingleUser({ + name: cookies.TROLLCALL_NAME + }); + if (checkUser == null) return res.status(404).end(); + if (!compareCredentials(checkUser, cookies)) + return res.status(403).end(); + const checkExistingTroll = await getSingleTroll({ + "name.0": validatedTroll.name[0], + "owners": checkUser._id + }); + if (checkExistingTroll != null) return res.status(409).end(); + // we are sure this object is full, so cast partial + let serverTroll = SubmitTrollToServerTroll(validatedTroll) as Omit< + ServerTroll, + "_id" + >; + serverTroll.owners[0] = checkUser._id; + const newTroll = await createTroll(serverTroll); + if (newTroll == null) return res.status(503).end(); + res.json(newTroll); + } else return res.status(405).end(); +} diff --git a/src/pages/api/user/.../[[...page]]/index.ts b/src/pages/api/user/.../[[...page]]/index.ts new file mode 100644 index 0000000..6af1929 --- /dev/null +++ b/src/pages/api/user/.../[[...page]]/index.ts @@ -0,0 +1,21 @@ +import { ServerUserToClientUser } from "@/lib/trollcall/convert/user"; +import { getManyPagedUsers } from "@/lib/trollcall/user"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { method, query } = req; + const page = query.page ? query.page[0] : 0; + if (method === "GET") { + const users = await getManyPagedUsers( + {}, + ServerUserToClientUser, + 5, + page + ); + if (users == null) return res.status(404).end(); + res.json(users); + } else return res.status(405).end(); +} diff --git a/src/pages/api/user/[user]/index.ts b/src/pages/api/user/[user]/index.ts index 74b1c61..809b2b8 100644 --- a/src/pages/api/user/[user]/index.ts +++ b/src/pages/api/user/[user]/index.ts @@ -1,10 +1,22 @@ -import { MergeServerUsers, ServerUserToClientUser, SubmitUserToServerUser } from "@/lib/trollcall/convert/user"; +import { + MergeServerUsers, + ServerUserToClientUser, + SubmitUserToServerUser +} from "@/lib/trollcall/convert/user"; +import { + compareCredentials, + compareLevels, + getLevel +} from "@/lib/trollcall/perms"; import { changeUser, getSingleUser } from "@/lib/trollcall/user"; import { PartialUserSchema, SubmitUser } from "@/types/client/user"; import { serialize } from "cookie"; import { NextApiRequest, NextApiResponse } from "next"; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { const { body, cookies, query, method } = req; if (method === "GET") { const user = await getSingleUser({ @@ -16,7 +28,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } else if (method === "PUT") { let validatedUser: Partial; try { - validatedUser = (await PartialUserSchema.validate(body)) as Partial; + validatedUser = (await PartialUserSchema.validate( + body + )) as Partial; } catch (err) { return res.status(400).send(err); } @@ -24,16 +38,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) name: query.user }); if (checkExistingUser == null) return res.status(404).end(); - if (checkExistingUser.code !== cookies.TROLLCALL_CODE || checkExistingUser.name !== cookies.TROLLCALL_NAME) - return res.status(403).end(); + if (!compareCredentials(checkExistingUser, cookies)) { + const thisUser = await getSingleUser({ + name: query.user + }); + if (thisUser == null || !compareCredentials(thisUser, cookies)) + return res.status(403).end(); + if (!compareLevels(getLevel(thisUser), "MODERATOR")) + return res.status(403).end(); + } const serverUser = SubmitUserToServerUser(validatedUser); const bothUsers = MergeServerUsers(checkExistingUser, serverUser); const newUser = await changeUser(bothUsers); if (newUser == null) return res.status(503).end(); // Give cookies, redundant style res.setHeader("Set-Cookie", [ - serialize("TROLLCALL_NAME", newUser.name, { path: "/", maxAge: 31540000 }), - serialize("TROLLCALL_NAME", newUser.code, { path: "/", maxAge: 31540000 }) + serialize("TROLLCALL_NAME", newUser.name, { + path: "/", + maxAge: 31540000 + }), + serialize("TROLLCALL_CODE", newUser.code, { + path: "/", + maxAge: 31540000 + }), + serialize("TROLLCALL_PFP", newUser.pfp ?? "", { + path: "/", + maxAge: 31540000 + }) ]).json(newUser); } else return res.status(405).end(); } diff --git a/src/pages/api/user/[user]/troll/[troll]/index.ts b/src/pages/api/user/[user]/troll/[troll]/index.ts deleted file mode 100644 index 9e01f64..0000000 --- a/src/pages/api/user/[user]/troll/[troll]/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ServerTrollToClientTroll } from "@/lib/trollcall/convert/troll"; -import { getSingleTroll } from "@/lib/trollcall/troll"; -import { getSingleUser } from "@/lib/trollcall/user"; -import { Router } from "express"; -import { NextApiRequest, NextApiResponse } from "next"; - -export const trollRouter = Router(); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { query, method } = req; - if (method === "GET") { - const user = await getSingleUser({ - name: query.user - }); - if (user == null) return res.status(404).end(); - const troll = await getSingleTroll({ - "name.0": query.troll, - "owners.0": user._id - }); - if (troll == null) return res.status(404).end(); - const serverTroll = await ServerTrollToClientTroll(troll); - res.json(serverTroll); - } else return res.status(405).end(); -} diff --git a/src/pages/api/user/index.ts b/src/pages/api/user/index.ts index 7d38126..ccb1f7e 100644 --- a/src/pages/api/user/index.ts +++ b/src/pages/api/user/index.ts @@ -5,26 +5,44 @@ import { ServerUser } from "@/types/user"; import { serialize } from "cookie"; import { NextApiRequest, NextApiResponse } from "next"; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { body } = req; - let validatedUser; - try { - validatedUser = await SubmitUserSchema.validate(body); - } catch (err) { - return res.status(400).send(err); - } - const checkExistingUser = await getSingleUser({ - name: validatedUser.name - }); - if (checkExistingUser != null) return res.status(409).end(); - // we are sure this object is full, so cast partial - const serverUser = SubmitUserToServerUser(validatedUser) as Omit; - const newUser = await createUser(serverUser); - if (newUser == null) return res.status(503).end(); - // Give cookies - res.setHeader("Set-Cookie", [ - serialize("TROLLCALL_NAME", newUser.name, { path: "/", maxAge: 31540000 }), - serialize("TROLLCALL_NAME", newUser.code, { path: "/", maxAge: 31540000 }) - ]); - res.json(newUser); +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { body, method } = req; + if (method === "POST") { + let validatedUser; + try { + validatedUser = await SubmitUserSchema.validate(body); + } catch (err) { + return res.status(400).send(err); + } + const checkExistingUser = await getSingleUser({ + name: validatedUser.name + }); + if (checkExistingUser != null) return res.status(409).end(); + // we are sure this object is full, so cast partial + const serverUser = SubmitUserToServerUser(validatedUser) as Omit< + ServerUser, + "_id" + >; + const newUser = await createUser(serverUser); + if (newUser == null) return res.status(503).end(); + // Give cookies + res.setHeader("Set-Cookie", [ + serialize("TROLLCALL_NAME", newUser.name, { + path: "/", + maxAge: 31540000 + }), + serialize("TROLLCALL_CODE", newUser.code, { + path: "/", + maxAge: 31540000 + }), + serialize("TROLLCALL_PFP", newUser.pfp ?? "", { + path: "/", + maxAge: 31540000 + }) + ]); + res.json(newUser); + } else return res.status(405).end(); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 01ba52d..c0f2413 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,115 +1,3 @@ -import styles from "@/styles/Home.module.css"; -import { Inter } from "next/font/google"; -import Head from "next/head"; -import Image from "next/image"; -import React from "react"; - -const inter = Inter({ subsets: ["latin"] }); - export default function Home() { - return ( - <> - - Create Next App - - - - -
-
-

- Get started by editing  - src/pages/index.tsx -

- -
- -
- Next.js Logo -
- - -
- - ); + return "Welcome home!"; } diff --git a/src/permissions.ts b/src/permissions.ts new file mode 100644 index 0000000..fada79a --- /dev/null +++ b/src/permissions.ts @@ -0,0 +1,24 @@ +export const Permissions = [ + { + name: "SUPPORTER", + values: ["6487ec00cd90481163238f79", "648b76878de421ccb5937f54"] + }, + { + name: "MODERATOR", + values: ["649ef0a9c62f6a4c8eb3f98a"] + }, + { + name: "ADMIN", + values: ["6488077200893ada4afdd09c"] + }, + { + name: "OWNER", + values: ["6487eb5fcd90481163238f6a"] + } +]; +export const Levels: { [key: string]: number } = { + "SUPPORTER": 0, + "MODERATOR": 1, + "ADMIN": 2, + "OWNER": 3 +}; diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css deleted file mode 100644 index 9411a5e..0000000 --- a/src/styles/Home.module.css +++ /dev/null @@ -1,229 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - width: var(--max-width); - max-width: 100%; -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/src/styles/globals.css b/src/styles/globals.css deleted file mode 100644 index d4f491e..0000000 --- a/src/styles/globals.css +++ /dev/null @@ -1,107 +0,0 @@ -:root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', - 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', - 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; - - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; - - --primary-glow: conic-gradient( - from 180deg at 50% 50%, - #16abff33 0deg, - #0885ff33 55deg, - #54d6ff33 120deg, - #0071ff33 160deg, - transparent 360deg - ); - --secondary-glow: radial-gradient( - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); - - --tile-start-rgb: 239, 245, 249; - --tile-end-rgb: 228, 232, 233; - --tile-border: conic-gradient( - #00000080, - #00000040, - #00000030, - #00000020, - #00000010, - #00000010, - #00000080 - ); - - --callout-rgb: 238, 240, 241; - --callout-border-rgb: 172, 175, 176; - --card-rgb: 180, 185, 188; - --card-border-rgb: 131, 134, 135; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); - --secondary-glow: linear-gradient( - to bottom right, - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0.3) - ); - - --tile-start-rgb: 2, 13, 46; - --tile-end-rgb: 2, 5, 19; - --tile-border: conic-gradient( - #ffffff80, - #ffffff40, - #ffffff30, - #ffffff20, - #ffffff10, - #ffffff10, - #ffffff80 - ); - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - } -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -a { - color: inherit; - text-decoration: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } -} diff --git a/src/types/assist/color.ts b/src/types/assist/color.ts index 8397ea5..c921270 100644 --- a/src/types/assist/color.ts +++ b/src/types/assist/color.ts @@ -3,100 +3,100 @@ import * as yup from "yup"; export type ColorTypes = [number, number, number]; export const ColorSchema = yup.tuple([ - yup.number().required().min(0).max(255), - yup.number().required().min(0).max(255), - yup.number().required().min(0).max(255), + yup.number().required().min(0).max(255), + yup.number().required().min(0).max(255), + yup.number().required().min(0).max(255) ]); const clamp = (n: number, mi: number, ma: number) => - Math.max(mi, Math.min(n, ma)); + Math.max(mi, Math.min(n, ma)); export class Color3 { - R: number; - G: number; - B: number; - constructor(red: number, green: number, blue: number) { - this.R = red; - this.G = green; - this.B = blue; - } - static clone(color3: Color3) { - return new Color3(color3.R, color3.G, color3.B); - } - static fromRGB(red: number, green: number, blue: number) { - return new Color3(red / 255, green / 255, blue / 255); - } - static fromHex(hex: string) { - // @ts-ignore - const hexSplit: [number, number, number] = ( - hex.match(new RegExp(`[0-9a-f]{1,${hex.length / 3}}`, "gi")) ?? [ - "0", - "0", - "0", - ] - ).map((x) => parseInt(x, 16) / 255); - return new Color3(...hexSplit); - } - static fromInt(int: number) { - return new Color3( - (int & 0xff0000) >> 16, - (int & 0x00ff00) >> 8, - int & 0x0000ff - ); - } - static assumeColor( - value: [number, number, number] | string | number, - rgb?: boolean - ) { - if (Color3.isColor(value)) { - if (Array.isArray(value)) - return rgb ? Color3.fromRGB(...value) : new Color3(...value); - else if (typeof value === "string") return Color3.fromHex(value); - else if (typeof value === "number") return Color3.fromInt(value); + R: number; + G: number; + B: number; + constructor(red: number, green: number, blue: number) { + this.R = red; + this.G = green; + this.B = blue; + } + static clone(color3: Color3) { + return new Color3(color3.R, color3.G, color3.B); + } + static fromRGB(red: number, green: number, blue: number) { + return new Color3(red / 255, green / 255, blue / 255); + } + static fromHex(hex: string) { + // @ts-ignore + const hexSplit: [number, number, number] = ( + hex.match(new RegExp(`[0-9a-f]{1,${hex.length / 3}}`, "gi")) ?? [ + "0", + "0", + "0" + ] + ).map(x => parseInt(x, 16) / 255); + return new Color3(...hexSplit); + } + static fromInt(int: number) { + return new Color3( + (int & 0xff0000) >> 16, + (int & 0x00ff00) >> 8, + int & 0x0000ff + ); + } + static assumeColor( + value: [number, number, number] | string | number, + rgb?: boolean + ) { + if (Color3.isColor(value)) { + if (Array.isArray(value)) + return rgb ? Color3.fromRGB(...value) : new Color3(...value); + else if (typeof value === "string") return Color3.fromHex(value); + else if (typeof value === "number") return Color3.fromInt(value); + } + throw new Error("Not a valid color type"); + } + static isColor( + value: [number, number, number] | string | number, + rgb?: boolean + ) { + return ( + (Array.isArray(value) && + value.length === 3 && + value.every(x => typeof x === "number" && !isNaN(x))) || + (typeof value === "string" && !isNaN(parseInt(value, 16))) || + (typeof value === "number" && !isNaN(value)) + ); } - throw new Error("Not a valid color type"); - } - static isColor( - value: [number, number, number] | string | number, - rgb?: boolean - ) { - return ( - (Array.isArray(value) && - value.length === 3 && - value.every((x) => typeof x === "number" && !isNaN(x))) || - (typeof value === "string" && !isNaN(parseInt(value, 16))) || - (typeof value === "number" && !isNaN(value)) - ); - } - toHex() { - return this.toInt().toString(16).padStart(6, "0"); - } - toInt() { - return ( - (Math.round(this.R * 255) << 16) + - (Math.round(this.G * 255) << 8) + - Math.round(this.B * 255) - ); - } - toRGB(): [number, number, number] { - return [this.R * 255, this.G * 255, this.B * 255]; - } - multiply(mult: number) { - return new Color3(this.R * mult, this.G * mult, this.B * mult); - } - lighten(mult: number) { - return new Color3( - clamp(this.R + (mult / 100) * (1 - this.R), 0, 1), - clamp(this.G + (mult / 100) * (1 - this.G), 0, 1), - clamp(this.B + (mult / 100) * (1 - this.B), 0, 1) - ); - } - darken(mult: number) { - return new Color3( - clamp(this.R - (mult / 100) * this.R, 0, 1), - clamp(this.G - (mult / 100) * this.G, 0, 1), - clamp(this.B - (mult / 100) * this.B, 0, 1) - ); - } + toHex() { + return this.toInt().toString(16).padStart(6, "0"); + } + toInt() { + return ( + (Math.round(this.R * 255) << 16) + + (Math.round(this.G * 255) << 8) + + Math.round(this.B * 255) + ); + } + toRGB(): [number, number, number] { + return [this.R * 255, this.G * 255, this.B * 255]; + } + multiply(mult: number) { + return new Color3(this.R * mult, this.G * mult, this.B * mult); + } + lighten(mult: number) { + return new Color3( + clamp(this.R + (mult / 100) * (1 - this.R), 0, 1), + clamp(this.G + (mult / 100) * (1 - this.G), 0, 1), + clamp(this.B + (mult / 100) * (1 - this.B), 0, 1) + ); + } + darken(mult: number) { + return new Color3( + clamp(this.R - (mult / 100) * this.R, 0, 1), + clamp(this.G - (mult / 100) * this.G, 0, 1), + clamp(this.B - (mult / 100) * this.B, 0, 1) + ); + } } diff --git a/src/types/assist/extended_zodiac.ts b/src/types/assist/extended_zodiac.ts index 5b00139..b78b69e 100644 --- a/src/types/assist/extended_zodiac.ts +++ b/src/types/assist/extended_zodiac.ts @@ -316,7 +316,9 @@ export const SignColorSchema = yup.object({ description: yup.string().required(), sign: yup.string().required(), color: ColorSchema.required(), - dates: yup.tuple([yup.string().required(), yup.string().required()]).required() + dates: yup + .tuple([yup.string().required(), yup.string().required()]) + .required() }); export type SignColorType = { diff --git a/src/types/assist/generics.ts b/src/types/assist/generics.ts index e565ef2..8a7d15b 100644 --- a/src/types/assist/generics.ts +++ b/src/types/assist/generics.ts @@ -1,6 +1,6 @@ import * as yup from "yup"; export const PolicySchema = yup - .string() - .oneOf(["yes", "ask", "no"]) - .default("no"); + .string() + .oneOf(["yes", "ask", "no"]) + .default("no"); diff --git a/src/types/assist/language.ts b/src/types/assist/language.ts index d2c9c5f..d06f4aa 100644 --- a/src/types/assist/language.ts +++ b/src/types/assist/language.ts @@ -1,51 +1,53 @@ export function AdaptivePossessive(owner: string, possession: string) { - return owner + (owner.endsWith("s") ? "'" : "'s") + " " + possession; + return owner + (owner.endsWith("s") ? "'" : "'s") + " " + possession; } export function PronounGrouper( - pronouns: [string, string, string], - sep?: string, - amount?: number + pronouns: [string, string, string], + sep?: string, + amount?: number ) { - return pronouns.slice(0, amount ?? 2).join(sep ?? "/"); + return pronouns.slice(0, amount ?? 2).join(sep ?? "/"); } export function HeightConverter(inches: number) { - var feetandinches = Math.floor(inches / 12) + "'" + (inches % 12) + '"'; - var meters = Math.floor((inches / 39.37) * 100) / 100 + "m"; - return feetandinches + " (" + meters + ")"; + var feetandinches = Math.floor(inches / 12) + "'" + (inches % 12) + '"'; + var meters = Math.floor((inches / 39.37) * 100) / 100 + "m"; + return feetandinches + " (" + meters + ")"; } export function AgeConverter(sweeps: number, years?: boolean) { - return years - ? `${Math.round(sweeps * 2.1667 * 10) / 10} years` - : `${sweeps} sweeps`; + return years + ? `${Math.round(sweeps * 2.1667 * 10) / 10} years` + : `${sweeps} sweeps`; } export function Pluralize(stringe: string) { - if (stringe.match(/(?:s|ch|sh|x|z|[^aeiou]o)$/)) return stringe + "es"; - else if (stringe.match(/[aeiou](?:y|o)$/)) return stringe + "s"; - else if (stringe.match(/[^aeiou]y$/)) return stringe.slice(0, -1) + "ies"; - else if (stringe.match(/(?:f|fe)$/)) return stringe.slice(0, -1) + "ves"; - else return stringe + "s"; + if (stringe.match(/(?:s|ch|sh|x|z|[^aeiou]o)$/)) return stringe + "es"; + else if (stringe.match(/[aeiou](?:y|o)$/)) return stringe + "s"; + else if (stringe.match(/[^aeiou]y$/)) return stringe.slice(0, -1) + "ies"; + else if (stringe.match(/(?:f|fe)$/)) return stringe.slice(0, -1) + "ves"; + else return stringe + "s"; } export function ArraySample(array: any[]) { - return array[Math.floor(Math.random() * array.length)]; + return array[Math.floor(Math.random() * array.length)]; } export function ProperNounCase(string: string) { - return string - .split(" ") - .map((x) => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()) - .join(" "); + return string + .split(" ") + .map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()) + .join(" "); } export function PesterchumNameFormatter(string: string) { - return ( - string + - " [" + - string.replace(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "$2$4").toUpperCase() + - "]" - ); + return ( + string + + " [" + + string + .replace(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "$2$4") + .toUpperCase() + + "]" + ); } diff --git a/src/types/assist/mongo.ts b/src/types/assist/mongo.ts index d56baed..d8d876e 100644 --- a/src/types/assist/mongo.ts +++ b/src/types/assist/mongo.ts @@ -2,5 +2,5 @@ import { ObjectId } from "mongodb"; import * as yup from "yup"; export const ObjectIdSchema = yup.mixed((value): value is ObjectId => - ObjectId.isValid(value) + ObjectId.isValid(value) ); diff --git a/src/types/client/quirks.ts b/src/types/client/quirks.ts index 48b3bbc..8dd10dd 100644 --- a/src/types/client/quirks.ts +++ b/src/types/client/quirks.ts @@ -3,8 +3,17 @@ import { QuirkSchema } from "../quirks"; export const SubmitQuirkHolderSchema = yup .array() - .of(yup.tuple([yup.string().required().lowercase(), QuirkSchema.required()]).required()) + .of( + yup + .tuple([ + yup.string().required().lowercase(), + QuirkSchema.required() + ]) + .required() + ) .required() - .test("has-default", 'Needs "default" Quirk Mode', v => v.some(([k, v]) => k === "default" || k === "Default")); + .test("has-default", 'Needs "default" Quirk Mode', v => + v.some(([k, v]) => k === "default" || k === "Default") + ); export type SubmitQuirkHolder = yup.InferType; diff --git a/src/types/client/troll.ts b/src/types/client/troll.ts index 2e68366..b130534 100644 --- a/src/types/client/troll.ts +++ b/src/types/client/troll.ts @@ -79,11 +79,26 @@ export const SubmitTrollSchema = yup // Personal preferences: yup .object({ - love: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10), - hate: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10) + love: yup + .array() + .of(yup.string().required().min(5).max(100)) + .required() + .min(3) + .max(10), + hate: yup + .array() + .of(yup.string().required().min(5).max(100)) + .required() + .min(3) + .max(10) }) .required(), - facts: yup.array().of(yup.string().required().min(5).max(100)).required().min(3).max(10), + facts: yup + .array() + .of(yup.string().required().min(5).max(100)) + .required() + .min(3) + .max(10), // Hiveswap identity trueSign: yup.string().required().oneOf(TrueSignKeys), @@ -100,7 +115,10 @@ export const SubmitTrollSchema = yup username: yup .string() .required() - .matches(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "Username must match Pesterchum formatting."), + .matches( + /^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, + "Username must match Pesterchum formatting." + ), textColor: ColorSchema.notRequired(), // default to trueSign color if undefined, quirks: SubmitQuirkHolderSchema.required(), // DO NOT HANDLE RIGHT NOW. // Handled! :D @@ -208,7 +226,10 @@ export const PartialTrollSchema = yup // Trollian username: yup .string() - .matches(/^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, "Username must match Pesterchum formatting."), + .matches( + /^(([a-z])[a-z]+)(([A-Z])[a-z]+)$/, + "Username must match Pesterchum formatting." + ), textColor: yup.tuple([ yup.number().min(0).max(255), yup.number().min(0).max(255), diff --git a/src/types/client/user.ts b/src/types/client/user.ts index 402dab4..43caa4f 100644 --- a/src/types/client/user.ts +++ b/src/types/client/user.ts @@ -40,7 +40,11 @@ export const PartialUserSchema = yup return v === "" ? null : v; }) .oneOf(TrueSignKeys), - color: yup.tuple([yup.number().min(0).max(255), yup.number().min(0).max(255), yup.number().min(0).max(255)]), + color: yup.tuple([ + yup.number().min(0).max(255), + yup.number().min(0).max(255), + yup.number().min(0).max(255) + ]), pfp: yup.string().url(), code: yup.string().max(256, "Too secure!!") // flairs: yup.array().of(ClientFlairSchema).required(), diff --git a/src/types/quirks.ts b/src/types/quirks.ts index a057bdd..5f78c94 100644 --- a/src/types/quirks.ts +++ b/src/types/quirks.ts @@ -70,7 +70,8 @@ export const QuirkReplacementTypes: { }, case: { find: null, - replace: 'Change the case of all text. "lower" for lowercase. "upper" for uppercase.' + replace: + 'Change the case of all text. "lower" for lowercase. "upper" for uppercase.' }, case_simple: { find: "Text to find what you want to change the case of.",