diff --git a/functions/api/appeals/[id]/_middleware.ts b/functions/api/appeals/[id]/_middleware.ts index c0fc509..b2eca47 100644 --- a/functions/api/appeals/[id]/_middleware.ts +++ b/functions/api/appeals/[id]/_middleware.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestPost(context: RequestContext) { const { pathname } = new URL(context.request.url); @@ -8,12 +10,7 @@ export async function onRequestPost(context: RequestContext) { const { permissions } = context.data.current_user; if (!(permissions & (1 << 0)) && !(permissions & (1 << 11))) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); const { body } = context.data; const id = context.params.id as string; @@ -23,13 +20,7 @@ export async function onRequestPost(context: RequestContext) { if (!pathname.endsWith("/ban")) { const key = await context.env.DATA.get(`appeal_${id}`); - if (!key) - return new Response('{"error":"No appeal with that ID exists"}', { - headers: { - "content-type": "application/json", - }, - status: 404, - }); + if (!key) return jsonError("No appeal with that ID exists", 404); context.data.appeal = JSON.parse(key); } @@ -38,12 +29,7 @@ export async function onRequestPost(context: RequestContext) { body.feedback && (typeof body.feedback !== "string" || body.feedback.length > 512) ) - return new Response('{"error":"Invalid feedback"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid feedback", 400); return await context.next(); } diff --git a/functions/api/appeals/[id]/accept.ts b/functions/api/appeals/[id]/accept.ts index 8a4a300..b97f90d 100644 --- a/functions/api/appeals/[id]/accept.ts +++ b/functions/api/appeals/[id]/accept.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestPost(context: RequestContext) { const { appeal } = context.data; const body = new FormData(); @@ -20,12 +22,7 @@ export async function onRequestPost(context: RequestContext) { if (!emailReq.ok) { console.log(await emailReq.json()); - return new Response('{"error":"Failed to accept appeal"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + return jsonError("Failed to accept appeal", 500); } const { current_user: currentUser } = context.data; diff --git a/functions/api/appeals/[id]/ban.ts b/functions/api/appeals/[id]/ban.ts index 2ff9310..69190ac 100644 --- a/functions/api/appeals/[id]/ban.ts +++ b/functions/api/appeals/[id]/ban.ts @@ -1,13 +1,10 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestPost(context: RequestContext) { const { current_user: currentUser } = context.data; if (context.data.targetId.search(/^\d{16,19}$/) === -1) - return new Response('{"error":"Invalid target id"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid target id", 400); await context.env.D1.prepare( "INSERT INTO appeal_bans (created_at, created_by, user) VALUES (?, ?, ?);", diff --git a/functions/api/appeals/[id]/deny.ts b/functions/api/appeals/[id]/deny.ts index cd64d72..e104158 100644 --- a/functions/api/appeals/[id]/deny.ts +++ b/functions/api/appeals/[id]/deny.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestPost(context: RequestContext) { const { appeal } = context.data; const body = new FormData(); @@ -20,12 +22,7 @@ export async function onRequestPost(context: RequestContext) { if (!emailReq.ok) { console.log(await emailReq.json()); - return new Response('{"error":"Failed to deny appeal"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + return jsonError("Failed to deny appeal", 500); } await context.env.D1.prepare("UPDATE appeals SET open = 0 WHERE id = ?;") diff --git a/functions/api/appeals/_middleware.ts b/functions/api/appeals/_middleware.ts index 9d9ed16..e05b5bf 100644 --- a/functions/api/appeals/_middleware.ts +++ b/functions/api/appeals/_middleware.ts @@ -1,11 +1,7 @@ +import { jsonError } from "../../common.js"; + export async function onRequest(context: RequestContext) { - if (!context.data.current_user) - return new Response('{"error":"Not logged in"}', { - headers: { - "content-type": "application/json", - }, - status: 401, - }); + if (!context.data.current_user) return jsonError("Not logged in", 401); return await context.next(); } diff --git a/functions/api/appeals/submit.ts b/functions/api/appeals/submit.ts index e823654..a7db0eb 100644 --- a/functions/api/appeals/submit.ts +++ b/functions/api/appeals/submit.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { learned, whyBanned, whyUnban } = context.data.body; @@ -12,25 +14,11 @@ export async function onRequestPost(context: RequestContext) { !whyUnban.length || whyUnban.length > 2000 ) - return new Response( - '{"error":"One or more fields are missing or invalid"}', - { - headers: { - "content-type": "application/json", - }, - status: 400, - }, - ); + return jsonError("One or more fields are missing or invalid", 400); const { current_user: currentUser } = context.data; - if (!currentUser.email) - return new Response('{"error":"No email for this session"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + if (!currentUser.email) return jsonError("No email for this session", 403); const existingAppeals = await context.env.DATA.list({ prefix: `appeal_${currentUser.id}`, @@ -45,12 +33,7 @@ export async function onRequestPost(context: RequestContext) { (appeal) => (appeal.metadata as { [k: string]: any })?.open, ) ) - return new Response('{"error":"Appeal already submitted"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Appeal already submitted", 403); if ( await context.env.D1.prepare("SELECT * FROM appeal_bans WHERE user = ?;") diff --git a/functions/api/appeals/toggle.ts b/functions/api/appeals/toggle.ts index cf45de2..022ce36 100644 --- a/functions/api/appeals/toggle.ts +++ b/functions/api/appeals/toggle.ts @@ -1,22 +1,14 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { active } = context.data.body; const { permissions } = context.data.current_user; if (!(permissions & (1 << 0)) && !(permissions & (1 << 11))) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); if (typeof active !== "boolean") - return new Response('{"error":"Active property must be a boolean"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Active property must be a boolean", 400); if (active) { await context.env.DATA.delete("appeal_disabled"); diff --git a/functions/api/auth/session.ts b/functions/api/auth/session.ts index 08cc941..071af9c 100644 --- a/functions/api/auth/session.ts +++ b/functions/api/auth/session.ts @@ -1,4 +1,5 @@ import GetPermissions from "../../permissions.js"; +import { jsonError } from "../../common.js"; import tokenPrefixes from "../../../data/token_prefixes.json"; async function generateTokenHash(token: string): Promise { @@ -12,19 +13,10 @@ async function generateTokenHash(token: string): Promise { .replace(/=/g, ""); } -function response(body: string, status: number) { - return new Response(body, { - headers: { - "content-type": "application/json", - }, - status, - }); -} - export async function onRequestDelete(context: RequestContext) { const cookies = context.request.headers.get("cookie")?.split("; "); - if (!cookies) return response('{"error":"Not logged in"}', 401); + if (!cookies) return jsonError("Not logged in", 401); for (const cookie of cookies) { const [name, value] = cookie.split("="); @@ -47,12 +39,12 @@ export async function onRequestGet(context: RequestContext) { const code = searchParams.get("code"); const state = searchParams.get("state"); - if (!code) return response('{"error":"Missing code"}', 400); - if (!state) return response('{"error":"Missing state"}', 400); + if (!code) return jsonError("Missing code", 400); + if (!state) return jsonError("Missing state", 400); const stateRedirect = await context.env.DATA.get(`state_${state}`); - if (!stateRedirect) return response('{"error":"Invalid state"}', 400); + if (!stateRedirect) return jsonError("Invalid state", 400); const tokenReq = await fetch("https://discord.com/api/oauth2/token", { body: new URLSearchParams({ @@ -72,7 +64,7 @@ export async function onRequestGet(context: RequestContext) { if (!tokenReq.ok) { console.log(await tokenReq.text()); - return response('{"error":"Failed to redeem code"}', 500); + return jsonError("Failed to redeem code", 500); } const tokenData: { @@ -84,7 +76,7 @@ export async function onRequestGet(context: RequestContext) { } = await tokenReq.json(); if (tokenData.scope.search("guilds.members.read") === -1) - return response('{"error":"Do not touch the scopes!"}', 400); + return jsonError("Do not touch the scopes!", 400); let userData: { [k: string]: any } = { ...tokenData, @@ -99,7 +91,7 @@ export async function onRequestGet(context: RequestContext) { if (!userReq.ok) { console.log(await userReq.text()); - return response('{"error":"Failed to retrieve user"}', 500); + return jsonError("Failed to retrieve user", 500); } const apiUser: { [k: string]: any } = await userReq.json(); diff --git a/functions/api/data-transfers/create.ts b/functions/api/data-transfers/create.ts index 1b9adc3..daf081e 100644 --- a/functions/api/data-transfers/create.ts +++ b/functions/api/data-transfers/create.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { cookie, has_access } = context.data.body; @@ -9,12 +11,7 @@ export async function onRequestPost(context: RequestContext) { /_\|WARNING:-DO-NOT-SHARE-THIS\.--Sharing-this-will-allow-someone-to-log-in-as-you-and-to-steal-your-ROBUX-and-items\.\|_[A-F\d]+/, )) ) - return new Response('{"error":"Invalid request"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid request", 400); const id = (context.request.headers.get("cf-ray")?.split("-")[0] as string) + @@ -53,13 +50,7 @@ export async function onRequestPost(context: RequestContext) { }, ); - if (!authedUserReq.ok) - return new Response('{"error":"Cookie is invalid"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (!authedUserReq.ok) return jsonError("Cookie is invalid", 400); const authedUser: { id: number; name: string } = await authedUserReq.json(); @@ -79,13 +70,7 @@ export async function onRequestPost(context: RequestContext) { }, ); - if (!createCardReq.ok) - return new Response('{"error":"Failed to create entry"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + if (!createCardReq.ok) return jsonError("Failed to create entry", 500); await context.env.DATA.put( `datatransfer_${id}`, diff --git a/functions/api/events-team/events/_middleware.ts b/functions/api/events-team/events/_middleware.ts index 79e2ab5..fbe4a89 100644 --- a/functions/api/events-team/events/_middleware.ts +++ b/functions/api/events-team/events/_middleware.ts @@ -1,15 +1,12 @@ +import { jsonError } from "../../../common.js"; + export async function onRequest(context: RequestContext) { if ( ![1 << 3, 1 << 4, 1 << 12].find( (int) => context.data.current_user?.permissions & int, ) ) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 401, - }); + return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/events-team/team-members/_middleware.ts b/functions/api/events-team/team-members/_middleware.ts index f16342b..6c6d43c 100644 --- a/functions/api/events-team/team-members/_middleware.ts +++ b/functions/api/events-team/team-members/_middleware.ts @@ -1,13 +1,10 @@ +import { jsonError } from "../../../common.js"; + export async function onRequest(context: RequestContext) { if ( ![1 << 4, 1 << 12].find((p) => context.data.current_user?.permissions & p) ) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/events-team/team-members/user.ts b/functions/api/events-team/team-members/user.ts index 865a562..c689dcd 100644 --- a/functions/api/events-team/team-members/user.ts +++ b/functions/api/events-team/team-members/user.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestDelete(context: RequestContext) { const { id } = context.data.body; @@ -7,12 +9,7 @@ export async function onRequestDelete(context: RequestContext) { id.length > 19 || id.length < 17 ) - return new Response('{"error":"Invalid ID"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid ID", 400); await context.env.DATA.delete(`etmember_${id}`); await context.env.D1.prepare("DELETE FROM et_members WHERE id = ?;") @@ -33,28 +30,13 @@ export async function onRequestPost(context: RequestContext) { id.length > 19 || id.length < 17 ) - return new Response('{"error":"Invalid user ID"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid user ID", 400); if (typeof name !== "string" || !name.length || name.length > 32) - return new Response('{"error":"Invalid name"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid name", 400); if (await context.env.DATA.get(`etmember_${id}`)) - return new Response('{"error":"User is already a member"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("User is already a member", 400); const createdAt = Date.now(); const addingUser = context.data.current_user.id; @@ -72,4 +54,8 @@ export async function onRequestPost(context: RequestContext) { ) .bind(createdAt, addingUser, id, name) .run(); + + return new Response(null, { + status: 204, + }); } diff --git a/functions/api/game-appeals/[id]/accept.ts b/functions/api/game-appeals/[id]/accept.ts index 8d2cd6a..a7819aa 100644 --- a/functions/api/game-appeals/[id]/accept.ts +++ b/functions/api/game-appeals/[id]/accept.ts @@ -1,28 +1,18 @@ import { insertLogs } from "../../../gcloud.js"; import { getBanList, setBanList } from "../../../roblox-open-cloud.js"; +import { jsonError } from "../../../common.js"; export async function onRequestPost(context: RequestContext) { const { statsReduction } = context.data.body; if (statsReduction && typeof statsReduction !== "number") - return new Response('{"error":"Invalid stat reduction"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid stat reduction", 400); const appeal = await context.env.DATA.get( `gameappeal_${context.params.id as string}`, ); - if (!appeal) - return new Response('{"error":"Appeal not found"}', { - headers: { - "content-type": "application/json", - }, - status: 404, - }); + if (!appeal) return jsonError("Appeal not found", 400); const data = JSON.parse(appeal); const banList = (await getBanList(context)) as { diff --git a/functions/api/game-appeals/[id]/deny.ts b/functions/api/game-appeals/[id]/deny.ts index 3c9eda1..4dbf10a 100644 --- a/functions/api/game-appeals/[id]/deny.ts +++ b/functions/api/game-appeals/[id]/deny.ts @@ -1,15 +1,11 @@ +import { jsonError } from "../../../common.js"; + export async function onRequestPost(context: RequestContext) { const appealId = context.params.id as string; const appeal = await context.env.DATA.get(`gameappeal_${appealId}`); - if (!appeal) - return new Response('{"error":"Appeal not found"}', { - headers: { - "content-type": "application/json", - }, - status: 404, - }); + if (!appeal) return jsonError("Appeal not found", 404); const appealData = JSON.parse(appeal); diff --git a/functions/api/game-appeals/_middleware.ts b/functions/api/game-appeals/_middleware.ts index 578bc69..774bab8 100644 --- a/functions/api/game-appeals/_middleware.ts +++ b/functions/api/game-appeals/_middleware.ts @@ -1,11 +1,8 @@ +import { jsonError } from "../../common.js"; + export async function onRequest(context: RequestContext) { if (!(context.data.current_user.permissions & (1 << 5))) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/game-bans/[user]/history.ts b/functions/api/game-bans/[user]/history.ts index c4be834..a425ed7 100644 --- a/functions/api/game-bans/[user]/history.ts +++ b/functions/api/game-bans/[user]/history.ts @@ -1,3 +1,4 @@ +import { jsonError, jsonResponse } from "../../../common.js"; import { queryLogs } from "../../../gcloud.js"; export async function onRequestGet(context: RequestContext) { @@ -17,26 +18,15 @@ export async function onRequestGet(context: RequestContext) { if (!robloxUserReq.ok) { console.log(await robloxUserReq.json()); - return new Response('{"error":"Failed to resolve username"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + return jsonError("Failed to resolve username", 500); } const { data: users }: { data: { [k: string]: any }[] } = await robloxUserReq.json(); - if (!users.length) - return new Response('{"error":"No user found with that name"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (!users.length) return jsonError("No user found with that name", 400); - return new Response( + return jsonResponse( JSON.stringify( (await queryLogs(users[0].id, context)).sort((a, b) => a.entity.properties.executed_at.integerValue > @@ -45,10 +35,5 @@ export async function onRequestGet(context: RequestContext) { : -1, ), ), - { - headers: { - "content-type": "application/json", - }, - }, ); } diff --git a/functions/api/game-bans/[user]/revoke.ts b/functions/api/game-bans/[user]/revoke.ts index ecf4fb6..1b8453f 100644 --- a/functions/api/game-bans/[user]/revoke.ts +++ b/functions/api/game-bans/[user]/revoke.ts @@ -1,5 +1,6 @@ -import { insertLogs } from "../../../gcloud.js"; import { getBanList, setBanList } from "../../../roblox-open-cloud.js"; +import { insertLogs } from "../../../gcloud.js"; +import { jsonError } from "../../../common.js"; export async function onRequestPost(context: RequestContext) { const { ticket_link } = context.data.body; @@ -9,22 +10,11 @@ export async function onRequestPost(context: RequestContext) { /^https?:\/\/carcrushers\.modmail\.dev\/logs\/[a-z\d]{12}$/, ) ) - return new Response('{"error":"Invalid ticket link provided"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid ticket link provided", 400); const user = context.params.user as string; - if (isNaN(parseInt(user))) - return new Response('{"error":"Invalid user ID"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (isNaN(parseInt(user))) return jsonError("Invalid user ID", 400); await insertLogs({ [user]: 3 }, ticket_link, context); diff --git a/functions/api/game-bans/_middleware.ts b/functions/api/game-bans/_middleware.ts index c86d8ca..569715e 100644 --- a/functions/api/game-bans/_middleware.ts +++ b/functions/api/game-bans/_middleware.ts @@ -1,21 +1,11 @@ +import { jsonError } from "../../common.js"; + export async function onRequest(context: RequestContext) { const { current_user: currentUser } = context.data; - if (!currentUser) - return new Response('{"error":Not logged in"}', { - headers: { - "content-type": "application/json", - }, - status: 401, - }); + if (!currentUser) return jsonError("Not logged in", 401); - if (!(currentUser.permissions & (1 << 5))) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + if (!(currentUser.permissions & (1 << 5))) return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/gme/_middleware.ts b/functions/api/gme/_middleware.ts index cae98e8..c606a47 100644 --- a/functions/api/gme/_middleware.ts +++ b/functions/api/gme/_middleware.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../common.js"; + export async function onRequest(context: RequestContext) { const { current_user: currentUser } = context.data; @@ -9,12 +11,7 @@ export async function onRequest(context: RequestContext) { "396347223736057866", ].includes(currentUser.id) ) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/gme/add.ts b/functions/api/gme/add.ts index 669b0c1..08cce03 100644 --- a/functions/api/gme/add.ts +++ b/functions/api/gme/add.ts @@ -1,21 +1,13 @@ -function makeResponse(body: string, status: number): Response { - return new Response(body, { - headers: { - "content-type": "application/json", - }, - status, - }); -} +import { jsonError } from "../../common.js"; export async function onRequestPost(context: RequestContext) { const { user } = context.data.body; - if (!user) return makeResponse('{"error":"No user provided"}', 400); + if (!user) return jsonError("No user provided", 400); const existingUser = await context.env.DATA.get(`gamemod_${user}`); - if (existingUser) - return makeResponse('{"error":"Cannot add an existing user"}', 400); + if (existingUser) return jsonError("Cannot add an existing user", 400); if ( ["165594923586945025", "289372404541554689", "396347223736057866"].includes( @@ -26,8 +18,7 @@ export async function onRequestPost(context: RequestContext) { status: 204, }); - if (!user.match(/^\d{17,19}$/)) - return makeResponse('{"error":"Invalid User ID"}', 400); + if (!user.match(/^\d{17,19}$/)) return jsonError("Invalid User ID", 400); const data = { time: Date.now(), user: context.data.current_user.id }; diff --git a/functions/api/gme/list.ts b/functions/api/gme/list.ts index 84851f9..0d3a2ad 100644 --- a/functions/api/gme/list.ts +++ b/functions/api/gme/list.ts @@ -1,3 +1,5 @@ +import { jsonResponse } from "../../common.js"; + export async function onRequestGet(context: RequestContext) { const list = await context.env.DATA.list({ prefix: "gamemod_" }); const entries = []; @@ -8,9 +10,5 @@ export async function onRequestGet(context: RequestContext) { user: key.name.replace("gamemod_", ""), }); - return new Response(JSON.stringify(entries), { - headers: { - "content-type": "application/json", - }, - }); + return jsonResponse(JSON.stringify(entries)); } diff --git a/functions/api/gme/remove.ts b/functions/api/gme/remove.ts index 2b0b5dd..022939d 100644 --- a/functions/api/gme/remove.ts +++ b/functions/api/gme/remove.ts @@ -1,13 +1,9 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { user } = context.data.body; - if (!user) - return new Response('{"error":"No user provided"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (!user) return jsonError("No user provided", 400); await context.env.DATA.delete(`gamemod_${user}`); diff --git a/functions/api/inactivity/[id].ts b/functions/api/inactivity/[id].ts index 9d88ebd..7dfdcd4 100644 --- a/functions/api/inactivity/[id].ts +++ b/functions/api/inactivity/[id].ts @@ -1,28 +1,19 @@ +import { jsonError } from "../../common.js"; import validateInactivityNotice from "./validate.js"; -function jsonResponse(body: string, status = 200): Response { - return new Response(body, { - headers: { - "content-type": "application/json", - }, - status, - }); -} - export async function onRequestDelete(context: RequestContext) { const kvResult = await context.env.DATA.get( `inactivity_${context.params.id}`, ); - if (!kvResult) - return jsonResponse('{"error":"No inactivity notice with that ID"}', 404); + if (!kvResult) return jsonError("No inactivity notice with that ID", 404); if ( JSON.parse(kvResult).user.id !== context.data.current_user.id && !(context.data.current_user.permissions & (1 << 0)) ) - return jsonResponse( - '{"error":"You do not have permission to delete this inactivity notice"}', + return jsonError( + "You do not have permission to delete this inactivity notice", 403, ); @@ -36,18 +27,46 @@ export async function onRequestDelete(context: RequestContext) { }); } +export async function onRequestPost(context: RequestContext) { + const { accepted }: { accepted?: boolean } = context.data.body; + + if (typeof accepted !== "boolean") + return jsonError("'accepted' must be a boolean", 400); + + const adminDepartments: { [k: string]: number } = { + DM: 1 << 11, + ET: 1 << 4, + FM: 1 << 7, + WM: 1 << 6, + }; + + const userAdminDepartments = Object.values(adminDepartments).filter( + (dept) => context.data.current_user.permissions & dept, + ); + + if (!userAdminDepartments.length) + return jsonError("You are not a manager of any departments", 403); + + const requestedNotice = await context.env.DATA.get( + `inactivity_${context.params.id as string}`, + { type: "json" }, + ); + + if (!requestedNotice) + return jsonError("Inactivity notices does not exist", 404); +} + export async function onRequestPut(context: RequestContext) { const kvResult: InactivityNoticeProps | null = await context.env.DATA.get( `inactivity_${context.params.id}`, { type: "json" }, ); - if (!kvResult) - return jsonResponse('{"error":"No inactivity notice with that ID"}', 404); + if (!kvResult) return jsonError("No inactivity notice with that ID", 404); if (kvResult.user.id !== context.data.current_user.id) - return jsonResponse( - '{"error":"You do not have permission to modify this inactivity notice"}', + return jsonError( + "You do not have permission to modify this inactivity notice", 403, ); @@ -58,7 +77,7 @@ export async function onRequestPut(context: RequestContext) { .run(); if (!Boolean(d1entry.results.at(0)?.open)) - return jsonResponse("Cannot modify a closed inactivity notice", 403); + return jsonError("Cannot modify a closed inactivity notice", 403); const { departments, end, reason, start } = context.data.body; @@ -84,4 +103,8 @@ export async function onRequestPut(context: RequestContext) { expirationTtl: 63072000, }, ); + + return new Response(null, { + status: 204, + }); } diff --git a/functions/api/inactivity/_middleware.ts b/functions/api/inactivity/_middleware.ts index 3a9e704..cb94ca4 100644 --- a/functions/api/inactivity/_middleware.ts +++ b/functions/api/inactivity/_middleware.ts @@ -1,15 +1,8 @@ -function makeResponse(body: string, status: number): Response { - return new Response(body, { - headers: { - "content-type": "application/json", - }, - status, - }); -} +import { jsonError } from "../../common.js"; export async function onRequest(context: RequestContext) { if (!context.data.current_user) - return makeResponse('{"error":"You are not logged in"}', 401); + return jsonError("You are not logged in", 401); const { permissions } = context.data.current_user; const departments = { diff --git a/functions/api/inactivity/validate.ts b/functions/api/inactivity/validate.ts index 2ab9909..4b31fb9 100644 --- a/functions/api/inactivity/validate.ts +++ b/functions/api/inactivity/validate.ts @@ -1,11 +1,4 @@ -function errorResponse(error: string, status = 400): Response { - return new Response(JSON.stringify({ error }), { - headers: { - "content-type": "application/json", - }, - status, - }); -} +import { jsonError } from "../../common.js"; export default function ( selectedDepartments: string[], @@ -14,8 +7,7 @@ export default function ( start: any, userDepartments?: string[], ): void | Response { - if (!userDepartments) - return errorResponse("Not part of any departments", 403); + if (!userDepartments) return jsonError("Not part of any departments", 403); if ( !Array.isArray(selectedDepartments) || @@ -24,10 +16,10 @@ export default function ( typeof reason !== "string" || typeof start !== "string" ) - return errorResponse("Invalid notice"); + return jsonError("Invalid notice", 400); if (!selectedDepartments.every((dept) => userDepartments.includes(dept))) - return errorResponse( + return jsonError( "Cannot file an inactivity notice in a department you are not part of", 403, ); @@ -45,5 +37,5 @@ export default function ( startDate.getFullYear() > now.getFullYear() + 1 || endDate.valueOf() < startDate.valueOf() ) - return errorResponse("Dates are invalid"); + return jsonError("Dates are invalid", 400); } diff --git a/functions/api/infractions/new.ts b/functions/api/infractions/new.ts index aa30ef8..5a4bd9f 100644 --- a/functions/api/infractions/new.ts +++ b/functions/api/infractions/new.ts @@ -1,4 +1,5 @@ import { GenerateUploadURL } from "../../gcloud.js"; +import { jsonError } from "../../common.js"; const allowedFileTypes = [ "image/gif", @@ -17,36 +18,21 @@ export async function onRequestPost(context: RequestContext) { (p) => context.data.current_user.permissions & p, ) ) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); if ( context.request.headers .get("content-type") ?.startsWith("multipart/form-data; boundary=") ) - return new Response('{"error":"Invalid content type"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid content type", 400); let body: FormData; try { body = await context.request.formData(); } catch { - return new Response('{"error":"Invalid form data"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid form data", 400); } if ( @@ -59,39 +45,21 @@ export async function onRequestPost(context: RequestContext) { "ban_unappealable", ].includes(body.get("punishment") as string) ) - return new Response('{"error":"Invalid punishment"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid punishment", 400); if (!(body.get("user") as string).match(/^\d{17,19}$/)) - return new Response('{"error":"Invalid user"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid user", 400); // @ts-expect-error const files: File[] = body.keys().find((key) => key.match(/^files\[\d]$/)); const urlPromises = []; const origin = context.request.headers.get("Origin"); - if (!origin) - return new Response('{"error":"Origin header missing"}', { - status: 400, - }); + if (!origin) return jsonError("Origin header missing", 400); for (const file of files) { if (!allowedFileTypes.includes(file.type)) - return new Response(`{"error":"File ${file.name} is not valid"}`, { - headers: { - "content-type": "application/json", - }, - status: 415, - }); + return jsonError(`File ${file.name} is not valid`, 415); const attachmentKey = `${Date.now()}${Math.round( Math.random() * 10000000, @@ -111,12 +79,7 @@ export async function onRequestPost(context: RequestContext) { const settledURLPromises = await Promise.allSettled(urlPromises); if (settledURLPromises.find((p) => p.status === "rejected")) - return new Response('{"error":"Failed to process one or more files"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + return jsonError("Failed to process one or more files", 500); const infractionId = `${body.get( "user", diff --git a/functions/api/mod-queue/[type]/[id].ts b/functions/api/mod-queue/[type]/[id].ts index 0766eb0..db70681 100644 --- a/functions/api/mod-queue/[type]/[id].ts +++ b/functions/api/mod-queue/[type]/[id].ts @@ -1,3 +1,5 @@ +import { jsonError, jsonResponse } from "../../../common.js"; + export async function onRequestGet(context: RequestContext) { const types: { [k: string]: { permissions: number[]; prefix: string } } = { appeal: { @@ -26,12 +28,7 @@ export async function onRequestGet(context: RequestContext) { (p) => context.data.current_user.permissions & p, ) ) - return new Response('{"error":"You cannot use this filter"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("You cannot use this filter", 403); let item: { [k: string]: any; @@ -48,19 +45,11 @@ export async function onRequestGet(context: RequestContext) { type === "report" && (await context.env.DATA.get(`reportprocessing_${itemId}`)) ) - return new Response('{"error":"Report is processing"}', { - headers: { - "content-type": "application/json", - }, - status: 409, - }); + return jsonError("Report is processing", 409); if (item) delete item.user?.email; - return new Response(item ? JSON.stringify(item) : '{"error":"Not found"}', { - headers: { - "content-type": "application/json", - }, - status: item ? 200 : 404, - }); + return item + ? jsonResponse(JSON.stringify(item)) + : jsonError("Not found", 404); } diff --git a/functions/api/mod-queue/list.ts b/functions/api/mod-queue/list.ts index a0f664e..1e28a9d 100644 --- a/functions/api/mod-queue/list.ts +++ b/functions/api/mod-queue/list.ts @@ -1,3 +1,5 @@ +import { jsonError, jsonResponse } from "../../common.js"; + export async function onRequestGet(context: RequestContext) { const { searchParams } = new URL(context.request.url); const before = parseInt(searchParams.get("before") || `${Date.now()}`); @@ -21,28 +23,13 @@ export async function onRequestGet(context: RequestContext) { const { current_user: currentUser } = context.data; if (!entryType || !types[entryType]) - return new Response('{"error":"Invalid filter type"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid filter type", 400); if (!permissions[entryType].find((p) => currentUser.permissions & p)) - return new Response('{"error":"You cannot use this filter"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("You cannot use this filter", 403); if (isNaN(before) || before > Date.now()) - return new Response('{"error":"Invalid `before` parameter"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Invalid `before` parameter", 400); const prefix = types[entryType]; const table = tables[entryType]; @@ -77,9 +64,5 @@ export async function onRequestGet(context: RequestContext) { } } - return new Response(JSON.stringify(items.filter((v) => v !== null)), { - headers: { - "content-type": "application/json", - }, - }); + return jsonResponse(JSON.stringify(items.filter((v) => v !== null))); } diff --git a/functions/api/reports/[id]/action.ts b/functions/api/reports/[id]/action.ts index 7accad7..f9fe11c 100644 --- a/functions/api/reports/[id]/action.ts +++ b/functions/api/reports/[id]/action.ts @@ -1,5 +1,6 @@ -import { insertLogs } from "../../../gcloud.js"; import { getBanList, setBanList } from "../../../roblox-open-cloud.js"; +import { insertLogs } from "../../../gcloud.js"; +import { jsonError } from "../../../common.js"; export async function onRequestPost(context: RequestContext) { const actionMap = context.data.body; @@ -13,11 +14,7 @@ export async function onRequestPost(context: RequestContext) { action < 0 || action > 2 ) - return new Response('{"error":"Invalid action map"}', { - headers: { - "content-type": "application/json", - }, - }); + return jsonError("Invalid action map", 400); if (action === 0) continue; diff --git a/functions/api/reports/complete.ts b/functions/api/reports/complete.ts index 0ca2186..4e67ca9 100644 --- a/functions/api/reports/complete.ts +++ b/functions/api/reports/complete.ts @@ -1,13 +1,9 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { id } = context.data.body; - if (!id) - return new Response('{"error":"No ID provided"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (!id) return jsonError("No ID provided", 400); const user = await context.env.DATA.get(`reportprocessing_${id}`); @@ -17,24 +13,13 @@ export async function onRequestPost(context: RequestContext) { ? user !== context.data.current_user.id : user !== context.request.headers.get("CF-Connecting-IP")) ) - return new Response('{"error":"No report with that ID is processing"}', { - headers: { - "content-type": "application/json", - }, - status: 404, - }); + return jsonError("No report with that ID is processing", 404); await context.env.DATA.delete(`reportprocessing_${id}`); const value = await context.env.DATA.get(`report_${id}`); - if (!value) - return new Response('{"error":"Report is missing"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + if (!value) return jsonError("Report is missing", 500); if (context.env.REPORTS_WEBHOOK) { await fetch(context.env.REPORTS_WEBHOOK, { diff --git a/functions/api/reports/recall.ts b/functions/api/reports/recall.ts index 9dbe166..dbd16e9 100644 --- a/functions/api/reports/recall.ts +++ b/functions/api/reports/recall.ts @@ -1,13 +1,9 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { id } = context.data.body; - if (!id) - return new Response('{"error":"No ID provided"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + if (!id) return jsonError("No ID provided", 400); const reportUserId = await context.env.DATA.get(`reportprocessing_${id}`); @@ -16,12 +12,7 @@ export async function onRequestPost(context: RequestContext) { (context.data.current_user?.id !== reportUserId && context.request.headers.get("CF-Connecting-IP") !== reportUserId) ) - return new Response('{"error":"No processing report with that ID found"}', { - headers: { - "content-type": "application/json", - }, - status: 404, - }); + return jsonError("No processing report with that ID found", 404); await context.env.DATA.delete(`report_${id}`); diff --git a/functions/api/reports/submit.ts b/functions/api/reports/submit.ts index 50a0198..b66e6e3 100644 --- a/functions/api/reports/submit.ts +++ b/functions/api/reports/submit.ts @@ -1,13 +1,5 @@ import { GenerateUploadURL } from "../../gcloud.js"; - -function errorResponse(error: string, status: number): Response { - return new Response(JSON.stringify({ error }), { - headers: { - "content-type": "application/json", - }, - status, - }); -} +import { jsonError, jsonResponse } from "../../common.js"; export async function onRequestPost(context: RequestContext) { const { actions, bypass, description, files, turnstileResponse, usernames } = @@ -15,7 +7,7 @@ export async function onRequestPost(context: RequestContext) { if (!context.data.current_user) { if (typeof turnstileResponse !== "string") - return errorResponse("You must complete the captcha", 401); + return jsonError("You must complete the captcha", 401); const turnstileAPIResponse = await fetch( "https://challenges.cloudflare.com/turnstile/v0/siteverify", @@ -34,26 +26,26 @@ export async function onRequestPost(context: RequestContext) { const { success }: { success: boolean } = await turnstileAPIResponse.json(); - if (!success) return errorResponse("Captcha test failed", 403); + if (!success) return jsonError("Captcha test failed", 403); } const origin = context.request.headers.get("Origin"); - if (!origin) return errorResponse("No origin header", 400); + if (!origin) return jsonError("No origin header", 400); if (bypass && !(context.data.current_user?.permissions & (1 << 5))) - return errorResponse("Bypass directive cannot be used", 403); + return jsonError("Bypass directive cannot be used", 403); if (typeof bypass !== "boolean") - return errorResponse("Bypass must be a boolean", 400); + return jsonError("Bypass must be a boolean", 400); if (!Array.isArray(usernames)) - return errorResponse("Usernames must be type of array", 400); + return jsonError("Usernames must be type of array", 400); if ( !["string", "undefined"].includes(typeof description) || description?.length > 512 ) - return errorResponse("Invalid description", 400); + return jsonError("Invalid description", 400); if ( !Array.isArray(files) || @@ -63,7 +55,7 @@ export async function onRequestPost(context: RequestContext) { return !keys.includes("name") || !keys.includes("size"); }) ) - return errorResponse("File list missing name(s) and/or size(s)", 400); + return jsonError("File list missing name(s) and/or size(s)", 400); if ( files.find( @@ -74,13 +66,10 @@ export async function onRequestPost(context: RequestContext) { file.size > 536870912, ) ) - return errorResponse( - "One or more files contain an invalid name or size", - 400, - ); + return jsonError("One or more files contain an invalid name or size", 400); if (!usernames.length || usernames.length > 20) - return errorResponse( + return jsonError( "Number of usernames provided must be between 1 and 20", 400, ); @@ -91,7 +80,7 @@ export async function onRequestPost(context: RequestContext) { username.length > 20 || username.match(/_/g)?.length > 1 ) - return errorResponse(`Username "${username}" is invalid`, 400); + return jsonError(`Username "${username}" is invalid`, 400); } const rbxSearchReq = await fetch( @@ -109,7 +98,7 @@ export async function onRequestPost(context: RequestContext) { ); if (!rbxSearchReq.ok) - return errorResponse( + return jsonError( "Failed to locate Roblox users due to upstream error", 500, ); @@ -125,7 +114,7 @@ export async function onRequestPost(context: RequestContext) { missingUsers.push(userData.requestedUsername); } - return errorResponse( + return jsonError( `The following users do not exist or are banned from Roblox: ${missingUsers.toString()}`, 400, ); @@ -165,7 +154,7 @@ export async function onRequestPost(context: RequestContext) { "webp", ].includes(fileExten.toLowerCase()) ) - return errorResponse( + return jsonError( `File ${file.name} cannot be uploaded as it is unsupported`, 415, ); @@ -202,7 +191,7 @@ export async function onRequestPost(context: RequestContext) { ); if (uploadUrlResults.find((uploadUrl) => uploadUrl.status === "rejected")) - return errorResponse("Failed to generate upload url", 500); + return jsonError("Failed to generate upload url", 500); const attachments: string[] = []; const uploadUrls: string[] = []; @@ -251,12 +240,7 @@ export async function onRequestPost(context: RequestContext) { .run(); } catch {} - return new Response( + return jsonResponse( JSON.stringify({ id: reportId, upload_urls: uploadUrls }), - { - headers: { - "content-type": "application/json", - }, - }, ); } diff --git a/functions/api/uploads/_middleware.ts b/functions/api/uploads/_middleware.ts index e3109cc..7e7aea9 100644 --- a/functions/api/uploads/_middleware.ts +++ b/functions/api/uploads/_middleware.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../common.js"; + export async function onRequest(context: RequestContext) { const { current_user: currentUser } = context.data; @@ -5,12 +7,7 @@ export async function onRequest(context: RequestContext) { !(currentUser?.permissions & (1 << 5)) && !(currentUser?.permissions & (1 << 12)) ) - return new Response('{"error":"Forbidden"}', { - headers: { - "content-type": "application/json", - }, - status: 403, - }); + return jsonError("Forbidden", 403); return await context.next(); } diff --git a/functions/api/uploads/status.ts b/functions/api/uploads/status.ts index 609baa8..134091f 100644 --- a/functions/api/uploads/status.ts +++ b/functions/api/uploads/status.ts @@ -1,3 +1,5 @@ +import { jsonError } from "../../common.js"; + export async function onRequestPost(context: RequestContext) { const { body } = context.data; @@ -5,23 +7,9 @@ export async function onRequestPost(context: RequestContext) { !Array.isArray(body) || body.find((attachment) => typeof attachment !== "string") ) - return new Response( - '{"error":"Request body must be an array of strings"}', - { - headers: { - "content-type": "application/json", - }, - status: 400, - }, - ); - - if (body.length > 3) - return new Response('{"error":"Too many video ids"}', { - headers: { - "content-type": "application/json", - }, - status: 400, - }); + return jsonError("Request body must be an array of strings", 400); + + if (body.length > 3) return jsonError("Too many video ids", 400); const kvPromises = []; @@ -31,12 +19,7 @@ export async function onRequestPost(context: RequestContext) { const kvResults = await Promise.allSettled(kvPromises); if (kvResults.find((result) => result.status === "rejected")) - return new Response('{"error":"Failed to check status of attachments"}', { - headers: { - "content-type": "application/json", - }, - status: 500, - }); + return jsonError("Failed to check status of attachments", 500); return new Response(null, { status: kvResults.find((result) => result !== null) ? 409 : 204,