Add PPR error screening to admin pages

This commit is contained in:
Sam Becker 2024-02-26 11:53:34 -06:00
parent 1dc267d07c
commit d06de4d8ea
8 changed files with 39 additions and 19 deletions

View File

@ -1,11 +1,11 @@
import { authCached } from '@/auth/cache';
import { authCachedSafe } from '@/auth/cache';
import AdminPhotoMenuClient from './AdminPhotoMenuClient';
import { ComponentProps } from 'react';
export default async function AdminPhotoMenu(
props: ComponentProps<typeof AdminPhotoMenuClient>,
) {
const session = await authCached();
const session = await authCachedSafe();
return Boolean(session?.user?.email)
? <AdminPhotoMenuClient {...props} />
: null;

View File

@ -1,4 +1,6 @@
import { cache } from 'react';
import { auth } from '@/auth';
import { screenForPPR } from '@/utility/ppr';
export const authCached = cache(auth);
export const authCachedSafe = cache(() => auth()
.catch(e => screenForPPR(e, null, 'auth')));

View File

@ -27,6 +27,7 @@ import {
isUrlFromCloudflareR2,
} from './cloudflare-r2';
import { PATH_API_PRESIGNED_URL } from '@/site/paths';
import { screenForPPR } from '@/utility/ppr';
export const generateStorageId = () => generateNanoid(16);
@ -191,13 +192,16 @@ const getStorageUrlsForPrefix = async (prefix = '') => {
const urls: StorageListResponse = [];
if (HAS_VERCEL_BLOB_STORAGE) {
urls.push(...await vercelBlobList(prefix));
urls.push(...await vercelBlobList(prefix)
.catch(e => screenForPPR(e, [], 'vercel blob')));
}
if (HAS_AWS_S3_STORAGE) {
urls.push(...await awsS3List(prefix));
urls.push(...await awsS3List(prefix)
.catch(e => screenForPPR(e, [], 'aws blob')));
}
if (HAS_CLOUDFLARE_R2_STORAGE) {
urls.push(...await cloudflareR2List(prefix));
urls.push(...await cloudflareR2List(prefix)
.catch(e => screenForPPR(e, [], 'cloudflare blob')));
}
return urls

View File

@ -12,6 +12,7 @@ import { parameterize } from '@/utility/string';
import { Tags } from '@/tag';
import { FilmSimulation, FilmSimulations } from '@/simulation';
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
import { screenForPPR } from '@/utility/ppr';
const PHOTO_DEFAULT_LIMIT = 100;
@ -283,11 +284,8 @@ const safelyQueryPhotos = async <T>(callback: () => Promise<T>): Promise<T> => {
try {
result = await callback();
} catch (e: any) {
if (/ppr-caught-error/.test(e.message) && e.sourceError) {
// PPR errors, if caught, must be re-thrown in order to
// postpone rendering
throw e.sourceError;
} else if (/relation "photos" does not exist/i.test(e.message)) {
screenForPPR(e, undefined, 'neon postgres');
if (/relation "photos" does not exist/i.test(e.message)) {
console.log('Creating table "photos" because it did not exist');
await sqlCreatePhotosTable();
result = await callback();

View File

@ -17,7 +17,7 @@ import {
pathForTag,
} from './paths';
import { formatCameraText } from '@/camera';
import { authCached } from '@/auth/cache';
import { authCachedSafe } from '@/auth/cache';
import { getPhotos } from '@/services/vercel-postgres';
import { photoQuantityText, titleForPhoto } from '@/photo';
import PhotoTiny from '@/photo/PhotoTiny';
@ -45,7 +45,7 @@ export default async function CommandK() {
getUniqueFilmSimulationsCached().catch(() => []),
]);
const session = await authCached().catch(() => null);
const session = await authCachedSafe();
const isAdminLoggedIn = Boolean(session?.user?.email);

View File

@ -1,9 +1,8 @@
import { authCached } from '@/auth/cache';
import { authCachedSafe } from '@/auth/cache';
import FooterClient from './FooterClient';
export default async function Footer() {
// Make footer auth resilient to error on first time setup
const session = await authCached().catch(() => null);
const session = await authCachedSafe();
return (
<FooterClient userEmail={session?.user?.email} />
);

View File

@ -1,9 +1,8 @@
import { authCached } from '@/auth/cache';
import { authCachedSafe } from '@/auth/cache';
import NavClient from './NavClient';
export default async function Nav() {
// Make nav auth resilient to error on first time setup
const session = await authCached().catch(() => null);
const session = await authCachedSafe();
return (
<NavClient showAdmin={Boolean(session?.user?.email)} />
);

18
src/utility/ppr.ts Normal file
View File

@ -0,0 +1,18 @@
export const screenForPPR = <T>(
error: any,
fallback: T,
sourceToLog?: string,
): T => {
if (/ppr-caught-error/.test(error.message) && error.sourceError) {
// PPR errors, if caught, must be re-thrown in order to
// postpone rendering
console.log(
sourceToLog ? `${sourceToLog}: PPR error caught` : 'PPR error caught',
error.sourceError,
);
throw error.sourceError;
} else if (sourceToLog) {
console.error(sourceToLog, error.sourceError);
}
return fallback;
};