Compare commits

...

7 Commits

Author SHA1 Message Date
26bef47403 Disable text masking and media blocking
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 1m2s
Test, Build, Deploy / Create Sentry Release (push) Successful in 6s
2026-03-15 03:14:19 -04:00
47df3dc55f Combine queries into batches
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 45s
Test, Build, Deploy / Create Sentry Release (push) Successful in 6s
2026-03-14 05:18:50 -04:00
994a7a7a58 Include both ray and region in report id
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 47s
Test, Build, Deploy / Create Sentry Release (push) Successful in 5s
2026-03-14 02:25:53 -04:00
2ca8cc163d Add trailing slash that is apparently needed
Some checks failed
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 54s
Test, Build, Deploy / Create Sentry Release (push) Failing after 25s
2026-03-14 02:21:00 -04:00
8e34e2ce24 Send back response from sentry through tunnel
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 54s
Test, Build, Deploy / Create Sentry Release (push) Successful in 7s
2026-03-14 01:47:42 -04:00
7352d0bb43 Automatically update commit_sha variable on deploy
All checks were successful
Test, Build, Deploy / Test, Build, and Deploy (push) Successful in 1m12s
Test, Build, Deploy / Create Sentry Release (push) Successful in 6s
2026-03-13 05:29:15 -04:00
291afd5eaa Set commit sha as release 2026-03-13 05:28:33 -04:00
8 changed files with 92 additions and 64 deletions

View File

@@ -5,6 +5,13 @@ jobs:
Test-Build-Deploy: Test-Build-Deploy:
name: Test, Build, and Deploy name: Test, Build, and Deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_PROJECT_NAME: ${{ vars.CLOUDFLARE_PROJECT_NAME }}
COMMIT_SHA: ${{ gitea.sha }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
@@ -26,14 +33,28 @@ jobs:
- name: Build - name: Build
run: npm run build run: npm run build
- name: Set Version as Var
run: |
curl https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$CLOUDFLARE_PROJECT_NAME \
-X PATCH \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"deployment_configs": {
"production": {
"env_vars": {
"COMMIT_SHA": {
"type": "plain_text",
"value": "'"$COMMIT_SHA"'"
}
}
}
}
}'
- name: Deploy - name: Deploy
run: wrangler pages deploy public --project-name $CLOUDFLARE_PROJECT_NAME run: wrangler pages deploy public --project-name $CLOUDFLARE_PROJECT_NAME
env:
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_PROJECT_NAME: ${{ vars.CLOUDFLARE_PROJECT_NAME }}
Sentry-Release: Sentry-Release:
name: Create Sentry Release name: Create Sentry Release
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -16,10 +16,18 @@ Sentry.init({
useLocation, useLocation,
useMatches, useMatches,
}), }),
Sentry.replayIntegration(), Sentry.replayIntegration({
blockAllMedia: false,
maskAllInputs: false,
maskAllText: false,
}),
], ],
replaysOnErrorSampleRate: 1, replaysOnErrorSampleRate: 1,
replaysSessionSampleRate: 0.02, replaysSessionSampleRate: 0.02,
release:
document
.querySelector("meta[name='commit_sha']")
?.getAttribute("content") ?? undefined,
sendDefaultPii: true, sendDefaultPii: true,
tracesSampleRate: 0.1, tracesSampleRate: 0.1,
tunnel: "/api/st", tunnel: "/api/st",

View File

