feat(frontend): show error ID for internal server errors

This commit is contained in:
sam 2025-01-30 02:00:49 +01:00
parent 83b62b4845
commit a0ba712632
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
8 changed files with 1554 additions and 25 deletions

View file

@ -1,7 +1,18 @@
# Example .env file--DO NOT EDIT, copy to .env or .env.local then edit
# The language the frontend will use. Valid languages are listed in src/lib/i18n/index.ts.
PUBLIC_LANGUAGE=en
# The public base URL, i.e. the one users will see. Used for building links.
PUBLIC_BASE_URL=https://pronouns.cc
# The base URL for the URL shortener service. Used for building short links.
PUBLIC_SHORT_URL=https://prns.cc
# The base public URL for the API. This is (almost) always the public base URL + /api.
PUBLIC_API_BASE=https://pronouns.cc/api
# The base *private* URL for the API's rate limiter proxy. The frontend will rewrite API URLs to use this.
# In development, you can set this to the same value as $PRIVATE_INTERNAL_API_HOST, but be aware that this will disable rate limiting.
PRIVATE_API_HOST=http://localhost:5003/api
# The base private URL for the API, which bypasses the rate limiter. Used for /api/internal paths and unauthenticated GET requests.
PRIVATE_INTERNAL_API_HOST=http://localhost:6000/api
# The Sentry URL to use. Optional.
PRIVATE_SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0

View file

@ -39,6 +39,7 @@
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c",
"dependencies": {
"@fontsource/firago": "^5.1.0",
"@sentry/sveltekit": "^8.52.0",
"base64-arraybuffer": "^1.0.2",
"bootstrap-icons": "^1.11.3",
"luxon": "^3.5.0",

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,8 @@ declare global {
message: string;
status: number;
code: ErrorCode;
id: string;
errors?: Array<{ key: string; errors: ValidationError[] }>;
error_id?: string;
}
// interface Error {}
// interface Locals {}

View file

@ -1,8 +1,10 @@
import ApiError, { ErrorCode } from "$api/error";
import { PRIVATE_API_HOST, PRIVATE_INTERNAL_API_HOST } from "$env/static/private";
import { env } from "$env/dynamic/private";
import { PUBLIC_API_BASE } from "$env/static/public";
import log from "$lib/log";
import type { HandleFetch, HandleServerError } from "@sveltejs/kit";
import * as Sentry from "@sentry/sveltekit";
export const handleFetch: HandleFetch = async ({ request, fetch }) => {
if (request.url.startsWith(`${PUBLIC_API_BASE}/internal`)) {
@ -14,12 +16,17 @@ export const handleFetch: HandleFetch = async ({ request, fetch }) => {
return await fetch(request);
};
Sentry.init({
dsn: env.PRIVATE_SENTRY_DSN,
});
export const handleError: HandleServerError = async ({ error, status, message }) => {
const id = crypto.randomUUID();
// as far as i know, sentry IDs are just UUIDs with the dashes removed. use those here as well
let id = crypto.randomUUID().replaceAll("-", "");
if (error instanceof ApiError) {
return {
id,
error_id: id,
status: error.raw?.status || status,
message: error.raw?.message || "Unknown error",
code: error.code,
@ -27,10 +34,18 @@ export const handleError: HandleServerError = async ({ error, status, message })
}
if (status >= 400 && status <= 499) {
return { id, status, message, code: ErrorCode.GenericApiError };
return { error_id: id, status, message, code: ErrorCode.GenericApiError };
}
// client errors and backend API errors just clog up sentry, so we don't send those.
id = Sentry.captureException(error, {
mechanism: {
type: "sveltekit",
handled: false,
},
});
log.error("[%s] error in handler:", id, error);
return { id, status, message, code: ErrorCode.InternalServerError };
return { error_id: id, status, message, code: ErrorCode.InternalServerError };
};

View file

@ -14,6 +14,7 @@ export default class ApiError {
toObject(): RawApiError {
return {
error_id: this.raw?.error_id,
status: this.raw?.status || 500,
code: this.code,
message: this.raw?.message || "Internal server error",
@ -23,6 +24,7 @@ export default class ApiError {
}
export type RawApiError = {
error_id?: string;
status: number;
message: string;
code: ErrorCode;

View file

@ -18,6 +18,14 @@
</svelte:element>
{/if}
<p>{errorDescription($t, error.code)}</p>
{#if error.error_id}
<p>
{$t("error.error-id")}
<code>
{error.error_id}
</code>
</p>
{/if}
{#if error.errors}
<details>
<summary>{$t("error.extra-info-header")}</summary>

View file

@ -110,7 +110,8 @@
"back-to-prev-page-button": "Go back to the previous page",
"400-description": "Something went wrong with your request. This error should never land you on this page, so it's probably a bug.",
"500-description": "Something went wrong on the server. Please try again later.",
"unknown-status-description": "Something went wrong, but we're not sure what. Please try again."
"unknown-status-description": "Something went wrong, but we're not sure what. Please try again.",
"error-id": "If you report this error to the developers, please give them this ID:"
},
"settings": {
"general-information-tab": "General information",