From 5c17f87f89615fe2b39364f4a8fa7d38232f5774 Mon Sep 17 00:00:00 2001 From: Regalijan Date: Sat, 11 Apr 2026 04:32:09 -0400 Subject: [PATCH] Migrate the rest of the easy stuff --- functions/api/appeals/[id]/_middleware.ts | 27 +++--- functions/api/appeals/[id]/accept.ts | 18 ++-- functions/api/appeals/[id]/ban.ts | 19 ++-- functions/api/events-team/events/list.ts | 30 ++++-- functions/api/game-appeals/[id]/accept.ts | 43 +++++---- functions/api/game-appeals/[id]/deny.ts | 22 +++-- functions/api/game-bans/[user]/history.ts | 15 +-- functions/api/game-bans/notes/[id].ts | 48 ++++++---- functions/api/inactivity/[id].ts | 110 +++++++++++++--------- functions/api/inactivity/new.ts | 34 +++---- functions/api/reports/complete.ts | 9 +- functions/api/short-links/list.ts | 21 +++-- functions/api/short-links/new.ts | 25 +++-- 13 files changed, 244 insertions(+), 177 deletions(-) diff --git a/functions/api/appeals/[id]/_middleware.ts b/functions/api/appeals/[id]/_middleware.ts index 6c4ecaa..134ad00 100644 --- a/functions/api/appeals/[id]/_middleware.ts +++ b/functions/api/appeals/[id]/_middleware.ts @@ -1,4 +1,8 @@ import { jsonError } from "../../../common.js"; +import { + Appeal, + PushNotification, +} from "../../../../generated/prisma/client.js"; export async function onRequestPost(context: RequestContext) { const { pathname } = new URL(context.request.url); @@ -20,22 +24,23 @@ export async function onRequestPost(context: RequestContext) { context.data.targetId = id; if (!pathname.endsWith("/ban")) { - const appeal: Record | null = await context.env.D1.prepare( - "SELECT * FROM appeals WHERE id = ?;", - ) - .bind(id) - .first(); + const appeal: Appeal | null = await context.data.prisma.appeal.findUnique({ + where: { + id: id, + }, + }); if (!appeal) return jsonError("No appeal with that ID exists", 404); - appeal.user = JSON.parse(appeal.user); context.data.appeal = appeal; - const pushNotificationData = await context.env.D1.prepare( - "SELECT token FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';", - ) - .bind(id) - .first(); + const pushNotificationData: PushNotification | null = + await context.data.prisma.pushNotification.findUnique({ + where: { + event_id: id, + event_type: "appeal", + }, + }); if (pushNotificationData) context.data.fcm_token = pushNotificationData.token; diff --git a/functions/api/appeals/[id]/accept.ts b/functions/api/appeals/[id]/accept.ts index 1a8e16d..524d07d 100644 --- a/functions/api/appeals/[id]/accept.ts +++ b/functions/api/appeals/[id]/accept.ts @@ -13,11 +13,12 @@ export async function onRequestPost(context: RequestContext) { fcm_token, ); - await context.env.D1.prepare( - "DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';", - ) - .bind(appeal.id) - .run(); + await context.data.prisma.pushNotification.delete({ + where: { + event_id: appeal.id, + event_type: "appeal", + }, + }); } else { const emailResponse = await sendEmail( appeal.user.email, @@ -37,11 +38,8 @@ export async function onRequestPost(context: RequestContext) { const { current_user: currentUser } = context.data; - await context.env.D1.prepare( - "UPDATE appeals SET approved = 1, user = json_remove(user, '$.email') WHERE id = ?;", - ) - .bind(context.params.id) - .run(); + await context.data.prisma + .$executeRaw`UPDATE appeals SET approved = TRUE, user = json_remove(user, '$.id') WHERE id = ${appeal.id};`; await fetch( `https://discord.com/api/v10/guilds/242263977986359297/bans/${appeal.user.id}`, diff --git a/functions/api/appeals/[id]/ban.ts b/functions/api/appeals/[id]/ban.ts index ecbba34..9621c81 100644 --- a/functions/api/appeals/[id]/ban.ts +++ b/functions/api/appeals/[id]/ban.ts @@ -6,9 +6,11 @@ export async function onRequestDelete(context: RequestContext) { if (targetId.search(/^\d{16.19}$/) === -1) return jsonError("Invalid target id", 400); - await context.env.D1.prepare("DELETE FROM appeal_bans WHERE user = ?;") - .bind(targetId) - .run(); + await context.data.prisma.appealBan.delete({ + where: { + user: targetId, + }, + }); const { current_user: currentUser } = context.data; @@ -46,11 +48,12 @@ export async function onRequestPost(context: RequestContext) { if (targetId.search(/^\d{16,19}$/) === -1) return jsonError("Invalid target id", 400); - await context.env.D1.prepare( - "INSERT INTO appeal_bans (created_at, created_by, user) VALUES (?, ?, ?);", - ) - .bind(Date.now(), context.data.current_user.id, targetId) - .run(); + await context.data.prisma.appealBan.create({ + data: { + created_by: context.data.current_user.id, + user: targetId, + }, + }); await fetch(context.env.APPEALS_WEBHOOK, { body: JSON.stringify({ diff --git a/functions/api/events-team/events/list.ts b/functions/api/events-team/events/list.ts index 9c817b5..c72f868 100644 --- a/functions/api/events-team/events/list.ts +++ b/functions/api/events-team/events/list.ts @@ -14,13 +14,27 @@ export async function onRequestGet(context: RequestContext) { if (currentYear < year || (currentYear === year && currentMonth < month)) return jsonError("Cannot get events for a time in the future", 400); - const eventRecords = await context.env.D1.prepare( - "SELECT answer, approved, created_by, day, details, month, pending, performed_at, type, year FROM events WHERE month = ? AND year = ? ORDER BY day ASC;", - ) - .bind(month, year) - .all(); + const eventRecords = await context.data.prisma.event.findMany({ + select: { + answer: true, + approved: true, + created_by: true, + day: true, + details: true, + month: true, + pending: true, + performed_at: true, + type: true, + year: true, + }, + where: { + month: month, + year: year, + }, + orderBy: { + day: "asc", + }, + }); - if (!eventRecords.success) return jsonError("Failed to retrieve events", 400); - - return jsonResponse(JSON.stringify(eventRecords.results)); + return jsonResponse(JSON.stringify(eventRecords)); } diff --git a/functions/api/game-appeals/[id]/accept.ts b/functions/api/game-appeals/[id]/accept.ts index 05be642..0dc0c92 100644 --- a/functions/api/game-appeals/[id]/accept.ts +++ b/functions/api/game-appeals/[id]/accept.ts @@ -7,19 +7,25 @@ export async function onRequestPost(context: RequestContext) { if (statsReduction && typeof statsReduction !== "number") return jsonError("Invalid stat reduction", 400); - const appeal: Record | null = await context.env.D1.prepare( - "SELECT * FROM game_appeals WHERE id = ?;", - ) - .bind(context.params.id) - .first(); + const appeal = await context.data.prisma.gameAppeal.findUnique({ + select: { + roblox_id: true, + type: true, + }, + where: { + id: context.params.id as string, + }, + }); if (!appeal) return jsonError("Appeal not found", 400); const { etag, value: banList } = await getBanList(context); - await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;") - .bind(context.params.id) - .run(); + await context.data.prisma.gameAppeal.delete({ + where: { + id: context.params.id as string, + }, + }); if (!banList[appeal.roblox_id]?.BanType) return new Response(null, { @@ -46,18 +52,15 @@ export async function onRequestPost(context: RequestContext) { }; } - await context.env.D1.prepare( - "INSERT INTO game_mod_logs (action, evidence, executed_at, executor, id, target) VALUES (?, ?, ?, ?, ?, ?);", - ) - .bind( - `accept appeal | ${banList[appeal.roblox_id]?.BanType === 2 ? "ban" : appeal.type}`, - `https://carcrushers.cc/mod-queue?id=${context.params.id}&type=gma`, - Date.now(), - context.data.current_user.id, - crypto.randomUUID(), - appeal.roblox_id, - ) - .run(); + await context.data.prisma.gameModLog.create({ + data: { + action: `accept appeal | ${banList[appeal.roblox_id]?.BanType === 2 ? "ban" : appeal.type}`, + evidence: `https://carcrushers.cc/mod-queue?id=${context.params.id}&type=gma`, + executor: context.data.current_user.id, + id: crypto.randomUUID(), + target: appeal.roblox_id, + }, + }); await setBanList(context, banList, etag); diff --git a/functions/api/game-appeals/[id]/deny.ts b/functions/api/game-appeals/[id]/deny.ts index d483a0c..04a5a71 100644 --- a/functions/api/game-appeals/[id]/deny.ts +++ b/functions/api/game-appeals/[id]/deny.ts @@ -2,18 +2,22 @@ import { jsonError } from "../../../common.js"; export async function onRequestPost(context: RequestContext) { const appealId = context.params.id as string; - - const appeal = await context.env.D1.prepare( - "SELECT * FROM game_appeals WHERE id = ?;", - ) - .bind(appealId) - .first(); + const appeal = await context.data.prisma.gameAppeal.findUnique({ + select: { + roblox_id: true, + }, + where: { + id: appealId, + }, + }); if (!appeal) return jsonError("Appeal not found", 404); - await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;") - .bind(appealId) - .run(); + await context.data.prisma.gameAppeal.delete({ + where: { + id: appealId, + }, + }); await context.env.DATA.put( `gameappealblock_${appeal.roblox_id}`, diff --git a/functions/api/game-bans/[user]/history.ts b/functions/api/game-bans/[user]/history.ts index 0cf1b90..c9d2dc6 100644 --- a/functions/api/game-bans/[user]/history.ts +++ b/functions/api/game-bans/[user]/history.ts @@ -58,13 +58,14 @@ export async function onRequestGet(context: RequestContext) { } else if (banData.BanType === 2) current_status = "Banned"; const response = { - history: ( - await context.env.D1.prepare( - "SELECT * FROM game_mod_logs WHERE target = ? ORDER BY executed_at DESC;", - ) - .bind(users[0].id) - .all() - ).results, + history: await context.data.prisma.gameModLog.findMany({ + orderBy: { + executed_at: "desc", + }, + where: { + target: users[0].id, + }, + }), user: { avatar: thumbnailRequest.ok ? ( diff --git a/functions/api/game-bans/notes/[id].ts b/functions/api/game-bans/notes/[id].ts index 50b92e3..4c4394b 100644 --- a/functions/api/game-bans/notes/[id].ts +++ b/functions/api/game-bans/notes/[id].ts @@ -2,38 +2,50 @@ import { jsonError, jsonResponse } from "../../../common.js"; export async function onRequestDelete(context: RequestContext) { const noteId = context.params.id as string; - const creatorIdResult: null | Record = - await context.env.D1.prepare( - "SELECT created_by FROM game_mod_logs WHERE id = ?;", - ) - .bind(noteId) - .first(); + const creatorIdResult = await context.data.prisma.gameModNote.findUnique({ + select: { + created_by: true, + }, + where: { + id: noteId, + }, + }); - if (creatorIdResult?.created_by !== context.data.current_user.id) + try { + await context.data.prisma.gameModNote.delete({ + select: { + id: true, + }, + where: { + created_by: context.data.current_user.id, + id: noteId, + }, + }); + } catch { return jsonError("Cannot delete notes that are not your own", 403); - - await context.env.D1.prepare("DELETE FROM game_mod_logs WHERE id = ?;") - .bind(noteId) - .first(); + } return new Response(null, { status: 204 }); } export async function onRequestGet(context: RequestContext) { const noteId = context.params.id as string; - const result = await context.env.D1.prepare( - "SELECT * FROM game_mod_notes WHERE id = ?;", - ) - .bind(noteId) - .first(); + const result = await context.data.prisma.gameModNote.findUnique({ + where: { + id: noteId, + }, + }); if (!result) return jsonError("Note not found", 404); - const noteData = structuredClone(result); + let noteData = structuredClone(result); const gmeEntry: null | { time: number; user: string; name: string } = await context.env.DATA.get(`gamemod_${result.created_by}`, "json"); - if (gmeEntry) noteData.creator_name = gmeEntry.name; + if (gmeEntry) + noteData = Object.defineProperty(noteData, "creator_name", { + value: gmeEntry.name, + }); return jsonResponse(JSON.stringify(noteData)); } diff --git a/functions/api/inactivity/[id].ts b/functions/api/inactivity/[id].ts index 138dfa5..74b3927 100644 --- a/functions/api/inactivity/[id].ts +++ b/functions/api/inactivity/[id].ts @@ -1,18 +1,22 @@ import { jsonError, jsonResponse } from "../../common.js"; import sendEmail from "../../email.js"; import { sendPushNotification } from "../../gcloud.js"; +import { type JsonArray, type JsonObject } from "@prisma/client/runtime/client"; export async function onRequestDelete(context: RequestContext) { - const result = await context.env.D1.prepare( - "SELECT json_extract(user, '*.id') AS uid FROM inactivity_notices WHERE id = ?;", - ) - .bind(context.params.id) - .first(); + const result = await context.data.prisma.inactivityNotice.findUnique({ + select: { + user: true, + }, + where: { + id: context.params.id as string, + }, + }); if (!result) return jsonError("No inactivity notice with that ID", 404); if ( - result.uid !== context.data.current_user.id && + (result.user as JsonObject).id !== context.data.current_user.id && !(context.data.current_user.permissions & (1 << 0)) ) return jsonError( @@ -39,26 +43,17 @@ export async function onRequestGet(context: RequestContext) { ) return jsonError("Forbidden", 403); - const result: Record< - string, - string | number | { [k: string]: string } - > | null = await context.env.D1.prepare( - "SELECT * FROM inactivity_notices WHERE id = ?;", - ) - .bind(context.params.id) - .first(); - - if (!result) return jsonError("Inactivity notice does not exist", 404); - - result.decisions = JSON.parse(result.decisions as string); - result.departments = JSON.parse(result.departments as string); - result.user = JSON.parse(result.user as string); + const result = await context.data.prisma.inactivityNotice.findUnique({ + where: { + id: context.params.id as string, + }, + }); return jsonResponse(JSON.stringify(result)); } export async function onRequestPost(context: RequestContext) { - const { accepted }: { accepted?: boolean } = context.data.body; + const { accepted }: { accepted?: any } = context.data.body; if (typeof accepted !== "boolean") return jsonError("'accepted' must be a boolean", 400); @@ -77,32 +72,45 @@ export async function onRequestPost(context: RequestContext) { if (!userAdminDepartments.length) return jsonError("You are not a manager of any departments", 403); - const requestedNotice: { [k: string]: any } | null = - await context.env.D1.prepare( - "SELECT decisions, departments, user FROM inactivity_notices WHERE id = ?;", - ) - .bind(context.params.id) - .first(); + const requestedNotice = await context.data.prisma.inactivityNotice.findUnique( + { + select: { + decisions: true, + departments: true, + user: true, + }, + where: { + id: context.params.id as string, + }, + }, + ); if (!requestedNotice) return jsonError("Inactivity notices does not exist", 404); - const decisions: { [dept: string]: boolean } = JSON.parse( - requestedNotice.decisions, - ); + const decisions = requestedNotice.decisions as { [k: string]: boolean }; for (const department of userAdminDepartments) { - if (!JSON.parse(requestedNotice.departments).includes(department)) continue; + if (!(requestedNotice.departments as JsonArray).includes(department)) + continue; decisions[department] = accepted; } - const applicableDepartments = JSON.parse(requestedNotice.departments).length; + const applicableDepartments = (requestedNotice.departments as JsonArray) + .length; + const user = requestedNotice.user as JsonObject; - await context.env.D1.prepare( - "UPDATE inactivity_notices SET decisions = ?, user = json_remove(user, '$.email') WHERE id = ?;", - ) - .bind(JSON.stringify(decisions), context.params.id) - .run(); + delete user.email; + + await context.data.prisma.inactivityNotice.update({ + data: { + decisions, + user, + }, + where: { + id: context.params.id as string, + }, + }); if (Object.values(decisions).length === applicableDepartments) { const approved = @@ -111,11 +119,16 @@ export async function onRequestPost(context: RequestContext) { const denied = Object.values(decisions).filter((d) => !d).length !== applicableDepartments; - const fcmTokenResult: FCMTokenResult | null = await context.env.D1.prepare( - "SELECT token FROM push_notifications WHERE event_id = ? AND event_type = 'inactivity';", - ) - .bind(context.params.id) - .first(); + const fcmTokenResult = + await context.data.prisma.pushNotification.findUnique({ + select: { + token: true, + }, + where: { + event_id: context.params.id as string, + event_type: "inactivity", + }, + }); if (fcmTokenResult) { let status = "Approved"; @@ -132,16 +145,19 @@ export async function onRequestPost(context: RequestContext) { fcmTokenResult.token, ); - await context.env.D1.prepare( - "DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'inactivity';", - ).bind(context.params.id); + await context.data.prisma.pushNotification.delete({ + where: { + event_id: context.params.id as string, + event_type: "inactivity", + }, + }); } else { await sendEmail( - requestedNotice.user.email, + (requestedNotice.user as JsonObject).email as string, context.env.MAILGUN_API_KEY, `Inactivity Request ${approved ? "Approved" : "Denied"}`, `inactivity_${approved ? "approved" : "denied"}`, - { username: requestedNotice.user.username }, + { username: (requestedNotice.user as JsonObject).username as string }, ); } } diff --git a/functions/api/inactivity/new.ts b/functions/api/inactivity/new.ts index 929f6b7..112b256 100644 --- a/functions/api/inactivity/new.ts +++ b/functions/api/inactivity/new.ts @@ -20,31 +20,31 @@ export async function onRequestPost(context: RequestContext) { (context.request.headers.get("cf-ray") as string).split("-")[0] + Date.now().toString(); - await context.env.D1.prepare( - "INSERT INTO inactivity_notices (created_at, departments, end, hiatus, id, reason, start, user) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", - ) - .bind( - Date.now(), - JSON.stringify(departments), + await context.data.prisma.inactivityNotice.create({ + data: { + decisions: {}, + departments, end, - typeof hiatus === "boolean" ? Number(hiatus) : 0, - inactivityId, + hiatus, + id: inactivityId, reason, start, - JSON.stringify({ + user: { id: context.data.current_user.id, email: context.data.current_user.email, username: context.data.current_user.username, - }), - ) - .run(); + }, + }, + }); if (typeof senderTokenId === "string") { - await context.env.D1.prepare( - "INSERT INTO push_notifications (created_at, event_id, event_type) VALUES (?, ?, ?);", - ) - .bind(Date.now(), inactivityId, "inactivity") - .run(); + await context.data.prisma.pushNotification.create({ + data: { + event_id: inactivityId, + event_type: "inactivity", + token: senderTokenId, + }, + }); } const departmentsToNotify = []; diff --git a/functions/api/reports/complete.ts b/functions/api/reports/complete.ts index f02dd8a..d6808a1 100644 --- a/functions/api/reports/complete.ts +++ b/functions/api/reports/complete.ts @@ -17,11 +17,10 @@ export async function onRequestPost(context: RequestContext) { await context.env.DATA.delete(`reportprocessing_${id}`); - const value = await context.env.D1.prepare( - "SELECT id FROM reports WHERE id = ?;", - ) - .bind(id) - .first(); + const value = await context.data.prisma.report.findUnique({ + select: { id: true }, + where: { id }, + }); if (!value) return jsonError("Report is missing", 500); diff --git a/functions/api/short-links/list.ts b/functions/api/short-links/list.ts index d242c57..32c0851 100644 --- a/functions/api/short-links/list.ts +++ b/functions/api/short-links/list.ts @@ -1,11 +1,18 @@ import { jsonResponse } from "../../common.js"; export async function onRequestGet(context: RequestContext) { - const { results } = await context.env.D1.prepare( - "SELECT created_at, destination, path FROM short_links WHERE user = ?;", - ) - .bind(context.data.current_user.id) - .all(); - - return jsonResponse(JSON.stringify(results)); + return jsonResponse( + JSON.stringify( + await context.data.prisma.shortLink.findMany({ + select: { + created_at: true, + destination: true, + path: true, + }, + where: { + user: context.data.current_user.id, + }, + }), + ), + ); } diff --git a/functions/api/short-links/new.ts b/functions/api/short-links/new.ts index 3a2fc6e..c4f992c 100644 --- a/functions/api/short-links/new.ts +++ b/functions/api/short-links/new.ts @@ -6,11 +6,14 @@ export async function onRequestPost(context: RequestContext) { if (typeof path !== "string" || path.length > 256) return jsonError("Invalid path", 400); - const result = await context.env.D1.prepare( - "SELECT path FROM short_links WHERE path = ?;", - ) - .bind(path) - .first(); + const result = await context.data.prisma.shortLink.findUnique({ + select: { + path: true, + }, + where: { + path, + }, + }); if (result) return jsonError( @@ -78,11 +81,13 @@ export async function onRequestPost(context: RequestContext) { 400, ); - await context.env.D1.prepare( - "INSERT INTO short_links (created_at, destination, path, user) VALUES (?, ?, ?, ?);", - ) - .bind(Date.now(), destination, path, context.data.current_user.id) - .run(); + await context.data.prisma.shortLink.create({ + data: { + destination, + path, + user: context.data.current_user.id, + }, + }); return new Response(null, { status: 204,