@@ -130,6 +130,7 @@ export async function loader({
}): Promise<{ [k: string]: any }> { }): Promise<{ [k: string]: any }> {
let data: { [k: string]: string } = {}; let data: { [k: string]: string } = {};
if (context.env.COMMIT_SHA) data.commit_sha = context.env.COMMIT_SHA;
if (context.data.current_user) data = { ...context.data.current_user }; if (context.data.current_user) data = { ...context.data.current_user };
if (context.env.REMIX_DSN) data.dsn = context.env.REMIX_DSN; if (context.env.REMIX_DSN) data.dsn = context.env.REMIX_DSN;
if (context.data.nonce) data.nonce = context.data.nonce; if (context.data.nonce) data.nonce = context.data.nonce;
@@ -202,6 +203,9 @@ function DocumentWrapper(props: {
/> />
))} ))}
<meta charSet="UTF-8" /> <meta charSet="UTF-8" />
{loaderData.commit_sha ? (
<meta name="commit_sha" content={loaderData.commit_sha} />
) : null}
{loaderData.dsn ? ( {loaderData.dsn ? (
<meta name="dsn" content={loaderData.dsn} /> <meta name="dsn" content={loaderData.dsn} />
) : null} ) : null}

View File

@@ -35,29 +35,24 @@ export async function loader({ context }: { context: RequestContext }) {
month = 12; month = 12;
} }
const eventMemberQuery = await context.env.D1.prepare( const batchStatements: D1Result<Record<string, any>>[] =
"SELECT id, name, roblox_id FROM et_members;", await context.env.D1.batch([
).all(); context.env.D1.prepare("SELECT id, name, roblox_id FROM et_members;"),
const inactivityQuery: D1Result<Record<string, any>> = context.env.D1.prepare(
await context.env.D1.prepare( "SELECT decisions, json_extract(user, '$.id') AS uid FROM inactivity_notices WHERE (end BETWEEN DATE('now', 'start of month', '-1 month') AND DATE('now', 'start of month', '-1 day')) OR (start BETWEEN DATE('now', 'start of month', '-1 month') AND DATE('now', 'start of month', '-1 day'));",
"SELECT decisions, json_extract(user, '$.id') AS uid FROM inactivity_notices WHERE (end BETWEEN DATE('now', 'start of month', '-1 month') AND DATE('now', 'start of month', '-1 day')) OR (start BETWEEN DATE('now', 'start of month', '-1 month') AND DATE('now', 'start of month', '-1 day'));", ),
).all(); context.env.D1.prepare(
"SELECT approved, answered_at, created_by, performed_at, reached_minimum_player_count, type FROM events WHERE month = ? AND year = ?;",
).bind(month, year),
]);
const eventsQuery = await context.env.D1.prepare(
"SELECT approved, answered_at, created_by, performed_at, reached_minimum_player_count, type FROM events WHERE month = ? AND year = ?;",
)
.bind(month, year)
.all();
const memberMap = Object.fromEntries( const memberMap = Object.fromEntries(
eventMemberQuery.results.map((entry) => { batchStatements[0].results.map((e) => {
return [ return [e.id, { name: e.name, points: 0, roblox_id: e.roblox_id }];
entry.id,
{ name: entry.name, points: 0, roblox_id: entry.roblox_id },
];
}), }),
); );
for (const event of eventsQuery.results as { for (const event of batchStatements[2].results as {
approved: number; approved: number;
answered_at: number; answered_at: number;
created_by: string; created_by: string;
@@ -77,10 +72,10 @@ export async function loader({ context }: { context: RequestContext }) {
for (const member of Object.keys(memberMap)) for (const member of Object.keys(memberMap))
if ( if (
(memberMap[member].points < 50 || (memberMap[member].points < 50 ||
eventsQuery.results.filter( batchStatements[2].results.filter(
(e) => e.type === "gamenight" && e.created_by === member, (e) => e.type === "gamenight" && e.created_by === member,
).length === 0) && ).length === 0) &&
!inactivityQuery.results.find( !batchStatements[1].results.find(
(i) => i.uid === member && JSON.parse(i.decisions).et, (i) => i.uid === member && JSON.parse(i.decisions).et,
) )
) )

View File

@@ -33,37 +33,31 @@ export async function loader({ context }: { context: RequestContext }) {
if (!currentUser) throw new Response(null, { status: 401 }); if (!currentUser) throw new Response(null, { status: 401 });
const d1Promises = []; const batchStatements = [];
for (const itemType of ["appeals", "inactivity_notices", "reports"]) for (const itemType of ["appeals", "inactivity_notices", "reports"]) {
d1Promises.push( batchStatements.push(
context.env.D1.prepare( context.env.D1.prepare(
`SELECT * `SELECT * FROM ${itemType} WHERE json_extract(user, '$.id') = ? ORDER BY created_at DESC;`,
FROM ${itemType} ).bind(currentUser.id),
WHERE json_extract(user, '$.id') = ?
ORDER BY created_at DESC;`,
)
.bind(currentUser.id)
.all(),
); );
const settledPromises = await Promise.allSettled(d1Promises);
let etData: { [k: string]: any } | null = null;
if (currentUser.permissions & (1 << 3)) {
etData = await context.env.D1.prepare(
"SELECT name, points, roblox_id FROM et_members WHERE id = ?;",
)
.bind(currentUser.id)
.first();
} }
return { if (currentUser.permissions & (1 << 3))
etData, batchStatements.push(
items: settledPromises.map((p) => { context.env.D1.prepare(
if (p.status === "fulfilled") return p.value.results; "SELECT name, points, roblox_id FROM et_members WHERE id = ? LIMIT 1;",
).bind(currentUser.id),
);
return null; const batchResults = await context.env.D1.batch(batchStatements);
return {
etData: batchResults.at(3)?.results?.at(0) as { [k: string]: any } | null,
items: batchResults.map((r) => {
if (r.success) return r.results;
return [];
}) as any as ({ [k: string]: any }[] | null)[], }) as any as ({ [k: string]: any }[] | null)[],
permissions: currentUser.permissions as number, permissions: currentUser.permissions as number,
}; };

View File

@@ -383,6 +383,7 @@ async function setTheme(context: RequestContext) {
export const onRequest = [ export const onRequest = [
Sentry.sentryPagesPlugin((context: RequestContext) => ({ Sentry.sentryPagesPlugin((context: RequestContext) => ({
dsn: context.env.FUNCTIONS_DSN, dsn: context.env.FUNCTIONS_DSN,
release: context.env.COMMIT_SHA,
sendDefaultPii: true, sendDefaultPii: true,
})), })),
setAuth, setAuth,

View File

@@ -194,8 +194,7 @@ export async function onRequestPost(context: RequestContext) {
const reportId = `${Date.now()}${context.request.headers const reportId = `${Date.now()}${context.request.headers
.get("cf-ray") .get("cf-ray")
?.split("-") ?.replace("-", "")}${crypto.randomUUID().replaceAll("-", "")}`;
?.at(0)}${crypto.randomUUID().replaceAll("-", "")}`;
const { current_user: currentUser } = context.data; const { current_user: currentUser } = context.data;
if (filesToProcess.length) if (filesToProcess.length)

View File

@@ -12,18 +12,24 @@ export async function onRequestPost(context: RequestContext) {
const sentryUrl = new URL(dsn); const sentryUrl = new URL(dsn);
await fetch(`https://${sentryUrl.host}/api${sentryUrl.pathname}/envelope`, { const resp = await fetch(
body: clonedRequest.body, `https://${sentryUrl.host}/api${sentryUrl.pathname}/envelope/`,
headers: { {
"content-type": "application/x-sentry-envelope", body: clonedRequest.body,
"x-forwarded-for": context.request.headers.get( headers: {
"cf-connecting-ip", "content-type": "application/x-sentry-envelope",
) as string, "x-forwarded-for": context.request.headers.get(
"cf-connecting-ip",
) as string,
},
method: "POST",
}, },
method: "POST", );
});
return new Response(null, { return new Response(await resp.text(), {
status: 204, headers: {
"content-type": resp.headers.get("content-type") || "text/plain",
},
status: resp.status,
}); });
} }