Compare commits

...

4 Commits

Author SHA1 Message Date
regalijan 2ae11670de Enforce event booking limit server side
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 1m58s
Test, Build, Deploy / Create Sentry Release (push) Successful in 9s
2026-06-02 18:31:28 -04:00
regalijan 3ee4a6cb29 Fix et report formatting 2026-06-02 18:27:18 -04:00
regalijan a76749bfff Prevent more than three events pre-bookings 2026-06-02 18:25:34 -04:00
regalijan cc79d29c8e Fix time nonsense with new et striked 2026-06-02 18:15:05 -04:00
4 changed files with 50 additions and 11 deletions
+27 -3
View File
@@ -1,4 +1,8 @@
import { import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Button, Button,
Container, Container,
Heading, Heading,
@@ -28,11 +32,23 @@ export async function loader({ context }: { context: RequestContext }) {
status: 403, status: 403,
}); });
return null; const now = new Date();
const preBookedEvents = await context.data.prisma.event.count({
where: {
created_by: context.data.current_user.id,
day: {
gte: now.getUTCDate(),
},
month: now.getUTCMonth() + 1,
year: now.getUTCFullYear(),
},
});
return preBookedEvents > 3;
} }
export default function () { export default function () {
useLoaderData<typeof loader>(); const exceededMaxBookings = useLoaderData<typeof loader>();
const toast = useToast(); const toast = useToast();
const currentDate = new Date(); const currentDate = new Date();
@@ -44,7 +60,7 @@ export default function () {
const [eventType, setEventType] = useState(""); const [eventType, setEventType] = useState("");
const [riddleAnswer, setRiddleAnswer] = useState(""); const [riddleAnswer, setRiddleAnswer] = useState("");
const [submitSuccess, setSubmitSuccess] = useState(false); const [submitSuccess, setSubmitSuccess] = useState(false);
const [disableSubmit, setDisableSubmit] = useState(false); const [disableSubmit, setDisableSubmit] = useState(exceededMaxBookings);
useEffect(() => { useEffect(() => {
setDatePickerMin(`${new Date().toISOString().split("T").at(0)}`); setDatePickerMin(`${new Date().toISOString().split("T").at(0)}`);
@@ -112,6 +128,14 @@ export default function () {
/> />
) : ( ) : (
<Container maxW="container.md"> <Container maxW="container.md">
<Alert display={exceededMaxBookings ? undefined : "none"} status="error">
<AlertIcon />
<AlertTitle>Max pre-bookings exceeded!</AlertTitle>
<AlertDescription>
You cannot pre-book more than three events. Please wait until you
complete an event before trying again.
</AlertDescription>
</Alert>
<Heading pb="32px">Book an Event</Heading> <Heading pb="32px">Book an Event</Heading>
<Heading mb="8px" size="md"> <Heading mb="8px" size="md">
Event Type Event Type
+2 -2
View File
@@ -136,11 +136,11 @@ export default function () {
my="16px" my="16px"
onClick={async () => { onClick={async () => {
await navigator.clipboard.writeText( await navigator.clipboard.writeText(
`local Report = { `\`\`\`\nlocal Report = {
${Object.values(data.members) ${Object.values(data.members)
.map((v) => `[${v.roblox_id}] = ${v.points};`) .map((v) => `[${v.roblox_id}] = ${v.points};`)
.join("\n ")} .join("\n ")}
}`, }\n\`\`\``,
); );
alert("Report copied."); alert("Report copied.");
+19 -3
View File
@@ -21,8 +21,24 @@ export async function onRequestPost(context: RequestContext) {
) )
return jsonError("Invalid body", 400); return jsonError("Invalid body", 400);
const { prisma } = context.data;
if ( if (
await context.data.prisma.event.findFirst({ (await prisma.event.count({
where: {
created_by: context.data.current_user.id,
day: {
gte: now.getUTCDate(),
},
month: currentMonth,
year: currentYear,
},
})) > 3
)
return jsonError("Too many events scheduled", 403);
if (
await prisma.event.findFirst({
select: { select: {
id: true, id: true,
}, },
@@ -51,7 +67,7 @@ export async function onRequestPost(context: RequestContext) {
const weekRange = Math.floor(day / 7); const weekRange = Math.floor(day / 7);
const existingEventInRange = await context.data.prisma.event.findFirst({ const existingEventInRange = await prisma.event.findFirst({
select: { select: {
id: true, id: true,
}, },
@@ -73,7 +89,7 @@ export async function onRequestPost(context: RequestContext) {
const id = `${now.getTime()}${crypto.randomUUID().replaceAll("-", "")}`; const id = `${now.getTime()}${crypto.randomUUID().replaceAll("-", "")}`;
await context.data.prisma.event.create({ await prisma.event.create({
data: { data: {
answer: context.data.body.answer || null, answer: context.data.body.answer || null,
approved: type === "gamenight", approved: type === "gamenight",
+2 -3
View File
@@ -19,14 +19,13 @@ export async function onRequestPost(context: RequestContext) {
) )
return jsonError("Invalid user id", 400); return jsonError("Invalid user id", 400);
const now = Date.now();
const id = crypto.randomUUID().replaceAll("-", ""); const id = crypto.randomUUID().replaceAll("-", "");
const actingUser = context.data.current_user.id; const actingUser = context.data.current_user.id;
await D1.batch([ await D1.batch([
D1.prepare( D1.prepare(
"INSERT INTO et_strikes (created_at, created_by, id, reason, user) VALUES (?, ?, ?, ?, ?);", "INSERT INTO et_strikes (created_by, id, reason, user) VALUES (?, ?, ?, ?);",
).bind(now, actingUser, id, reason, user), ).bind(actingUser, id, reason, user),
D1.prepare( D1.prepare(
"UPDATE et_members SET points = points - 100 WHERE id = ?;", "UPDATE et_members SET points = points - 100 WHERE id = ?;",
).bind(user), ).bind(user),