diff --git a/app/home-image/route.tsx b/app/home-image/route.tsx
index a8a2231a..68df76be 100644
--- a/app/home-image/route.tsx
+++ b/app/home-image/route.tsx
@@ -5,10 +5,9 @@ import {
} from '@/image-response';
import HomeImageResponse from '@/image-response/HomeImageResponse';
import { getIBMPlexMono } from '@/app/font';
-import { ImageResponse } from 'next/og';
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
-import { isNextImageReadyBasedOnPhotos } from '@/photo';
import { APP_OG_IMAGE_QUERY_OPTIONS } from '@/feed';
+import { safePhotoImageResponse } from '@/platforms/safe-photo-image-response';
export const dynamic = 'force-static';
@@ -29,17 +28,15 @@ export async function GET() {
const { width, height } = IMAGE_OG_DIMENSION_SMALL;
- // Make sure next/image can be reached from absolute urls,
- // which may not exist on first pre-render
- const isNextImageReady = await isNextImageReadyBasedOnPhotos(photos);
-
- return new ImageResponse(
- ,
- { width, height, headers, fonts },
+ return safePhotoImageResponse(
+ photos,
+ isNextImageReady => (
+
+ ), { width, height, headers, fonts },
);
}
diff --git a/app/p/[photoId]/image/route.tsx b/app/p/[photoId]/image/route.tsx
index b70c6a89..6f8b6024 100644
--- a/app/p/[photoId]/image/route.tsx
+++ b/app/p/[photoId]/image/route.tsx
@@ -2,10 +2,9 @@ import { getPhotoCached } from '@/photo/cache';
import { IMAGE_OG_DIMENSION } from '@/image-response';
import PhotoImageResponse from '@/image-response/PhotoImageResponse';
import { getIBMPlexMono } from '@/app/font';
-import { ImageResponse } from 'next/og';
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
-import { isNextImageReadyBasedOnPhotos } from '@/photo';
import { staticallyGeneratePhotosIfConfigured } from '@/app/static';
+import { safePhotoImageResponse } from '@/platforms/safe-photo-image-response';
export const generateStaticParams = staticallyGeneratePhotosIfConfigured(
'image',
@@ -30,19 +29,18 @@ export async function GET(
if (!photo) { return new Response('Photo not found', { status: 404 }); }
const { width, height } = IMAGE_OG_DIMENSION;
-
- // Make sure next/image can be reached from absolute urls,
- // which may not exist on first pre-render
- const isNextImageReady = await isNextImageReadyBasedOnPhotos([photo]);
- return new ImageResponse(
- ,
+ return safePhotoImageResponse(
+ [photo],
+ isNextImageReady => (
+
+ ),
{ width, height, fonts, headers },
);
}
diff --git a/app/recents/image/route.tsx b/app/recents/image/route.tsx
index ef607dca..d7a40faa 100644
--- a/app/recents/image/route.tsx
+++ b/app/recents/image/route.tsx
@@ -6,11 +6,10 @@ import {
import RecentsImageResponse from
'@/image-response/RecentsImageResponse';
import { getIBMPlexMono } from '@/app/font';
-import { ImageResponse } from 'next/og';
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
import { getAppText } from '@/i18n/state/server';
import { SHOW_RECENTS } from '@/app/config';
-import { isNextImageReadyBasedOnPhotos } from '@/photo';
+import { safePhotoImageResponse } from '@/platforms/safe-photo-image-response';
export const dynamic = 'force-static';
@@ -36,18 +35,17 @@ export async function GET() {
const { width, height } = IMAGE_OG_DIMENSION_SMALL;
- // Make sure next/image can be reached from absolute urls,
- // which may not exist on first pre-render
- const isNextImageReady = await isNextImageReadyBasedOnPhotos(photos);
-
- return new ImageResponse(
- ,
+ return safePhotoImageResponse(
+ photos,
+ isNextImageReady => (
+
+ ),
{ width, height, fonts, headers },
);
}
diff --git a/app/template-image-tight/route.tsx b/app/template-image-tight/route.tsx
index d1ee4201..61b3fcd3 100644
--- a/app/template-image-tight/route.tsx
+++ b/app/template-image-tight/route.tsx
@@ -6,9 +6,8 @@ import {
import TemplateImageResponse from
'@/image-response/TemplateImageResponse';
import { getIBMPlexMono } from '@/app/font';
-import { ImageResponse } from 'next/og';
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
-import { isNextImageReadyBasedOnPhotos } from '@/photo';
+import { safePhotoImageResponse } from '@/platforms/safe-photo-image-response';
export async function GET() {
const [
@@ -26,12 +25,9 @@ export async function GET() {
const { width, height } = IMAGE_OG_DIMENSION;
- // Make sure next/image can be reached from absolute urls,
- // which may not exist on first pre-render
- const isNextImageReady = await isNextImageReadyBasedOnPhotos(photos);
-
- return new ImageResponse(
- (
+ return safePhotoImageResponse(
+ photos,
+ isNextImageReady => (
(
.filter(Boolean)
.map(keyword => keyword.toLocaleLowerCase());
-export const isNextImageReadyBasedOnPhotos = async (
- photos: Photo[],
-): Promise =>
- photos.length > 0 && fetch(getNextImageUrlForRequest({
- imageUrl: photos[0].url,
- size: 640,
- addBypassSecret: IS_PREVIEW,
- }))
- .then(response => response.ok)
- .catch(() => false);
-
export const downloadFileNameForPhoto = (photo: Photo) =>
photo.title
? `${parameterize(photo.title)}.${photo.extension}`
diff --git a/src/platforms/safe-photo-image-response.ts b/src/platforms/safe-photo-image-response.ts
new file mode 100644
index 00000000..3744f628
--- /dev/null
+++ b/src/platforms/safe-photo-image-response.ts
@@ -0,0 +1,32 @@
+import { Photo } from '@/photo';
+import { ImageResponse } from 'next/og';
+import { JSX } from 'react';
+import { getNextImageUrlForRequest } from './next-image';
+import { IS_PREVIEW } from '@/app/config';
+
+const isNextImageReadyBasedOnPhotos = async (
+ photos: Photo[],
+): Promise =>
+ photos.length > 0 &&
+ fetch(getNextImageUrlForRequest({
+ imageUrl: photos[0].url,
+ size: 640,
+ addBypassSecret: IS_PREVIEW,
+ }))
+ .then(response => response.ok)
+ .catch(() => false);
+
+export const safePhotoImageResponse = async (
+ photos: Photo[],
+ jsx: (isNextImageReady: boolean) => JSX.Element,
+ options: ConstructorParameters[1],
+) => {
+ // Make sure next/image can be reached from absolute urls,
+ // which may not exist on first pre-render
+ const isNextImageReady = await isNextImageReadyBasedOnPhotos(photos);
+
+ return new ImageResponse(
+ jsx(isNextImageReady),
+ options,
+ );
+};