diff --git a/src/lib/trollcall/convert/user.ts b/src/lib/trollcall/convert/user.ts index 2f64fcb..5ec9333 100644 --- a/src/lib/trollcall/convert/user.ts +++ b/src/lib/trollcall/convert/user.ts @@ -27,7 +27,7 @@ export function SubmitUserToServerUser( ): Omit, "_id"> { let serverUser: Omit, "_id"> = { ...submitUser, - flairs: [], + flairs: undefined, code: submitUser.code || "", updatedDate: new Date() }; diff --git a/src/lib/trollcall/perms.ts b/src/lib/trollcall/perms.ts index 6d9d6d1..27f49a0 100644 --- a/src/lib/trollcall/perms.ts +++ b/src/lib/trollcall/perms.ts @@ -1,7 +1,8 @@ import { Levels, Permissions } from "@/permissions"; import { ServerUser } from "@/types/user"; +import { ObjectId } from "mongodb"; -export function getLevel(user: ServerUser) { +export function getLevel(user: Partial & { flairs: ObjectId[] }) { let highestLevel = "USER"; for (let level of Permissions) { if (user.flairs.some(oid => level.values.includes(oid.toString()))) diff --git a/src/pages/api/troll/[user]/[troll]/index.ts b/src/pages/api/troll/[user]/[troll]/index.ts index e5a8817..e082f3e 100644 --- a/src/pages/api/troll/[user]/[troll]/index.ts +++ b/src/pages/api/troll/[user]/[troll]/index.ts @@ -11,11 +11,8 @@ import { import { changeTroll, getSingleTroll } from "@/lib/trollcall/troll"; import { getSingleUser } from "@/lib/trollcall/user"; import { PartialTrollSchema, SubmitTroll } from "@/types/client/troll"; -import { Router } from "express"; import { NextApiRequest, NextApiResponse } from "next"; -export const trollRouter = Router(); - export default async function handler( req: NextApiRequest, res: NextApiResponse @@ -46,7 +43,6 @@ export default async function handler( name: query.user }); if (checkUser == null) return res.status(404).end(); - // Make sure to reverse methods so that way other owners can edit this troll if (!compareCredentials(checkUser, cookies)) { const thisUser = await getSingleUser({ name: cookies.TROLLCALL_NAME @@ -59,7 +55,7 @@ export default async function handler( } const editingTroll = await getSingleTroll({ "name.0": query.troll, - "owners": checkUser._id + "owners.0": checkUser._id }); if (editingTroll == null) return res.status(404).end(); const serverTroll = SubmitTrollToServerTroll(validatedTroll); diff --git a/src/pages/api/troll/[user]/[troll]/server/index.ts b/src/pages/api/troll/[user]/[troll]/server/index.ts new file mode 100644 index 0000000..d3625cc --- /dev/null +++ b/src/pages/api/troll/[user]/[troll]/server/index.ts @@ -0,0 +1,36 @@ +import { + compareCredentials, + compareLevels, + getLevel +} from "@/lib/trollcall/perms"; +import { getSingleTroll } 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 { query, cookies, method, body } = req; + if (method === "GET") { + const user = await getSingleUser({ + name: query.user + }); + if (user == null) return res.status(404).end(); + if (!compareCredentials(user, cookies)) { + const thisUser = await getSingleUser({ + name: cookies.TROLLCALL_NAME + }); + if (thisUser == null || !compareCredentials(thisUser, cookies)) + return res.status(403).end(); + if (!compareLevels(getLevel(thisUser), "MODERATOR")) + return res.status(403).end(); + } + const troll = await getSingleTroll({ + "name.0": query.troll, + "owners.0": user._id + }); + if (troll == null) return res.status(404).end(); + res.json(troll); + } +} diff --git a/src/pages/api/user/[user]/index.ts b/src/pages/api/user/[user]/index.ts index fa44efc..addca71 100644 --- a/src/pages/api/user/[user]/index.ts +++ b/src/pages/api/user/[user]/index.ts @@ -39,6 +39,7 @@ export default async function handler( name: query.user }); if (checkExistingUser == null) return res.status(404).end(); + let isModerator = false; if (!compareCredentials(checkExistingUser, cookies)) { const thisUser = await getSingleUser({ name: cookies.TROLLCALL_NAME @@ -47,27 +48,33 @@ export default async function handler( return res.status(403).end(); if (!compareLevels(getLevel(thisUser), "MODERATOR")) return res.status(403).end(); + isModerator = true; } const serverUser = SubmitUserToServerUser(validatedUser); if (serverUser.code === "") serverUser.code = checkExistingUser.code || nanoid(16); + if (!compareLevels(getLevel(checkExistingUser), "SUPPORTER")) + serverUser.bgimage = null; 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_CODE", newUser.code, { - path: "/", - maxAge: 31540000 - }), - serialize("TROLLCALL_PFP", newUser.pfp ?? "", { - path: "/", - maxAge: 31540000 - }) - ]).json(newUser); + if (!isModerator) + // don't set cookies if moderator is changing credentials + 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 + }) + ]).json(newUser); + else res.json(newUser); } else return res.status(405).end(); } diff --git a/src/pages/api/user/index.ts b/src/pages/api/user/index.ts index f0de9c3..5f10f5f 100644 --- a/src/pages/api/user/index.ts +++ b/src/pages/api/user/index.ts @@ -1,4 +1,5 @@ import { SubmitUserToServerUser } from "@/lib/trollcall/convert/user"; +import { compareLevels, getLevel } from "@/lib/trollcall/perms"; import { createUser, getSingleUser } from "@/lib/trollcall/user"; import { SubmitUserSchema } from "@/types/client/user"; import { ServerUser } from "@/types/user"; @@ -30,6 +31,8 @@ export default async function handler( "_id" >; if (serverUser.code === "") serverUser.code = nanoid(16); + if (!compareLevels(getLevel(serverUser), "SUPPORTER")) + serverUser.bgimage = null; const newUser = await createUser(serverUser); if (newUser == null) return res.status(503).end(); // Give cookies diff --git a/src/types/client/user.ts b/src/types/client/user.ts index 43caa4f..3de8350 100644 --- a/src/types/client/user.ts +++ b/src/types/client/user.ts @@ -14,8 +14,40 @@ export const SubmitUserSchema = yup description: yup.string().max(10000).ensure(), url: yup.string().notRequired().url(), trueSign: yup.string().required().oneOf(TrueSignKeys), + pronouns: yup + .array() + .of( + yup + .tuple([ + yup + .string() + .required() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase(), // she, he, they + yup + .string() + .required() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase(), // her, him, them + yup + .string() + .required() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase() // hers, his, theirs + ]) + .required() + ) + .required() + .min(1), color: ColorSchema.required(), pfp: yup.string().notRequired().url(), + bgimage: yup.string().notRequired().url(), code: yup.string().notRequired().max(256, "Too secure!!") // flairs: yup.array().of(ClientFlairSchema).required(), }) @@ -40,12 +72,38 @@ export const PartialUserSchema = yup return v === "" ? null : v; }) .oneOf(TrueSignKeys), + pronouns: yup + .array() + .of( + yup.tuple([ + yup + .string() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase(), // she, he, they + yup + .string() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase(), // her, him, them + yup + .string() + .matches(/^[A-z]+$/, "Letters only") + .min(1) + .max(10) + .lowercase() // hers, his, theirs + ]) + ) + .min(1), 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(), + bgimage: yup.string().url(), code: yup.string().max(256, "Too secure!!") // flairs: yup.array().of(ClientFlairSchema).required(), })