Skip to content

Commit

Permalink
fix: backbutton and better error in error pages (#337)
Browse files Browse the repository at this point in the history
Co-authored-by: Mike Diarmid <[email protected]>
  • Loading branch information
mhadaily and Salakar authored Mar 13, 2024
1 parent 17e3aa2 commit 013d699
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 74 deletions.
77 changes: 56 additions & 21 deletions api/src/bundler/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import parseConfig, { Config, defaultConfig } from '../utils/config';
import { getGitHubContents, getPullRequestMetadata } from '../utils/github';
import { bundle } from './mdx';
import { escapeHtml } from '../utils/sanitize';

export class BundlerError extends Error {
constructor(public code: number, name: string, message: string) {
code: number;
links?: { title: string; url: string }[];

constructor({
code,
name,
message,
cause,
links,
}: {
code: number;
name: string;
message: string;
cause?: string;
links?: { title: string; url: string }[];
}) {
super(message);
this.code = code;
this.name = name;
this.message = message;
this.cause = cause;
this.links = links;
}
}

Expand Down Expand Up @@ -95,21 +114,29 @@ class Bundler {
});

if (!metadata) {
throw new BundlerError(
404,
ERROR_CODES.REPO_NOT_FOUND,
`The repository ${this.#source.owner}/${this.#source.repository} was not found.`,
);
throw new BundlerError({
code: 404,
name: ERROR_CODES.REPO_NOT_FOUND,
message: `The repository ${this.#source.owner}/${this.#source.repository} was not found.`,
});
}

if (!metadata.md) {
throw new BundlerError(
404,
ERROR_CODES.FILE_NOT_FOUND,
`The file "/docs/${this.#path}.mdx" or "/docs/${this.#path}/index.mdx" in repository ${
this.#source.owner
}/${this.#source.repository} was not found.`,
);
throw new BundlerError({
code: 404,
name: ERROR_CODES.FILE_NOT_FOUND,
message: `The file "/docs/${this.#path}.mdx" or "/docs/${
this.#path
}/index.mdx" in repository /${
this.#source.owner + '/' + this.#source.repository
} was not found.`,
links: [
{
title: 'Repository link',
url: `https://github.com/${this.#source.owner}/${this.#source.repository}`,
},
],
});
}

this.#markdown = metadata.md;
Expand Down Expand Up @@ -153,14 +180,22 @@ class Bundler {
};
} catch (e) {
console.error(e);
throw new BundlerError(
500,
ERROR_CODES.BUNDLE_ERROR,
`Something went wrong while bundling the file. Are you sure the MDX is valid? ${
// @ts-ignore
e?.message || ''
}`,
);
// @ts-ignore
const message = escapeHtml(e?.message || '');
throw new BundlerError({
code: 500,
name: ERROR_CODES.BUNDLE_ERROR,
message: `Something went wrong while bundling the file /${metadata.path}.mdx. Are you sure the MDX is valid?`,
cause: message,
links: [
{
title: `/${metadata.path}.mdx on GitHub`,
url: `https://github.com/${this.#source.owner}/${this.#source.repository}/blob/${
this.#ref
}/${metadata.path}.mdx`,
},
],
});
}
};
}
Expand Down
6 changes: 5 additions & 1 deletion api/src/res.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export function response<T extends object>(
code: string,
other:
| {
error: string;
error: {
message: string;
cause?: string | unknown;
links?: { title: string; url: string }[];
};
}
| {
data: T;
Expand Down
6 changes: 5 additions & 1 deletion api/src/routes/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export default async function bundle(req: Request, res: Response): Promise<Respo
} catch (e: unknown) {
if (e instanceof BundlerError) {
return response(res, e.code, e.name, {
error: e.message,
error: {
message: e.message,
cause: e.cause,
links: e.links,
},
});
}
return serverError(res, e);
Expand Down
8 changes: 8 additions & 0 deletions api/src/utils/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const escapeHtml = (text: string): string => {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
};
9 changes: 8 additions & 1 deletion website/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,27 @@ const $GetBundleResponseSuccess = z.object({
),
});

const $GetBundleResponseError = z.object({
message: z.string().catch(''),
cause: z.string().optional(),
links: z.array(z.object({ title: z.string(), url: z.string() })).optional(),
});

const $GetBundleResponse = z.union([
z.object({
code: z.literal('OK'),
data: $GetBundleResponseSuccess,
}),
z.object({
code: z.enum(['NOT_FOUND', 'BAD_REQUEST', 'REPO_NOT_FOUND', 'FILE_NOT_FOUND', 'BUNDLE_ERROR']),
error: z.string().catch(''),
error: $GetBundleResponseError,
}),
]);

export type GetBundleRequest = z.infer<typeof $GetBundleRequest>;
export type GetBundleResponse = z.infer<typeof $GetBundleResponse>;
export type GetBundleResponseSuccess = z.infer<typeof $GetBundleResponseSuccess>;
export type GetBundleResponseError = z.infer<typeof $GetBundleResponseError>;

export type BundleConfig = z.infer<typeof $BundleConfig>;
export type SidebarArray = z.infer<typeof $SidebarArray>;
Expand Down
120 changes: 80 additions & 40 deletions website/src/layouts/ErrorPage.astro
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
---
import type { GetBundleResponseError } from 'src/bundle';
type Props = {
code: number;
errorName?: string;
title?: string;
description?: string;
href?: string;
extraInfo?: string;
description?: string;
cause?: GetBundleResponseError['cause'];
links?: GetBundleResponseError['links'];
};
const { code, title, description, href, extraInfo } = Astro.props;
const { code, title, description, href, errorName, cause, links } = Astro.props;
---

<div class="flex h-[100vh] flex-col pt-16 pb-12">
Expand All @@ -21,45 +25,81 @@ const { code, title, description, href, extraInfo } = Astro.props;
</div>
<div class="py-16">
<div class="text-center">
<p class="text-sm font-semibold uppercase tracking-wide text-green-400">{code} error</p>
<h1
class="mt-3 font-open-sans text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl"
>
{title || 'Something went wrong.'}
</h1>
<p class="mt-4 text-base text-gray-500 dark:text-gray-300 max-w-xl mx-auto">
{description || `Sorry, something went wrong during the request.`}
<p class="text-sm font-semibold uppercase tracking-wide text-green-400">
{code} error
<h1
class="mt-3 font-open-sans text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl"
>
{title || 'Something went wrong.'}
</h1>

{
description && (
<div class="mt-6">
{description || `Sorry, something went wrong during the request.`}
{cause && (
<p class="mt-6">
Possible cause:
{errorName && (
<b class="text-sm font-semibold uppercase tracking-wide text-green-400">
{errorName}
</b>
)}
<pre class="flex justify-center overflow-auto">
<code set:html={cause} />
</pre>
</p>
)}
{links && links.length > 0 && (
<ul class="mt-6">
{links.map(({ title, url }) => {
return (
<li>
<a
class="text-base font-medium text-green-400 hover:text-green-500"
href={url}
target="_blank"
rel="noopener noreferrer nofollow"
title={title}
>
{title}
</a>
</li>
);
})}
</ul>
)}
</div>
)
}

<div class="mt-16">
<a
href={href || '/'}
class="rounded border border-gray-600 px-6 py-2 no-underline transition-all duration-100 hover:border-gray-300 dark:hover:border-white"
>
<span aria-hidden="true"> &larr;</span> Go back
</a>
</div>
</p>
{
extraInfo && (
<p class="mx-auto mt-4 max-w-xl text-base text-gray-500 dark:text-gray-300">
{extraInfo}
</p>
)
}
<div class="mt-6">
<a href={href || '/'} class="text-base font-medium text-green-400 hover:text-green-500">
Go back <span aria-hidden="true"> &rarr;</span>
</a>
</div>
</div>
</div>
<footer class="mx-auto w-full max-w-7xl flex-shrink-0 px-4 sm:px-6 lg:px-8">
<nav class="flex justify-center space-x-4">
<a
href="https://github.com/invertase/docs.page"
class="text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-gray-300"
>
GitHub
</a>
<span class="inline-block border-l border-gray-300" aria-hidden="true"></span>
<a
href="https://twitter.com/invertaseio"
class="text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-gray-300"
>
Twitter
</a>
</nav>
</footer>
</main>
<footer class="mx-auto w-full max-w-7xl flex-shrink-0 px-4 sm:px-6 lg:px-8">
<nav class="flex justify-center space-x-4">
<a
href="https://github.com/invertase/docs.page"
class="text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-gray-300"
>
GitHub
</a>
<span class="inline-block border-l border-gray-300" aria-hidden="true"></span>
<a
href="https://twitter.com/invertaseio"
class="text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-gray-300"
>
Twitter
</a>
</nav>
</footer>
</div>
23 changes: 13 additions & 10 deletions website/src/pages/[owner]/[repository]/[...path].astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Theme from '@components/Theme.astro';
import { getBundle } from 'src/bundle';
import context from 'src/context';
import type { GetBundleResponse } from 'src/bundle';
import type { GetBundleResponse, GetBundleResponseError } from 'src/bundle';
import { isExternalLink, replaceMoustacheVariables, ensureLeadingSlash } from 'src/utils';
import { trackPageRequest } from 'src/plausible';
import domains from '../../../../../domains.json';
Expand All @@ -36,7 +36,7 @@ if (repository.includes('~')) {
let response: GetBundleResponse | undefined;
let code: GetBundleResponse['code'];
let message: string = '';
let error: GetBundleResponseError = { message: '' };
try {
response = await getBundle({
Expand All @@ -47,11 +47,12 @@ try {
});
code = response.code;
if ('error' in response) message = response.error;
// refer to [api/src/routes/bundle.ts] for more information;
if ('error' in response) error = response.error;
} catch (e) {
console.error(e);
code = 'BUNDLE_ERROR';
message = 'An error occurred while fetching the bundle.'
error.message = 'An error occurred while fetching the bundle.'
}
const statusCodes = {
Expand Down Expand Up @@ -180,10 +181,11 @@ function codeToTitle(code: GetBundleResponse['code']) {
<Root>
<ErrorPage
code={404}
errorName={code}
title={codeToTitle(code)}
description={`(${code})`}
extraInfo={message}
href={code === 'REPO_NOT_FOUND' ? '/' : `/${owner}/${repository}`}
description={error.message}
cause={error.cause}
links={error.links}
/>
</Root>
)
Expand All @@ -195,9 +197,10 @@ function codeToTitle(code: GetBundleResponse['code']) {
<ErrorPage
code={500}
title="Something went wrong"
description={`(${code})`}
extraInfo={message}
href={`/${owner}/${repository}`}
errorName={code}
description={error.message}
cause={error.cause}
links={error.links}
/>
</Root>
)
Expand Down

0 comments on commit 013d699

Please sign in to comment.