KV to D1 migration (this is totally gonna break something)
This commit is contained in:
parent
a2b3391bda
commit
e00b7e8c55
@ -196,7 +196,7 @@ export default function () {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
queueReq = await fetch(
|
queueReq = await fetch(
|
||||||
`/api/mod-queue/list?before=${before}&showClosed=${show_closed}&type=${queueType}`,
|
`/api/mod-queue/${queueType}/list?before=${before}&showClosed=${show_closed}`,
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
alert("Failed to load mod queue");
|
alert("Failed to load mod queue");
|
||||||
|
@ -20,11 +20,25 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
context.data.targetId = id;
|
context.data.targetId = id;
|
||||||
|
|
||||||
if (!pathname.endsWith("/ban")) {
|
if (!pathname.endsWith("/ban")) {
|
||||||
const key = await context.env.DATA.get(`appeal_${id}`);
|
const appeal: Record<string, any> | null = await context.env.D1.prepare(
|
||||||
|
"SELECT * FROM appeals WHERE id = ?;",
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!key) return jsonError("No appeal with that ID exists", 404);
|
if (!appeal) return jsonError("No appeal with that ID exists", 404);
|
||||||
|
|
||||||
context.data.appeal = JSON.parse(key);
|
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();
|
||||||
|
|
||||||
|
if (pushNotificationData)
|
||||||
|
context.data.fcm_token = pushNotificationData.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -3,15 +3,21 @@ import sendEmail from "../../../email.js";
|
|||||||
import { sendPushNotification } from "../../../gcloud.js";
|
import { sendPushNotification } from "../../../gcloud.js";
|
||||||
|
|
||||||
export async function onRequestPost(context: RequestContext) {
|
export async function onRequestPost(context: RequestContext) {
|
||||||
const { appeal } = context.data;
|
const { appeal, fcm_token } = context.data;
|
||||||
|
|
||||||
if (appeal.fcm_token) {
|
if (fcm_token) {
|
||||||
await sendPushNotification(
|
await sendPushNotification(
|
||||||
context.env,
|
context.env,
|
||||||
"Appeal Accepted",
|
"Appeal Accepted",
|
||||||
context.data.body.feedback || "No additional details to display",
|
context.data.body.feedback || "No additional details to display",
|
||||||
appeal.fcm_token
|
fcm_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';",
|
||||||
|
)
|
||||||
|
.bind(appeal.id)
|
||||||
|
.run();
|
||||||
} else {
|
} else {
|
||||||
const emailResponse = await sendEmail(
|
const emailResponse = await sendEmail(
|
||||||
appeal.user.email,
|
appeal.user.email,
|
||||||
@ -19,8 +25,8 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
"Appeal Accepted",
|
"Appeal Accepted",
|
||||||
"appeal_accepted",
|
"appeal_accepted",
|
||||||
{
|
{
|
||||||
note: context.data.body.feedback || "No note provided."
|
note: context.data.body.feedback || "No note provided.",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!emailResponse.ok) {
|
if (!emailResponse.ok) {
|
||||||
@ -32,29 +38,21 @@ 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.env.D1.prepare(
|
||||||
"UPDATE appeals SET approved = 1, open = 0 WHERE id = ?;"
|
"UPDATE appeals SET approved = 1, user = json_remove(user, '$.email') WHERE id = ?;",
|
||||||
)
|
)
|
||||||
.bind(context.params.id)
|
.bind(context.params.id)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
delete appeal.fcm_token;
|
|
||||||
delete appeal.user.email;
|
|
||||||
|
|
||||||
appeal.open = false;
|
|
||||||
|
|
||||||
await context.env.DATA.put(`appeal_${appeal.id}`, JSON.stringify(appeal), {
|
|
||||||
expirationTtl: 94608000
|
|
||||||
});
|
|
||||||
|
|
||||||
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}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bot ${context.env.BOT_TOKEN}`,
|
authorization: `Bot ${context.env.BOT_TOKEN}`,
|
||||||
"x-audit-log-reason": `Appeal accepted by ${currentUser.username} (${currentUser.id})`
|
"x-audit-log-reason": `Appeal accepted by ${currentUser.username} (${currentUser.id})`,
|
||||||
},
|
},
|
||||||
method: "DELETE"
|
method: "DELETE",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await fetch(context.env.APPEALS_WEBHOOK, {
|
await fetch(context.env.APPEALS_WEBHOOK, {
|
||||||
@ -67,19 +65,19 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "Moderator",
|
name: "Moderator",
|
||||||
value: `${currentUser.username} (${currentUser.id})`
|
value: `${currentUser.username} (${currentUser.id})`,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json"
|
"content-type": "application/json",
|
||||||
},
|
},
|
||||||
method: "POST"
|
method: "POST",
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 204
|
status: 204,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,22 @@ import sendEmail from "../../../email.js";
|
|||||||
import { sendPushNotification } from "../../../gcloud.js";
|
import { sendPushNotification } from "../../../gcloud.js";
|
||||||
|
|
||||||
export async function onRequestPost(context: RequestContext) {
|
export async function onRequestPost(context: RequestContext) {
|
||||||
const { appeal } = context.data;
|
const { appeal, fcm_token } = context.data;
|
||||||
if (appeal.fcm_token) {
|
if (fcm_token) {
|
||||||
await sendPushNotification(
|
await sendPushNotification(
|
||||||
context.env,
|
context.env,
|
||||||
"Appeal Denied",
|
"Appeal Denied",
|
||||||
`Unfortunately, we have decided to deny your appeal for the following reason: ${
|
`Unfortunately, we have decided to deny your appeal for the following reason: ${
|
||||||
context.data.body.feedback || "No additional details"
|
context.data.body.feedback || "No additional details"
|
||||||
}`,
|
}`,
|
||||||
appeal.fcm_token,
|
fcm_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'appeal';",
|
||||||
|
)
|
||||||
|
.bind(appeal.id)
|
||||||
|
.run();
|
||||||
} else {
|
} else {
|
||||||
const emailResponse = await sendEmail(
|
const emailResponse = await sendEmail(
|
||||||
appeal.user.email,
|
appeal.user.email,
|
||||||
@ -31,22 +37,13 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await context.env.D1.prepare(
|
await context.env.D1.prepare(
|
||||||
"UPDATE appeals SET approved = 0, open = 0 WHERE id = ?;",
|
"UPDATE appeals SET approved = 0, user = json_remove(user, '$.email') WHERE id = ?;",
|
||||||
)
|
)
|
||||||
.bind(context.params.id)
|
.bind(context.params.id)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
const { current_user: currentUser } = context.data;
|
const { current_user: currentUser } = context.data;
|
||||||
|
|
||||||
delete appeal.user.email;
|
|
||||||
delete appeal.fcm_token;
|
|
||||||
|
|
||||||
appeal.open = false;
|
|
||||||
|
|
||||||
await context.env.DATA.put(`appeal_${appeal.id}`, JSON.stringify(appeal), {
|
|
||||||
expirationTtl: 94608000,
|
|
||||||
});
|
|
||||||
|
|
||||||
await fetch(context.env.APPEALS_WEBHOOK, {
|
await fetch(context.env.APPEALS_WEBHOOK, {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
embeds: [
|
embeds: [
|
||||||
|
@ -41,18 +41,17 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
|
|
||||||
if (!currentUser.email) return jsonError("No email for this session", 403);
|
if (!currentUser.email) return jsonError("No email for this session", 403);
|
||||||
|
|
||||||
const existingAppeals = await context.env.DATA.list({
|
|
||||||
prefix: `appeal_${currentUser.id}`,
|
|
||||||
});
|
|
||||||
const existingBlockedAppeal = await context.env.DATA.get(
|
const existingBlockedAppeal = await context.env.DATA.get(
|
||||||
`blockedappeal_${currentUser.id}`,
|
`blockedappeal_${currentUser.id}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
existingBlockedAppeal ||
|
existingBlockedAppeal ||
|
||||||
existingAppeals.keys.find(
|
(await context.env.D1.prepare(
|
||||||
(appeal) => (appeal.metadata as { [k: string]: any })?.open,
|
"SELECT approved FROM appeals WHERE approved IS NULL AND json_extract(user, '$.id') = ?;",
|
||||||
)
|
)
|
||||||
|
.bind(currentUser.id)
|
||||||
|
.first())
|
||||||
)
|
)
|
||||||
return jsonError("Appeal already submitted", 403);
|
return jsonError("Appeal already submitted", 403);
|
||||||
|
|
||||||
@ -74,33 +73,31 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
.randomUUID()
|
.randomUUID()
|
||||||
.replaceAll("-", "")}`;
|
.replaceAll("-", "")}`;
|
||||||
|
|
||||||
await context.env.DATA.put(
|
await context.env.D1.prepare(
|
||||||
`appeal_${appealId}`,
|
"INSERT INTO appeals (ban_reason, created_at, id, learned, reason_for_unban, user) VALUES (?, ?, ?, ?, ?, ?);",
|
||||||
JSON.stringify({
|
)
|
||||||
ban_reason: whyBanned,
|
.bind(
|
||||||
created_at: Date.now(),
|
whyBanned,
|
||||||
fcm_token: typeof senderTokenId === "string" ? senderTokenId : undefined,
|
Date.now(),
|
||||||
|
appealId,
|
||||||
learned,
|
learned,
|
||||||
id: appealId,
|
whyUnban,
|
||||||
open: true,
|
JSON.stringify({
|
||||||
reason_for_unban: whyUnban,
|
|
||||||
user: {
|
|
||||||
email: currentUser.email,
|
email: currentUser.email,
|
||||||
id: currentUser.id,
|
id: currentUser.id,
|
||||||
username: currentUser.username,
|
username: currentUser.username,
|
||||||
},
|
}),
|
||||||
}),
|
)
|
||||||
{
|
|
||||||
expirationTtl: 94608000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await context.env.D1.prepare(
|
|
||||||
"INSERT INTO appeals (created_at, id, open, user) VALUES (?, ?, ?, ?)",
|
|
||||||
)
|
|
||||||
.bind(Date.now(), appealId, 1, currentUser.id)
|
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
if (typeof senderTokenId === "string") {
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"INSERT INTO push_notifications (created_at, event_id, event_type, token) VALUES (?, ?, 'appeal', ?)",
|
||||||
|
)
|
||||||
|
.bind(Date.now(), appealId, senderTokenId)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
await fetch(context.env.APPEALS_WEBHOOK, {
|
await fetch(context.env.APPEALS_WEBHOOK, {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
embeds: [
|
embeds: [
|
||||||
|
@ -7,13 +7,14 @@ 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 = await context.env.DATA.get(
|
const appeal: Record<string, any> | null = await context.env.D1.prepare(
|
||||||
`gameappeal_${context.params.id as string}`,
|
"SELECT * FROM game_appeals WHERE id = ?;",
|
||||||
);
|
)
|
||||||
|
.bind(context.params.id)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!appeal) return jsonError("Appeal not found", 400);
|
if (!appeal) return jsonError("Appeal not found", 400);
|
||||||
|
|
||||||
const data = JSON.parse(appeal);
|
|
||||||
const banList = (await getBanList(context)) as {
|
const banList = (await getBanList(context)) as {
|
||||||
[k: string]: { BanType: number; Unbanned?: boolean; UnbanReduct?: number };
|
[k: string]: { BanType: number; Unbanned?: boolean; UnbanReduct?: number };
|
||||||
};
|
};
|
||||||
@ -23,12 +24,12 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
.bind(context.params.id)
|
.bind(context.params.id)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
if (!banList[data.roblox_id])
|
if (!banList[appeal.roblox_id])
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
});
|
||||||
|
|
||||||
banList[data.roblox_id] = {
|
banList[appeal.roblox_id] = {
|
||||||
BanType: 0,
|
BanType: 0,
|
||||||
Unbanned: true,
|
Unbanned: true,
|
||||||
UnbanReduct: statsReduction,
|
UnbanReduct: statsReduction,
|
||||||
@ -43,7 +44,7 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
Date.now(),
|
Date.now(),
|
||||||
context.data.current_user.id,
|
context.data.current_user.id,
|
||||||
crypto.randomUUID(),
|
crypto.randomUUID(),
|
||||||
data.roblox_id,
|
appeal.roblox_id,
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
@ -3,18 +3,20 @@ 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.env.DATA.get(`gameappeal_${appealId}`);
|
const appeal = await context.env.D1.prepare(
|
||||||
|
"SELECT * FROM game_appeals WHERE id = ?;",
|
||||||
|
)
|
||||||
|
.bind(appealId)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!appeal) return jsonError("Appeal not found", 404);
|
if (!appeal) return jsonError("Appeal not found", 404);
|
||||||
|
|
||||||
const appealData = JSON.parse(appeal);
|
|
||||||
|
|
||||||
await context.env.DATA.delete(`gameappeal_${appealId}`);
|
await context.env.DATA.delete(`gameappeal_${appealId}`);
|
||||||
await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;")
|
await context.env.D1.prepare("DELETE FROM game_appeals WHERE id = ?;")
|
||||||
.bind(appealId)
|
.bind(appealId)
|
||||||
.run();
|
.run();
|
||||||
await context.env.DATA.put(
|
await context.env.DATA.put(
|
||||||
`gameappealblock_${appealData.roblox_id}`,
|
`gameappealblock_${appeal.roblox_id}`,
|
||||||
`${Date.now() + 2592000000}`,
|
`${Date.now() + 2592000000}`,
|
||||||
{ expirationTtl: 2592000 },
|
{ expirationTtl: 2592000 },
|
||||||
);
|
);
|
||||||
|
@ -5,9 +5,7 @@ export default async function (
|
|||||||
user: number,
|
user: number,
|
||||||
): Promise<{ can_appeal?: boolean; error?: string; reason?: string }> {
|
): Promise<{ can_appeal?: boolean; error?: string; reason?: string }> {
|
||||||
if (
|
if (
|
||||||
await context.env.D1.prepare(
|
await context.env.D1.prepare("SELECT * FROM game_appeals WHERE user = ?;")
|
||||||
"SELECT * FROM game_appeals WHERE open = 1 AND user = ?;",
|
|
||||||
)
|
|
||||||
.bind(user)
|
.bind(user)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
@ -43,25 +43,14 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
if (!precheckData.can_appeal)
|
if (!precheckData.can_appeal)
|
||||||
return jsonError(precheckData.reason as string, 400);
|
return jsonError(precheckData.reason as string, 400);
|
||||||
|
|
||||||
const appealId = `${id}${context.request.headers
|
const appealId = `${id}${
|
||||||
.get("cf-ray")
|
context.request.headers.get("cf-ray")?.split("-")[0]
|
||||||
?.split("-")[0]}${Date.now()}`;
|
}${Date.now()}`;
|
||||||
|
|
||||||
await context.env.DATA.put(
|
|
||||||
`gameappeal_${appealId}`,
|
|
||||||
JSON.stringify({
|
|
||||||
id: appealId,
|
|
||||||
reasonForUnban,
|
|
||||||
roblox_id: id,
|
|
||||||
roblox_username: username,
|
|
||||||
whatHappened,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await context.env.D1.prepare(
|
await context.env.D1.prepare(
|
||||||
"INSERT INTO game_appeals (created_at, id, open, user) VALUES (?, ?, ?, ?);",
|
"INSERT INTO game_appeals (created_at, id, reason_for_unban, roblox_id, roblox_username) VALUES (?, ?, ?, ?, ?);",
|
||||||
)
|
)
|
||||||
.bind(Date.now(), appealId, 1, id)
|
.bind(Date.now(), appealId, reasonForUnban, id, username)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
await fetch(context.env.REPORTS_WEBHOOK, {
|
await fetch(context.env.REPORTS_WEBHOOK, {
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { jsonError } from "../../common.js";
|
import { jsonError } from "../../common.js";
|
||||||
import sendEmail from "../../email.js";
|
import sendEmail from "../../email.js";
|
||||||
import { sendPushNotification } from "../../gcloud.js";
|
import { sendPushNotification } from "../../gcloud.js";
|
||||||
import validateInactivityNotice from "./validate.js";
|
|
||||||
|
|
||||||
export async function onRequestDelete(context: RequestContext) {
|
export async function onRequestDelete(context: RequestContext) {
|
||||||
const kvResult = await context.env.DATA.get(
|
const result = await context.env.D1.prepare(
|
||||||
`inactivity_${context.params.id}`,
|
"SELECT json_extract(user, '*.id') AS uid FROM inactivity_notices WHERE id = ?;",
|
||||||
);
|
)
|
||||||
|
.bind(context.params.id)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!kvResult) return jsonError("No inactivity notice with that ID", 404);
|
if (!result) return jsonError("No inactivity notice with that ID", 404);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
JSON.parse(kvResult).user.id !== context.data.current_user.id &&
|
result.uid !== context.data.current_user.id &&
|
||||||
!(context.data.current_user.permissions & (1 << 0))
|
!(context.data.current_user.permissions & (1 << 0))
|
||||||
)
|
)
|
||||||
return jsonError(
|
return jsonError(
|
||||||
@ -19,7 +20,6 @@ export async function onRequestDelete(context: RequestContext) {
|
|||||||
403,
|
403,
|
||||||
);
|
);
|
||||||
|
|
||||||
await context.env.DATA.delete(`inactivity_${context.params.id}`);
|
|
||||||
await context.env.D1.prepare("DELETE FROM inactivity_notices WHERE id = ?;")
|
await context.env.D1.prepare("DELETE FROM inactivity_notices WHERE id = ?;")
|
||||||
.bind(context.params.id)
|
.bind(context.params.id)
|
||||||
.run();
|
.run();
|
||||||
@ -50,40 +50,61 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
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: { [k: string]: any } | null =
|
||||||
await context.env.DATA.get(`inactivity_${context.params.id as string}`, {
|
await context.env.D1.prepare(
|
||||||
type: "json",
|
"SELECT decisions, departments, user FROM inactivity_notices WHERE id = ?;",
|
||||||
});
|
)
|
||||||
|
.bind(context.params.id)
|
||||||
|
.first();
|
||||||
|
|
||||||
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 } = {};
|
const decisions: { [dept: string]: boolean } = JSON.parse(
|
||||||
|
requestedNotice.decisions,
|
||||||
|
);
|
||||||
|
|
||||||
for (const department of userAdminDepartments)
|
for (const department of userAdminDepartments)
|
||||||
decisions[department] = accepted;
|
decisions[department] = accepted;
|
||||||
|
|
||||||
requestedNotice.decisions = decisions;
|
const applicableDepartments = JSON.parse(requestedNotice.departments).length;
|
||||||
|
|
||||||
if (Object.values(decisions).length === requestedNotice.departments.length) {
|
await context.env.D1.prepare(
|
||||||
requestedNotice.open = false;
|
"UPDATE inactivity_notices SET decisions = ?, user = json_remove(user, '*.email') WHERE id = ?;",
|
||||||
|
)
|
||||||
|
.bind(JSON.stringify(decisions), context.params.id)
|
||||||
|
.run();
|
||||||
|
|
||||||
const approved = !Object.values(decisions).filter((d) => !d).length;
|
if (Object.values(decisions).length === applicableDepartments) {
|
||||||
|
const approved =
|
||||||
await context.env.D1.prepare(
|
Object.values(decisions).filter((d) => d).length ===
|
||||||
"UPDATE inactivity_notices SET approved = ?, open = 0 WHERE id = ?;",
|
applicableDepartments;
|
||||||
|
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(Number(approved), context.params.id)
|
.bind(context.params.id)
|
||||||
.run();
|
.first();
|
||||||
|
|
||||||
|
if (fcmTokenResult) {
|
||||||
|
let status = "Approved";
|
||||||
|
|
||||||
|
if (denied) status = "Denied";
|
||||||
|
else if (!approved) status = "Partially Approved";
|
||||||
|
|
||||||
if (requestedNotice.fcm_token) {
|
|
||||||
await sendPushNotification(
|
await sendPushNotification(
|
||||||
context.env,
|
context.env,
|
||||||
`Inactivity Request ${approved ? "Approved" : "Denied"}`,
|
`Inactivity Request ${status}`,
|
||||||
accepted
|
accepted
|
||||||
? "Your inactivity request was approved."
|
? "Your inactivity request was approved."
|
||||||
: "Your inactivity request was denied, please reach out to management if you require more details.",
|
: `Your inactivity request was ${denied ? "denied" : "partially approved"}, please reach out to management if you require more details.`,
|
||||||
requestedNotice.fcm_token,
|
fcmTokenResult.token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'inactivity';",
|
||||||
|
).bind(context.params.id);
|
||||||
} else {
|
} else {
|
||||||
await sendEmail(
|
await sendEmail(
|
||||||
requestedNotice.user.email,
|
requestedNotice.user.email,
|
||||||
@ -93,96 +114,8 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
{ username: requestedNotice.user.username },
|
{ username: requestedNotice.user.username },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete requestedNotice.fcm_token;
|
|
||||||
delete requestedNotice.user.email;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.env.DATA.put(
|
|
||||||
`inactivity_${context.params.id as string}`,
|
|
||||||
JSON.stringify(requestedNotice),
|
|
||||||
{ expirationTtl: 63072000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function onRequestPut(context: RequestContext) {
|
|
||||||
const kvResult: InactivityNoticeProps | null = await context.env.DATA.get(
|
|
||||||
`inactivity_${context.params.id}`,
|
|
||||||
{ type: "json" },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!kvResult) return jsonError("No inactivity notice with that ID", 404);
|
|
||||||
|
|
||||||
if (kvResult.user.id !== context.data.current_user.id)
|
|
||||||
return jsonError(
|
|
||||||
"You do not have permission to modify this inactivity notice",
|
|
||||||
403,
|
|
||||||
);
|
|
||||||
|
|
||||||
const d1entry = await context.env.D1.prepare(
|
|
||||||
"SELECT open FROM inactivity_notices WHERE id = ?;",
|
|
||||||
)
|
|
||||||
.bind(context.params.id)
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!Boolean(d1entry?.open))
|
|
||||||
return jsonError("Cannot modify a closed inactivity notice", 403);
|
|
||||||
|
|
||||||
const { departments, end, reason, start } = context.data.body;
|
|
||||||
|
|
||||||
const validationFailureResponse = validateInactivityNotice(
|
|
||||||
departments,
|
|
||||||
end,
|
|
||||||
reason,
|
|
||||||
start,
|
|
||||||
context.data.departments,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validationFailureResponse) return validationFailureResponse;
|
|
||||||
|
|
||||||
kvResult.departments = departments;
|
|
||||||
kvResult.end = end;
|
|
||||||
kvResult.reason = reason;
|
|
||||||
kvResult.start = start;
|
|
||||||
|
|
||||||
await context.env.DATA.put(
|
|
||||||
`inactivity_${context.params.id}`,
|
|
||||||
JSON.stringify(kvResult),
|
|
||||||
{
|
|
||||||
expirationTtl: 63072000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const departmentWebhooks = [];
|
|
||||||
for (const department of departments)
|
|
||||||
departmentWebhooks.push(context.env[`${department}_INACTIVITY_WEBHOOK`]);
|
|
||||||
|
|
||||||
const webhookPromises = [];
|
|
||||||
for (const webhook of departmentWebhooks)
|
|
||||||
webhookPromises.push(
|
|
||||||
fetch(webhook, {
|
|
||||||
body: JSON.stringify({
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
title: "Inactivity Notice Modified",
|
|
||||||
color: 37562560,
|
|
||||||
description: `View the updated notice at https://carcrushers.cc/mod-queue?id=${context.params.id}&type=inactivity`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/json",
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.allSettled(webhookPromises);
|
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
});
|
||||||
|
@ -20,34 +20,33 @@ 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.DATA.put(
|
await context.env.D1.prepare(
|
||||||
`inactivity_${inactivityId}`,
|
"INSERT INTO inactivity_notices (created_at, departments, end, hiatus, id, reason, start, user) VALUES (?, ?, ?, ?, ?, ?, ?, ?);",
|
||||||
JSON.stringify({
|
)
|
||||||
created_at: Date.now(),
|
.bind(
|
||||||
departments,
|
Date.now(),
|
||||||
|
JSON.stringify(departments),
|
||||||
end,
|
end,
|
||||||
fcm_token: typeof senderTokenId === "string" ? senderTokenId : undefined,
|
Number(hiatus),
|
||||||
hiatus,
|
inactivityId,
|
||||||
open: true,
|
|
||||||
reason,
|
reason,
|
||||||
start,
|
start,
|
||||||
user: {
|
JSON.stringify({
|
||||||
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,
|
||||||
},
|
}),
|
||||||
}),
|
)
|
||||||
{
|
|
||||||
expirationTtl: 63072000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await context.env.D1.prepare(
|
|
||||||
"INSERT INTO inactivity_notices (created_at, id, user) VALUES (?, ?, ?);",
|
|
||||||
)
|
|
||||||
.bind(Date.now(), inactivityId, context.data.current_user.id)
|
|
||||||
.run();
|
.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();
|
||||||
|
}
|
||||||
|
|
||||||
const departmentsToNotify = [];
|
const departmentsToNotify = [];
|
||||||
const departmentRoles = [];
|
const departmentRoles = [];
|
||||||
const { env } = context;
|
const { env } = context;
|
||||||
|
@ -9,5 +9,13 @@ export async function onRequestGet(context: RequestContext) {
|
|||||||
|
|
||||||
if (!success) return jsonError("Unable to retrieve appeals", 500);
|
if (!success) return jsonError("Unable to retrieve appeals", 500);
|
||||||
|
|
||||||
return jsonResponse(JSON.stringify(results));
|
return jsonResponse(
|
||||||
|
JSON.stringify(
|
||||||
|
results.map((result) => {
|
||||||
|
result.user = JSON.parse(result.user as string);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,27 @@ export async function onRequestGet(context: RequestContext) {
|
|||||||
if (!["appeal", "inactivity", "report"].includes(type as string))
|
if (!["appeal", "inactivity", "report"].includes(type as string))
|
||||||
return jsonError("Invalid type", 400);
|
return jsonError("Invalid type", 400);
|
||||||
|
|
||||||
const data = (await context.env.DATA.get(`${type}_${id}`, {
|
const tables: { [k: string]: string } = {
|
||||||
type: "json",
|
appeal: "appeals",
|
||||||
})) as {
|
inactivity: "inactivity_notices",
|
||||||
created_at: number;
|
report: "reports",
|
||||||
id: string;
|
};
|
||||||
open: boolean;
|
|
||||||
user?: { id: string; username: string };
|
|
||||||
} & { [k: string]: any };
|
|
||||||
|
|
||||||
if (data?.user?.id !== context.data.current_user.id)
|
const data: Record<string, any> | null = await context.env.D1.prepare(
|
||||||
|
`SELECT *
|
||||||
|
FROM ${tables[type as string]}
|
||||||
|
WHERE id = ?;`,
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (data?.user) data.user = JSON.parse(data.user);
|
||||||
|
|
||||||
|
if (!data || data.user?.id !== context.data.current_user.id)
|
||||||
return jsonError("Item does not exist", 404);
|
return jsonError("Item does not exist", 404);
|
||||||
|
|
||||||
if (type === "report") {
|
if (type === "report") {
|
||||||
|
data.attachments = JSON.parse(data.attachments);
|
||||||
const { AwsClient } = await import("aws4fetch");
|
const { AwsClient } = await import("aws4fetch");
|
||||||
const aws = new AwsClient({
|
const aws = new AwsClient({
|
||||||
accessKeyId: context.env.R2_ACCESS_KEY,
|
accessKeyId: context.env.R2_ACCESS_KEY,
|
||||||
|
@ -8,42 +8,12 @@ export async function onRequestGet(context: RequestContext) {
|
|||||||
results: { id: string }[];
|
results: { id: string }[];
|
||||||
success: boolean;
|
success: boolean;
|
||||||
} = await context.env.D1.prepare(
|
} = await context.env.D1.prepare(
|
||||||
"SELECT id FROM reports WHERE user = ? ORDER BY created_at LIMIT 50;",
|
"SELECT created_at, id, open, target_usernames FROM reports WHERE json_extract(user, '$.id') = ? ORDER BY created_at LIMIT 50;",
|
||||||
)
|
)
|
||||||
.bind(context.data.current_user.id)
|
.bind(context.data.current_user.id)
|
||||||
.all();
|
.all();
|
||||||
|
|
||||||
if (!success) return jsonError("Failed to retrieve reports", 500);
|
if (!success) return jsonError("Failed to retrieve reports", 500);
|
||||||
|
|
||||||
const ids: string[] = [];
|
return jsonResponse(JSON.stringify(results));
|
||||||
results.map((v) => ids.push(v.id));
|
|
||||||
|
|
||||||
const kvDataPromises = [];
|
|
||||||
|
|
||||||
for (const id of ids)
|
|
||||||
kvDataPromises.push(context.env.DATA.get(`report_${id}`, { type: "json" }));
|
|
||||||
|
|
||||||
let settledKvPromises;
|
|
||||||
|
|
||||||
try {
|
|
||||||
settledKvPromises = (await Promise.all(
|
|
||||||
kvDataPromises,
|
|
||||||
)) as ReportCardProps[];
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
return jsonError("Failed to resolve reports", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonResponse(
|
|
||||||
JSON.stringify(
|
|
||||||
settledKvPromises.map((r) => {
|
|
||||||
return {
|
|
||||||
created_at: r.created_at,
|
|
||||||
id: r.id,
|
|
||||||
open: r.open,
|
|
||||||
target_usernames: r.target_usernames,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
import { jsonError, jsonResponse } from "../../../common.js";
|
import { jsonError, jsonResponse } from "../../../common.js";
|
||||||
|
|
||||||
export async function onRequestGet(context: RequestContext) {
|
export async function onRequestGet(context: RequestContext) {
|
||||||
const types: { [k: string]: { permissions: number[]; prefix: string } } = {
|
const types: {
|
||||||
|
[k: string]: { permissions: number[]; table: string };
|
||||||
|
} = {
|
||||||
appeal: {
|
appeal: {
|
||||||
permissions: [1 << 0, 1 << 1],
|
permissions: [1 << 0, 1 << 1],
|
||||||
prefix: `appeal_`,
|
table: "appeals",
|
||||||
},
|
},
|
||||||
gma: {
|
gma: {
|
||||||
permissions: [1 << 5],
|
permissions: [1 << 5],
|
||||||
prefix: "gameappeal_",
|
table: "game_appeals",
|
||||||
},
|
},
|
||||||
inactivity: {
|
inactivity: {
|
||||||
permissions: [1 << 0, 1 << 4, 1 << 6, 1 << 7, 1 << 11],
|
permissions: [1 << 0, 1 << 4, 1 << 6, 1 << 7, 1 << 11],
|
||||||
prefix: "inactivity_",
|
table: "inactivity_notices",
|
||||||
},
|
},
|
||||||
report: {
|
report: {
|
||||||
permissions: [1 << 5],
|
permissions: [1 << 5],
|
||||||
prefix: "report_",
|
table: "reports",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,16 +32,15 @@ export async function onRequestGet(context: RequestContext) {
|
|||||||
)
|
)
|
||||||
return jsonError("You cannot use this filter", 403);
|
return jsonError("You cannot use this filter", 403);
|
||||||
|
|
||||||
let item: {
|
const item: Record<string, any> | null = await context.env.D1.prepare(
|
||||||
[k: string]: any;
|
`SELECT *
|
||||||
} | null = await context.env.DATA.get(`${types[type].prefix}${itemId}`, {
|
FROM ${types[type].table}
|
||||||
type: "json",
|
WHERE id = ?;`,
|
||||||
});
|
)
|
||||||
|
.bind(itemId)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!item)
|
if (!item) return jsonError("Item not found", 404);
|
||||||
item = await context.env.DATA.get(`closed${types[type].prefix}${itemId}`, {
|
|
||||||
type: "json",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
type === "report" &&
|
type === "report" &&
|
||||||
@ -47,7 +48,11 @@ export async function onRequestGet(context: RequestContext) {
|
|||||||
)
|
)
|
||||||
return jsonError("Report is processing", 409);
|
return jsonError("Report is processing", 409);
|
||||||
|
|
||||||
if (item) delete item.user?.email;
|
if (item.user) {
|
||||||
|
item.user = JSON.parse(item.user);
|
||||||
|
|
||||||
|
delete item.user.email;
|
||||||
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
? jsonResponse(JSON.stringify(item))
|
? jsonResponse(JSON.stringify(item))
|
||||||
|
92
functions/api/mod-queue/[type]/list.ts
Normal file
92
functions/api/mod-queue/[type]/list.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { jsonError, jsonResponse } from "../../../common.js";
|
||||||
|
|
||||||
|
export async function onRequestGet(context: RequestContext): Promise<any> {
|
||||||
|
const type = context.params.type as string;
|
||||||
|
const { searchParams } = new URL(context.request.url);
|
||||||
|
const before = parseInt(searchParams.get("before") || `${Date.now()}`);
|
||||||
|
const showClosed = searchParams.get("showClosed") === "true";
|
||||||
|
const tables: { [k: string]: string } = {
|
||||||
|
appeal: "appeals",
|
||||||
|
gma: "game_appeals",
|
||||||
|
inactivity: "inactivity_notices",
|
||||||
|
report: "reports",
|
||||||
|
};
|
||||||
|
const permissions: { [k: string]: number[] } = {
|
||||||
|
appeal: [1 << 0, 1 << 1],
|
||||||
|
gma: [1 << 5],
|
||||||
|
inactivity: [1 << 4, 1 << 6, 1 << 7, 1 << 11, 1 << 12],
|
||||||
|
report: [1 << 5],
|
||||||
|
};
|
||||||
|
const { current_user: currentUser } = context.data;
|
||||||
|
|
||||||
|
if (!tables[type]) return jsonError("Invalid filter type", 400);
|
||||||
|
|
||||||
|
if (!permissions[type].find((p) => currentUser.permissions & p))
|
||||||
|
return jsonError("You cannot use this filter", 403);
|
||||||
|
|
||||||
|
if (isNaN(before)) return jsonError("Invalid `before` parameter", 400);
|
||||||
|
|
||||||
|
let rows: D1Result<Record<string, any>>;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "appeal":
|
||||||
|
rows = await context.env.D1.prepare(
|
||||||
|
`SELECT *
|
||||||
|
FROM appeals
|
||||||
|
WHERE created_at < ?
|
||||||
|
AND open ${showClosed ? "IS NOT" : "IS"} NULL
|
||||||
|
ORDER BY created_at DESC LIMIT 25;`,
|
||||||
|
)
|
||||||
|
.bind(before, !Number(showClosed))
|
||||||
|
.all();
|
||||||
|
rows.results.forEach((r) => {
|
||||||
|
r.user = JSON.parse(r.user);
|
||||||
|
delete r.user.email;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "gma":
|
||||||
|
rows = await context.env.D1.prepare(
|
||||||
|
"SELECT * FROM game_appeals WHERE created_at < ? ORDER BY created_at DESC LIMIT 25;",
|
||||||
|
)
|
||||||
|
.bind(before)
|
||||||
|
.all();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "inactivity":
|
||||||
|
rows = await context.env.D1.prepare(
|
||||||
|
`SELECT *,
|
||||||
|
(SELECT COUNT(*) FROM json_each(decisions)) as decision_count
|
||||||
|
FROM inactivity_notices
|
||||||
|
WHERE created_at < ?
|
||||||
|
AND decision_count ${showClosed ? "=" : "!="} json_array_length(departments)`,
|
||||||
|
)
|
||||||
|
.bind(before)
|
||||||
|
.all();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "report":
|
||||||
|
rows = await context.env.D1.prepare(
|
||||||
|
"SELECT * FROM reports WHERE created_at < ? AND open = ? ORDER BY created_at DESC LIMIT 25;",
|
||||||
|
)
|
||||||
|
.bind(before, !Number(showClosed))
|
||||||
|
.all();
|
||||||
|
|
||||||
|
rows.results.forEach((r) => {
|
||||||
|
r.attachments = JSON.parse(r.attachments);
|
||||||
|
r.target_ids = JSON.parse(r.target_ids);
|
||||||
|
r.target_usernames = JSON.parse(r.target_usernames);
|
||||||
|
|
||||||
|
if (!r.user) return;
|
||||||
|
|
||||||
|
r.user = JSON.parse(r.user);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return jsonError("Unknown filter error", 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse(JSON.stringify(rows.results));
|
||||||
|
}
|
@ -1,90 +0,0 @@
|
|||||||
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()}`);
|
|
||||||
const entryType = searchParams.get("type");
|
|
||||||
const showClosed = searchParams.get("showClosed") === "true";
|
|
||||||
const tables: { [k: string]: string } = {
|
|
||||||
appeal: "appeals",
|
|
||||||
gma: "game_appeals",
|
|
||||||
inactivity: "inactivity_notices",
|
|
||||||
report: "reports",
|
|
||||||
};
|
|
||||||
const types: { [k: string]: string } = {
|
|
||||||
appeal: "appeal",
|
|
||||||
gma: "gameappeal",
|
|
||||||
inactivity: "inactivity",
|
|
||||||
report: "report",
|
|
||||||
};
|
|
||||||
const permissions: { [k: string]: number[] } = {
|
|
||||||
appeal: [1 << 0, 1 << 1],
|
|
||||||
gma: [1 << 5],
|
|
||||||
inactivity: [1 << 4, 1 << 6, 1 << 7, 1 << 11, 1 << 12],
|
|
||||||
report: [1 << 5],
|
|
||||||
};
|
|
||||||
const { current_user: currentUser } = context.data;
|
|
||||||
|
|
||||||
if (!entryType || !types[entryType])
|
|
||||||
return jsonError("Invalid filter type", 400);
|
|
||||||
|
|
||||||
if (!permissions[entryType].find((p) => currentUser.permissions & p))
|
|
||||||
return jsonError("You cannot use this filter", 403);
|
|
||||||
|
|
||||||
if (isNaN(before)) return jsonError("Invalid `before` parameter", 400);
|
|
||||||
|
|
||||||
const prefix = types[entryType];
|
|
||||||
const table = tables[entryType];
|
|
||||||
const items = [];
|
|
||||||
const { results }: { results?: { created_at: number; id: string }[] } =
|
|
||||||
/*
|
|
||||||
This is normally VERY BAD and can lead to injection attacks
|
|
||||||
However, there is no other way to do this, as using bindings for table names is unsupported apparently
|
|
||||||
To avoid any potential injection attacks we enforce a list of specific values and permissions for table names
|
|
||||||
*/
|
|
||||||
await context.env.D1.prepare(
|
|
||||||
`SELECT id
|
|
||||||
FROM ${table}
|
|
||||||
WHERE created_at < ? AND open = ?
|
|
||||||
ORDER BY created_at DESC LIMIT 25;`,
|
|
||||||
)
|
|
||||||
.bind(before, Number(!showClosed))
|
|
||||||
.all();
|
|
||||||
|
|
||||||
if (results)
|
|
||||||
for (const { id } of results) {
|
|
||||||
const item: { [k: string]: any } | null = await context.env.DATA.get(
|
|
||||||
`${prefix}_${id}`,
|
|
||||||
{
|
|
||||||
type: "json",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
delete item.user?.email;
|
|
||||||
|
|
||||||
if (entryType === "inactivity") {
|
|
||||||
// Only include inactivity notices that a user can actually act on
|
|
||||||
const departments = {
|
|
||||||
DM: [1 << 11],
|
|
||||||
ET: [1 << 4, 1 << 12],
|
|
||||||
FM: [1 << 7],
|
|
||||||
WM: [1 << 6],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
!Object.entries(departments).find(
|
|
||||||
(dept) =>
|
|
||||||
item.departments.includes(dept[0]) &&
|
|
||||||
dept[1].find((p) => currentUser.permissions & p),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push({ ...item, id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonResponse(JSON.stringify(items.filter((v) => v !== null)));
|
|
||||||
}
|
|
@ -5,15 +5,18 @@ import { sendPushNotification } from "../../../gcloud.js";
|
|||||||
|
|
||||||
export async function onRequestPost(context: RequestContext) {
|
export async function onRequestPost(context: RequestContext) {
|
||||||
const reportId = context.params.id as string;
|
const reportId = context.params.id as string;
|
||||||
const reportData: (ReportCardProps & { fcm_token?: string }) | null =
|
const report: {
|
||||||
await context.env.DATA.get(`report_${reportId}`, { type: "json" });
|
[k: string]: any;
|
||||||
|
} | null = await context.env.D1.prepare("SELECT * FROM reports WHERE id = ?")
|
||||||
|
.bind(reportId)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!reportData) return jsonError("Report does not exist", 404);
|
if (!report) return jsonError("Report does not exist", 404);
|
||||||
|
|
||||||
const actionMap = context.data.body;
|
const actionMap = context.data.body;
|
||||||
const newActions: { [k: string]: { BanType: number } } = {};
|
const newActions: { [k: string]: { BanType: number } } = {};
|
||||||
const logMap: { [k: string]: number } = {};
|
const logMap: { [k: string]: number } = {};
|
||||||
const { user } = reportData as ReportCardProps & { user?: { email: string } };
|
const user = JSON.parse(report.user);
|
||||||
|
|
||||||
for (const [user, action] of Object.entries(actionMap)) {
|
for (const [user, action] of Object.entries(actionMap)) {
|
||||||
if (
|
if (
|
||||||
@ -64,35 +67,44 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
await setBanList(context, Object.assign(banList, newActions));
|
await setBanList(context, Object.assign(banList, newActions));
|
||||||
}
|
}
|
||||||
|
|
||||||
reportData.open = false;
|
const pushNotificationData: Record<string, string> | null =
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"SELECT token FROM push_notifications WHERE event_id = ? AND event_type = 'report';",
|
||||||
|
)
|
||||||
|
.bind(reportId)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (user?.email && !reportData.fcm_token)
|
if (user?.email)
|
||||||
await sendEmail(
|
await sendEmail(
|
||||||
user.email,
|
user?.email,
|
||||||
context.env.MAILGUN_API_KEY,
|
context.env.MAILGUN_API_KEY,
|
||||||
"Report Processed",
|
"Report Processed",
|
||||||
"report_processed",
|
"report_processed",
|
||||||
{
|
{
|
||||||
username: reportData.user?.username as string,
|
username: report.user?.username as string,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
else if (reportData.fcm_token)
|
else if (pushNotificationData)
|
||||||
await sendPushNotification(
|
await sendPushNotification(
|
||||||
context.env,
|
context.env,
|
||||||
"Report Processed",
|
"Report Processed",
|
||||||
`Your report for ${reportData.target_usernames.toString()} has been reviewed.`,
|
`Your report for ${JSON.parse(report.target_usernames).toString()} has been reviewed.`,
|
||||||
reportData.fcm_token,
|
pushNotificationData.token,
|
||||||
);
|
);
|
||||||
|
|
||||||
delete reportData.fcm_token;
|
delete (report.user as { email?: string; id: string; username: string })
|
||||||
delete (reportData.user as { email?: string; id: string; username: string })
|
|
||||||
?.email;
|
?.email;
|
||||||
|
|
||||||
await context.env.DATA.put(`report_${reportId}`, JSON.stringify(reportData));
|
|
||||||
await context.env.D1.prepare("UPDATE reports SET open = 0 WHERE id = ?;")
|
await context.env.D1.prepare("UPDATE reports SET open = 0 WHERE id = ?;")
|
||||||
.bind(reportId)
|
.bind(reportId)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"DELETE FROM push_notifications WHERE event_id = ? AND event_type = 'report';",
|
||||||
|
)
|
||||||
|
.bind(reportId)
|
||||||
|
.run();
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,11 @@ 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.DATA.get(`report_${id}`);
|
const value = await context.env.D1.prepare(
|
||||||
|
"SELECT id FROM reports WHERE id = ?;",
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!value) return jsonError("Report is missing", 500);
|
if (!value) return jsonError("Report is missing", 500);
|
||||||
|
|
||||||
|
@ -15,12 +15,15 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
)
|
)
|
||||||
return jsonError("No processing report with that ID found", 404);
|
return jsonError("No processing report with that ID found", 404);
|
||||||
|
|
||||||
const data: { [k: string]: any } | null = await context.env.DATA.get(
|
const data: Record<string, any> | null = await context.env.D1.prepare(
|
||||||
`report_${id}`,
|
"SELECT attachments FROM reports WHERE id = ?;",
|
||||||
{ type: "json" },
|
)
|
||||||
);
|
.bind(id)
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!data) return jsonError("Report doesn't exist", 404);
|
if (!data) return jsonError("No report with that ID found", 404);
|
||||||
|
|
||||||
|
data.attachments = JSON.parse(data.attachments);
|
||||||
|
|
||||||
const accessToken = await GetAccessToken(context.env);
|
const accessToken = await GetAccessToken(context.env);
|
||||||
const attachmentDeletePromises = [];
|
const attachmentDeletePromises = [];
|
||||||
@ -47,7 +50,6 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await Promise.allSettled(attachmentDeletePromises);
|
await Promise.allSettled(attachmentDeletePromises);
|
||||||
await context.env.DATA.delete(`report_${id}`);
|
|
||||||
await context.env.D1.prepare("DELETE FROM reports WHERE id = ?;")
|
await context.env.D1.prepare("DELETE FROM reports WHERE id = ?;")
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.run();
|
.run();
|
||||||
|
@ -195,33 +195,25 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
attachments.push(new URL(urlResult.value).pathname.replace(/^\/?t?\//, ""));
|
attachments.push(new URL(urlResult.value).pathname.replace(/^\/?t?\//, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.env.DATA.put(
|
await context.env.D1.prepare(
|
||||||
`report_${reportId}`,
|
"INSERT INTO reports (attachments, created_at, id, open, target_ids, target_usernames, user) VALUES (?, ?, ?, 1, ?, ?, ?);",
|
||||||
JSON.stringify({
|
)
|
||||||
attachments,
|
.bind(
|
||||||
created_at: Date.now(),
|
JSON.stringify(attachments),
|
||||||
fcm_token: typeof senderTokenId === "string" ? senderTokenId : undefined,
|
Date.now(),
|
||||||
id: reportId,
|
reportId,
|
||||||
open: true,
|
JSON.stringify(metaIDs),
|
||||||
user: currentUser
|
JSON.stringify(metaNames),
|
||||||
? {
|
currentUser ? JSON.stringify(currentUser) : null,
|
||||||
email: currentUser.email,
|
|
||||||
id: currentUser.id,
|
|
||||||
username: currentUser.username,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
target_ids: metaIDs,
|
|
||||||
target_usernames: metaNames,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await context.env.D1.prepare(
|
|
||||||
"INSERT INTO reports (created_at, id, open, user) VALUES (?, ?, ?, ?);",
|
|
||||||
)
|
)
|
||||||
.bind(Date.now(), reportId, 1, currentUser?.id || null)
|
.run();
|
||||||
|
|
||||||
|
if (typeof senderTokenId === "string")
|
||||||
|
await context.env.D1.prepare(
|
||||||
|
"INSERT INTO push_notifications (created_at, event_id, event_type, token) VALUES (?, ?, ?, ?);",
|
||||||
|
)
|
||||||
|
.bind(Date.now(), reportId, "report", senderTokenId)
|
||||||
.run();
|
.run();
|
||||||
} catch {}
|
|
||||||
|
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
JSON.stringify({ id: reportId, upload_urls: uploadUrls }),
|
JSON.stringify({ id: reportId, upload_urls: uploadUrls }),
|
||||||
|
7
index.d.ts
vendored
7
index.d.ts
vendored
@ -10,6 +10,13 @@ declare global {
|
|||||||
[k: string]: string;
|
[k: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FCMTokenResult = {
|
||||||
|
created_at: number;
|
||||||
|
event_id: string;
|
||||||
|
event_type: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
type RequestContext = EventContext<Env, string, { [k: string]: any }>;
|
type RequestContext = EventContext<Env, string, { [k: string]: any }>;
|
||||||
|
|
||||||
interface AppealCardProps {
|
interface AppealCardProps {
|
||||||
|
1694
package-lock.json
generated
1694
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@ -12,27 +12,27 @@
|
|||||||
"@chakra-ui/react": "^2.8.2",
|
"@chakra-ui/react": "^2.8.2",
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@fontsource/plus-jakarta-sans": "^5.0.19",
|
"@fontsource/plus-jakarta-sans": "^5.0.20",
|
||||||
"@remix-run/cloudflare": "^2.8.1",
|
"@remix-run/cloudflare": "^2.9.2",
|
||||||
"@remix-run/cloudflare-pages": "^2.8.1",
|
"@remix-run/cloudflare-pages": "^2.9.2",
|
||||||
"@remix-run/react": "^2.8.1",
|
"@remix-run/react": "^2.9.2",
|
||||||
"@sentry/react": "^7.110.1",
|
"@sentry/react": "^7.114.0",
|
||||||
"aws4fetch": "^1.0.18",
|
"aws4fetch": "^1.0.18",
|
||||||
"framer-motion": "^11.0.28",
|
"framer-motion": "^11.1.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@remix-run/dev": "^2.8.1",
|
"@remix-run/dev": "^2.9.2",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.11",
|
||||||
"@types/react": "^18.2.78",
|
"@types/react": "^18.3.2",
|
||||||
"@types/react-dom": "^18.2.25",
|
"@types/react-dom": "^18.3.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@cloudflare/workers-types": "^4.20240405.0"
|
"@cloudflare/workers-types": "^4.20240502.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"endOfLine": "auto"
|
"endOfLine": "auto"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user