Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/frontend/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use client';

import * as Sentry from '@sentry/nextjs';
import Link from 'next/link';
import { useEffect } from 'react';

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Next.js sets `digest` on errors that originated on the server, and the
// Sentry server SDK has already reported those. Only report genuine
// client-side failures here, so a single error is never billed twice.
if (error.digest === undefined) {
Sentry.captureException(error);
}
}, [error]);

return (
<main className="flex flex-col gap-8 py-12">
<header className="flex flex-col gap-2 px-4 md:px-20">
<h1 className="text-2xl font-bold">Something went wrong</h1>
<p>
This page failed to load. It is usually temporary — trying again
often works.
</p>
</header>

<div className="flex flex-row gap-4 px-4 md:px-20">
<button
onClick={reset}
className="rounded-md bg-[#e7e5be] px-4 py-2 hover:underline"
>
Try again
</button>
<Link href="/" className="px-4 py-2 hover:underline">
Back to home
</Link>
</div>

{error.digest !== undefined && (
<p className="px-4 md:px-20 text-sm">
Reference: <code>{error.digest}</code>
</p>
)}
</main>
);
}
38 changes: 23 additions & 15 deletions apps/frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Sentry from '@sentry/nextjs';
import Image from 'next/image';
import './global.css';
import Link from 'next/link';
Expand All @@ -23,22 +24,29 @@ export default async function RootLayout({
}: {
children: React.ReactNode;
}) {
const defaultGameTypes = await prisma.gameType.findMany({
select: {
name: true,
playerCount: true,
mapCount: true,
},
orderBy: [
{
playerCount: 'desc',
// error.tsx cannot catch root layout errors, so a failure here would take
// down every page at once. Degrade to empty tabs instead.
const defaultGameTypes = await prisma.gameType
.findMany({
select: {
name: true,
playerCount: true,
mapCount: true,
},
{
mapCount: 'desc',
},
],
take: 2,
});
orderBy: [
{
playerCount: 'desc',
},
{
mapCount: 'desc',
},
],
take: 2,
})
.catch((error) => {
Sentry.captureException(error);
return [];
});

return (
<html lang="en">
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/app/server/[ip]/[port]/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { decodeIp } from "../../../../utils/encoding";

export const paramsSchema = z.object({
ip: z.string().transform(decodeIp),
port: z.coerce.number().int().positive(),
port: z.coerce.number().int().positive().max(65535).catch(0),
});
12 changes: 5 additions & 7 deletions apps/frontend/sentry.client.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ if (process.env.SENTRY_DSN) {
Sentry.init({
dsn: process.env.SENTRY_DSN,

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
tracesSampleRate: 0.01,

// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,

replaysOnErrorSampleRate: 1.0,

// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,
// Replay is the tightest quota on the free plan (~50/month), so record only
// sessions that actually hit an error, never a percentage of all sessions.
replaysOnErrorSampleRate: 0.1,
replaysSessionSampleRate: 0,

// You can remove this option if you're not planning to use the Sentry Session Replay feature:
integrations: [
Expand Down
6 changes: 4 additions & 2 deletions apps/frontend/sentry.edge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@

import * as Sentry from "@sentry/nextjs";
import prisma from "./utils/prisma";
import { ignoreTransactions, tracesSampleRate, tracesSampler } from "./utils/sentrySampling";

if (process.env.SENTRY_DSN) {
Sentry.init({
dsn: process.env.SENTRY_DSN,

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
tracesSampleRate,
tracesSampler,
ignoreTransactions,

// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
Expand Down
6 changes: 4 additions & 2 deletions apps/frontend/sentry.server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

import * as Sentry from "@sentry/nextjs";
import prisma from "./utils/prisma";
import { ignoreTransactions, tracesSampleRate, tracesSampler } from "./utils/sentrySampling";

if (process.env.SENTRY_DSN) {
Sentry.init({
dsn: process.env.SENTRY_DSN,

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
tracesSampleRate,
tracesSampler,
ignoreTransactions,

// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
Expand Down
14 changes: 10 additions & 4 deletions apps/frontend/utils/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ export function encodeString(str: string) {
}
}

// Never throws: malformed percent-escapes fall back to the raw segment, which
// matches no record, so the page 404s instead of raising a 500.
export function decodeString(str: string) {
if (str.startsWith('_')) {
return base64url.decode(str.slice(1));
} else {
return decodeURIComponent(str);
try {
if (str.startsWith('_')) {
return base64url.decode(str.slice(1));
} else {
return decodeURIComponent(str);
}
} catch {
return str;
}
}
28 changes: 28 additions & 0 deletions apps/frontend/utils/sentrySampling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { isbot } from 'isbot';
import { z } from 'zod';

const DEFAULT_TRACES_SAMPLE_RATE = 0.01;

export const tracesSampleRate = z
.string()
.trim()
.min(1)
.pipe(z.coerce.number().min(0).max(1))
.catch(DEFAULT_TRACES_SAMPLE_RATE)
.parse(process.env.SENTRY_TRACES_SAMPLE_RATE);

export const ignoreTransactions = [
'/monitoring',
'/_next/',
'/favicon.ico',
'/robots.txt',
'/sitemap',
];

type SamplingContext = {
request?: { headers?: Record<string, string> };
};

export function tracesSampler({ request }: SamplingContext) {
return isbot(request?.headers?.['user-agent']) ? 0 : tracesSampleRate;
}
3 changes: 0 additions & 3 deletions apps/scheduler/src/schedulers/gameServerScheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
import { minutesToMilliseconds } from "date-fns";
import { prisma } from "../prisma";
import { schedule, scheduleWithSpread } from "../utils";
import { captureMessage } from "@sentry/node";

let lastId = 0;
let queuesFull = false;
Expand Down Expand Up @@ -45,8 +44,6 @@ export async function gameServerScheduler() {
for (const gameServer of gameServers) {
scheduleWithSpread(minutesToMilliseconds(5), async () => {
if (queuesFull) {
console.log('Queues are full, skipping game server poll');
captureMessage('Queues are full, skipping game server poll');
return;
}

Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"fuse.js": "^7.0.0",
"ioredis": "^5.5.0",
"ioredis-mock": "^8.9.0",
"isbot": "^5.2.1",
"lodash": "^4.17.21",
"lodash.groupby": "^4.6.0",
"lodash.isequal": "^4.5.0",
Expand Down
Loading