Migrate the rest of the easy stuff
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 58s
Test, Build, Deploy / Create Sentry Release (push) Successful in 6s

This commit is contained in:
2026-04-11 04:32:09 -04:00
parent 48631e32be
commit 5c17f87f89
13 changed files with 244 additions and 177 deletions

View File

@@ -1,4 +1,8 @@
import { jsonError } from "../../../common.js"; import { jsonError } from "../../../common.js";
import {
Appeal,
PushNotification,
} from "../../../../generated/prisma/client.js";
export async function onRequestPost(context: RequestContext) { export async function onRequestPost(context: RequestContext) {
const { pathname } = new URL(context.request.url); const { pathname } = new URL(context.request.url);
@@ -20,22 +24,23 @@ export async function onRequestPost(context: RequestContext) {
context.data.targetId = id; context.data.targetId = id;
if (!pathname.endsWith("/ban")) { if (!pathname.endsWith("/ban")) {
const appeal: Record<string, any> | null = await context.env.D1.prepare( const appeal: Appeal | null = await context.data.prisma.appeal.findUnique({
"SELECT * FROM appeals WHERE id = ?;", where: {
) id: id,
.bind(id) },
.first(); });
if (!appeal) return jsonError("No appeal with that ID exists", 404); if (!appeal) return jsonError("No appeal with that ID exists", 404);
appeal.user = JSON.parse(appeal.user);
context.data.appeal = appeal; context.data.appeal = appeal;
const pushNotificationData = await context.env.D1.prepare( const pushNotificationData: PushNotification | null =
"SELECT token FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';", await context.data.prisma.pushNotification.findUnique({
) where: {
.bind(id) event_id: id,
.first(); event_type: "appeal",
},
});
if (pushNotificationData) if (pushNotificationData)
context.data.fcm_token = pushNotificationData.token; context.data.fcm_token = pushNotificationData.token;

View File

@@ -13,11 +13,12 @@ export async function onRequestPost(context: RequestContext) {
fcm_token, fcm_token,
); );
await context.env.D1.prepare( await context.data.prisma.pushNotification.delete({
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';", where: {
) event_id: appeal.id,
.bind(appeal.id) event_type: "appeal",
.run(); },
});
} else { } else {
const emailResponse = await sendEmail( const emailResponse = await sendEmail(
appeal.user.email, appeal.user.email,
@@ -37,11 +38,8 @@ export async function onRequestPost(context: RequestContext) {
const { current_user: currentUser } = context.data; const { current_user: currentUser } = context.data;
await context.env.D1.prepare( await context.data.prisma
"UPDATE appeals SET approved = 1, user = json_remove(user, '$.email') WHERE id = ?;", .$executeRaw`UPDATE appeals SET approved = TRUE, user = json_remove(user, '$.id') WHERE id = ${appeal.id};`;
)
.bind(context.params.id)
.run();
await fetch( await fetch(
`https://discord.com/api/v10/guilds/242263977986359297/bans/${appeal.user.id}`, `https://discord.com/api/v10/guilds/242263977986359297/bans/${appeal.user.id}`,

View File

@@ -6,9 +6,11 @@ export async function onRequestDelete(context: RequestContext) {
if (targetId.search(/^\d{16.19}$/) === -1) if (targetId.search(/^\d{16.19}$/) === -1)
return jsonError("Invalid target id", 400); return jsonError("Invalid target id", 400);
await context.env.D1.prepare("DELETE FROM appeal_bans WHERE user = ?;") await context.data.prisma.appealBan.delete({
.bind(targetId) where: {
.run(); user: targetId,
},
});
const { current_user: currentUser } = context.data; const { current_user: currentUser } = context.data;
@@ -46,11 +48,12 @@ export async function onRequestPost(context: RequestContext) {
if (targetId.search(/^\d{16,19}$/) === -1) if (targetId.search(/^\d{16,19}$/) === -1)
return jsonError("Invalid target id", 400); return jsonError("Invalid target id", 400);
await context.env.D1.prepare( await context.data.prisma.appealBan.create({
"INSERT INTO appeal_bans (created_at, created_by, user) VALUES (?, ?, ?);", data: {
) created_by: context.data.current_user.id,
.bind(Date.now(), context.data.current_user.id, targetId) user: targetId,
.run(); },
});
await fetch(context.env.APPEALS_WEBHOOK, { await fetch(context.env.APPEALS_WEBHOOK, {
body: JSON.stringify({ body: JSON.stringify({

View File

@@ -14,13 +14,27 @@ export async function onRequestGet(context: RequestContext) {
if (currentYear < year || (currentYear === year && currentMonth < month)) if (currentYear < year || (currentYear === year && currentMonth < month))
return jsonError("Cannot get events for a time in the future", 400); return jsonError("Cannot get events for a time in the future", 400);
const eventRecords = await context.env.D1.prepare( const eventRecords = await context.data.prisma.event.findMany({
"SELECT answer, approved, created_by, day, details, month, pending, performed_at, type, year FROM events WHERE month = ? AND year = ? ORDER BY day ASC;", select: {
) answer: true,
.bind(month, year) approved: true,
.all(); 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));
return jsonResponse(JSON.stringify(eventRecords.results));
} }

View File

@@ -7,19 +7,25 @@ export async function onRequestPost(context: RequestContext) {
if (statsReduction && typeof statsReduction !== "number") if (statsReduction && typeof statsReduction !== "number")
return jsonError("Invalid stat reduction", 400); return jsonError("Invalid stat reduction", 400);
const appeal: Record<string, any> | null = await context.env.D1.prepare( const appeal = await context.data.prisma.gameAppeal.findUnique({
"SELECT * FROM game_appeals WHERE id = ?;", select: {
) roblox_id: true,
.bind(context.params.id) type: true,
.first(); },
where: {
id: context.params.id as string,
},
});
if (!appeal) return jsonError("Appeal not found", 400); if (!appeal) return jsonError("Appeal not found", 400);
const { etag, value: banList } = await getBanList(context); const { etag, value: banList } = await getBanList(context);
await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;") await context.data.prisma.gameAppeal.delete({
.bind(context.params.id) where: {
.run(); id: context.params.id as string,
},
});
if (!banList[appeal.roblox_id]?.BanType) if (!banList[appeal.roblox_id]?.BanType)
return new Response(null, { return new Response(null, {
@@ -46,18 +52,15 @@ export async function onRequestPost(context: RequestContext) {
}; };
} }
await context.env.D1.prepare( await context.data.prisma.gameModLog.create({
"INSERT INTO game_mod_logs (action, evidence, executed_at, executor, id, target) VALUES (?, ?, ?, ?, ?, ?);", data: {
) action: `accept appeal | ${banList[appeal.roblox_id]?.BanType === 2 ? "ban" : appeal.type}`,
.bind( evidence: `https://carcrushers.cc/mod-queue?id=${context.params.id}&type=gma`,
`accept appeal | ${banList[appeal.roblox_id]?.BanType === 2 ? "ban" : appeal.type}`, executor: context.data.current_user.id,
`https://carcrushers.cc/mod-queue?id=${context.params.id}&type=gma`, id: crypto.randomUUID(),
Date.now(), target: appeal.roblox_id,
context.data.current_user.id, },
crypto.randomUUID(), });
appeal.roblox_id,
)
.run();
await setBanList(context, banList, etag); await setBanList(context, banList, etag);

View File

@@ -2,18 +2,22 @@ import { jsonError } from "../../../common.js";
export async function onRequestPost(context: RequestContext) { export async function onRequestPost(context: RequestContext) {
const appealId = context.params.id as string; const appealId = context.params.id as string;
const appeal = await context.data.prisma.gameAppeal.findUnique({
const appeal = await context.env.D1.prepare( select: {
"SELECT * FROM game_appeals WHERE id = ?;", roblox_id: true,
) },
.bind(appealId) where: {
.first(); id: appealId,
},
});
if (!appeal) return jsonError("Appeal not found", 404); if (!appeal) return jsonError("Appeal not found", 404);
await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;") await context.data.prisma.gameAppeal.delete({
.bind(appealId) where: {
.run(); id: appealId,
},
});
await context.env.DATA.put( await context.env.DATA.put(
`gameappealblock_${appeal.roblox_id}`, `gameappealblock_${appeal.roblox_id}`,

View File

@@ -58,13 +58,14 @@ export async function onRequestGet(context: RequestContext) {
} else if (banData.BanType === 2) current_status = "Banned"; } else if (banData.BanType === 2) current_status = "Banned";
const response = { const response = {
history: ( history: await context.data.prisma.gameModLog.findMany({
await context.env.D1.prepare( orderBy: {
"SELECT * FROM game_mod_logs WHERE target = ? ORDER BY executed_at DESC;", executed_at: "desc",
) },
.bind(users[0].id) where: {
.all() target: users[0].id,
).results, },
}),
user: { user: {
avatar: thumbnailRequest.ok avatar: thumbnailRequest.ok
? ( ? (

View File

@@ -2,38 +2,50 @@ import { jsonError, jsonResponse } from "../../../common.js";
export async function onRequestDelete(context: RequestContext) { export async function onRequestDelete(context: RequestContext) {
const noteId = context.params.id as string; const noteId = context.params.id as string;
const creatorIdResult: null | Record<string, string> = const creatorIdResult = await context.data.prisma.gameModNote.findUnique({
await context.env.D1.prepare( select: {
"SELECT created_by FROM game_mod_logs WHERE id = ?;", created_by: true,
) },
.bind(noteId) where: {
.first(); 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); 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 }); return new Response(null, { status: 204 });
} }
export async function onRequestGet(context: RequestContext) { export async function onRequestGet(context: RequestContext) {
const noteId = context.params.id as string; const noteId = context.params.id as string;
const result = await context.env.D1.prepare( const result = await context.data.prisma.gameModNote.findUnique({
"SELECT * FROM game_mod_notes WHERE id = ?;", where: {
) id: noteId,
.bind(noteId) },
.first(); });
if (!result) return jsonError("Note not found", 404); 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 } = const gmeEntry: null | { time: number; user: string; name: string } =
await context.env.DATA.get(`gamemod_${result.created_by}`, "json"); 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)); return jsonResponse(JSON.stringify(noteData));
} }

View File

@@ -1,18 +1,22 @@
import { jsonError, jsonResponse } from "../../common.js"; import { jsonError, jsonResponse } from "../../common.js";
import sendEmail from "../../email.js"; import sendEmail from "../../email.js";
import { sendPushNotification } from "../../gcloud.js"; import { sendPushNotification } from "../../gcloud.js";
import { type JsonArray, type JsonObject } from "@prisma/client/runtime/client";
export async function onRequestDelete(context: RequestContext) { export async function onRequestDelete(context: RequestContext) {
const result = await context.env.D1.prepare( const result = await context.data.prisma.inactivityNotice.findUnique({
"SELECT json_extract(user, '*.id') AS uid FROM inactivity_notices WHERE id = ?;", select: {
) user: true,
.bind(context.params.id) },
.first(); where: {
id: context.params.id as string,
},
});
if (!result) return jsonError("No inactivity notice with that ID", 404); if (!result) return jsonError("No inactivity notice with that ID", 404);
if ( 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)) !(context.data.current_user.permissions & (1 << 0))
) )
return jsonError( return jsonError(
@@ -39,26 +43,17 @@ export async function onRequestGet(context: RequestContext) {
) )
return jsonError("Forbidden", 403); return jsonError("Forbidden", 403);
const result: Record< const result = await context.data.prisma.inactivityNotice.findUnique({
string, where: {
string | number | { [k: string]: string } id: context.params.id as 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);
return jsonResponse(JSON.stringify(result)); return jsonResponse(JSON.stringify(result));
} }
export async function onRequestPost(context: RequestContext) { export async function onRequestPost(context: RequestContext) {
const { accepted }: { accepted?: boolean } = context.data.body; const { accepted }: { accepted?: any } = context.data.body;
if (typeof accepted !== "boolean") if (typeof accepted !== "boolean")
return jsonError("'accepted' must be a boolean", 400); return jsonError("'accepted' must be a boolean", 400);
@@ -77,32 +72,45 @@ export async function onRequestPost(context: RequestContext) {
if (!userAdminDepartments.length) if (!userAdminDepartments.length)
return jsonError("You are not a manager of any departments", 403); return jsonError("You are not a manager of any departments", 403);
const requestedNotice: { [k: string]: any } | null = const requestedNotice = await context.data.prisma.inactivityNotice.findUnique(
await context.env.D1.prepare( {
"SELECT decisions, departments, user FROM inactivity_notices WHERE id = ?;", select: {
) decisions: true,
.bind(context.params.id) departments: true,
.first(); user: true,
},
where: {
id: context.params.id as string,
},
},
);
if (!requestedNotice) if (!requestedNotice)
return jsonError("Inactivity notices does not exist", 404); return jsonError("Inactivity notices does not exist", 404);
const decisions: { [dept: string]: boolean } = JSON.parse( const decisions = requestedNotice.decisions as { [k: string]: boolean };
requestedNotice.decisions,
);
for (const department of userAdminDepartments) { for (const department of userAdminDepartments) {
if (!JSON.parse(requestedNotice.departments).includes(department)) continue; if (!(requestedNotice.departments as JsonArray).includes(department))
continue;
decisions[department] = accepted; 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( delete user.email;
"UPDATE inactivity_notices SET decisions = ?, user = json_remove(user, '$.email') WHERE id = ?;",
) await context.data.prisma.inactivityNotice.update({
.bind(JSON.stringify(decisions), context.params.id) data: {
.run(); decisions,
user,
},
where: {
id: context.params.id as string,
},
});
if (Object.values(decisions).length === applicableDepartments) { if (Object.values(decisions).length === applicableDepartments) {
const approved = const approved =
@@ -111,11 +119,16 @@ export async function onRequestPost(context: RequestContext) {
const denied = const denied =
Object.values(decisions).filter((d) => !d).length !== Object.values(decisions).filter((d) => !d).length !==
applicableDepartments; applicableDepartments;
const fcmTokenResult: FCMTokenResult | null = await context.env.D1.prepare( const fcmTokenResult =
"SELECT token FROM push_notifications WHERE event_id = ? AND event_type = 'inactivity';", await context.data.prisma.pushNotification.findUnique({
) select: {
.bind(context.params.id) token: true,
.first(); },
where: {
event_id: context.params.id as string,
event_type: "inactivity",
},
});
if (fcmTokenResult) { if (fcmTokenResult) {
let status = "Approved"; let status = "Approved";
@@ -132,16 +145,19 @@ export async function onRequestPost(context: RequestContext) {
fcmTokenResult.token, fcmTokenResult.token,
); );
await context.env.D1.prepare( await context.data.prisma.pushNotification.delete({
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'inactivity';", where: {
).bind(context.params.id); event_id: context.params.id as string,
event_type: "inactivity",
},
});
} else { } else {
await sendEmail( await sendEmail(
requestedNotice.user.email, (requestedNotice.user as JsonObject).email as string,
context.env.MAILGUN_API_KEY, context.env.MAILGUN_API_KEY,
`Inactivity Request ${approved ? "Approved" : "Denied"}`, `Inactivity Request ${approved ? "Approved" : "Denied"}`,
`inactivity_${approved ? "approved" : "denied"}`, `inactivity_${approved ? "approved" : "denied"}`,
{ username: requestedNotice.user.username }, { username: (requestedNotice.user as JsonObject).username as string },
); );
} }
} }

View File

@@ -20,31 +20,31 @@ export async function onRequestPost(context: RequestContext) {
(context.request.headers.get("cf-ray") as string).split("-")[0] + (context.request.headers.get("cf-ray") as string).split("-")[0] +
Date.now().toString(); Date.now().toString();
await context.env.D1.prepare( await context.data.prisma.inactivityNotice.create({
"INSERT INTO inactivity_notices (created_at, departments, end, hiatus, id, reason, start, user) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", data: {
) decisions: {},
.bind( departments,
Date.now(),
JSON.stringify(departments),
end, end,
typeof hiatus === "boolean" ? Number(hiatus) : 0, hiatus,
inactivityId, id: inactivityId,
reason, reason,
start, start,
JSON.stringify({ user: {
id: context.data.current_user.id, id: context.data.current_user.id,
email: context.data.current_user.email, email: context.data.current_user.email,
username: context.data.current_user.username, username: context.data.current_user.username,
}), },
) },
.run(); });
if (typeof senderTokenId === "string") { if (typeof senderTokenId === "string") {
await context.env.D1.prepare( await context.data.prisma.pushNotification.create({
"INSERT INTO push_notifications (created_at, event_id, event_type) VALUES (?, ?, ?);", data: {
) event_id: inactivityId,
.bind(Date.now(), inactivityId, "inactivity") event_type: "inactivity",
.run(); token: senderTokenId,
},
});
} }
const departmentsToNotify = []; const departmentsToNotify = [];

View File

@@ -17,11 +17,10 @@ export async function onRequestPost(context: RequestContext) {
await context.env.DATA.delete(`reportprocessing_${id}`); await context.env.DATA.delete(`reportprocessing_${id}`);
const value = await context.env.D1.prepare( const value = await context.data.prisma.report.findUnique({
"SELECT id FROM reports WHERE id = ?;", select: { id: true },
) where: { id },
.bind(id) });
.first();
if (!value) return jsonError("Report is missing", 500); if (!value) return jsonError("Report is missing", 500);

View File

@@ -1,11 +1,18 @@
import { jsonResponse } from "../../common.js"; import { jsonResponse } from "../../common.js";
export async function onRequestGet(context: RequestContext) { export async function onRequestGet(context: RequestContext) {
const { results } = await context.env.D1.prepare( return jsonResponse(
"SELECT created_at, destination, path FROM short_links WHERE user = ?;", JSON.stringify(
) await context.data.prisma.shortLink.findMany({
.bind(context.data.current_user.id) select: {
.all(); created_at: true,
destination: true,
return jsonResponse(JSON.stringify(results)); path: true,
},
where: {
user: context.data.current_user.id,
},
}),
),
);
} }

View File

@@ -6,11 +6,14 @@ export async function onRequestPost(context: RequestContext) {
if (typeof path !== "string" || path.length > 256) if (typeof path !== "string" || path.length > 256)
return jsonError("Invalid path", 400); return jsonError("Invalid path", 400);
const result = await context.env.D1.prepare( const result = await context.data.prisma.shortLink.findUnique({
"SELECT path FROM short_links WHERE path = ?;", select: {
) path: true,
.bind(path) },
.first(); where: {
path,
},
});
if (result) if (result)
return jsonError( return jsonError(
@@ -78,11 +81,13 @@ export async function onRequestPost(context: RequestContext) {
400, 400,
); );
await context.env.D1.prepare( await context.data.prisma.shortLink.create({
"INSERT INTO short_links (created_at, destination, path, user) VALUES (?, ?, ?, ?);", data: {
) destination,
.bind(Date.now(), destination, path, context.data.current_user.id) path,
.run(); user: context.data.current_user.id,
},
});
return new Response(null, { return new Response(null, {
status: 204, status: 204,