diff --git a/src/app/admin/photos/[photoId]/edit/page.tsx b/src/app/admin/photos/[photoId]/edit/page.tsx
index 6321b102..ebf0d73c 100644
--- a/src/app/admin/photos/[photoId]/edit/page.tsx
+++ b/src/app/admin/photos/[photoId]/edit/page.tsx
@@ -2,7 +2,11 @@ import { redirect } from 'next/navigation';
import { getPhotoNoStore, getUniqueTagsCached } from '@/photo/cache';
import { PATH_ADMIN } from '@/site/paths';
import PhotoEditPageClient from '@/photo/PhotoEditPageClient';
-import { AI_TEXT_GENERATION_ENABLED, BLUR_ENABLED } from '@/site/config';
+import {
+ AI_TEXT_GENERATION_ENABLED,
+ BLUR_ENABLED,
+ IS_PREVIEW,
+} from '@/site/config';
import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server';
import { getNextImageUrlForManipulation } from '@/services/next-image';
@@ -23,12 +27,14 @@ export default async function PhotoEditPage({
// Only generate image thumbnails when AI generation is enabled
const imageThumbnailBase64 = AI_TEXT_GENERATION_ENABLED
- ? await resizeImageFromUrl(getNextImageUrlForManipulation(photo.url))
+ ? await resizeImageFromUrl(
+ getNextImageUrlForManipulation(photo.url, IS_PREVIEW)
+ )
: '';
const blurData = BLUR_ENABLED
? await blurImageFromUrl(
- getNextImageUrlForManipulation(photo.url)
+ getNextImageUrlForManipulation(photo.url, IS_PREVIEW)
)
: '';
diff --git a/src/image-response/components/ImagePhotoGrid.tsx b/src/image-response/components/ImagePhotoGrid.tsx
index 3722d562..cdec9da2 100644
--- a/src/image-response/components/ImagePhotoGrid.tsx
+++ b/src/image-response/components/ImagePhotoGrid.tsx
@@ -5,6 +5,7 @@ import {
NextImageSize,
getNextImageUrlForRequest,
} from '@/services/next-image';
+import { IS_PREVIEW } from '@/site/config';
export default function ImagePhotoGrid({
photos,
@@ -64,7 +65,13 @@ export default function ImagePhotoGrid({
}}
>
setFormData(data => ({ ...data, blurData }))}
/>
diff --git a/src/photo/index.ts b/src/photo/index.ts
index 97c96b71..17f8c381 100644
--- a/src/photo/index.ts
+++ b/src/photo/index.ts
@@ -3,7 +3,7 @@ import { formatFocalLength } from '@/focal';
import { Lens } from '@/lens';
import { getNextImageUrlForRequest } from '@/services/next-image';
import { FilmSimulation } from '@/simulation';
-import { HIGH_DENSITY_GRID, SHOW_EXIF_DATA } from '@/site/config';
+import { HIGH_DENSITY_GRID, IS_PREVIEW, SHOW_EXIF_DATA } from '@/site/config';
import { ABSOLUTE_PATH_FOR_HOME_IMAGE } from '@/site/paths';
import { formatDate, formatDateFromPostgresString } from '@/utility/date';
import {
@@ -307,8 +307,16 @@ export const getKeywordsForPhoto = (photo: Photo) =>
.filter(Boolean)
.map(keyword => keyword.toLocaleLowerCase());
-export const isNextImageReadyBasedOnPhotos = async (photos: Photo[]) =>
- photos.length > 0 && fetch(getNextImageUrlForRequest(photos[0].url, 640))
+export const isNextImageReadyBasedOnPhotos = async (
+ photos: Photo[],
+): Promise =>
+ photos.length > 0 && fetch(getNextImageUrlForRequest(
+ photos[0].url,
+ 640,
+ undefined,
+ undefined,
+ IS_PREVIEW,
+ ))
.then(response => response.ok)
.catch(() => false);
diff --git a/src/services/next-image.ts b/src/services/next-image.ts
index 13ce7ffe..682130d1 100644
--- a/src/services/next-image.ts
+++ b/src/services/next-image.ts
@@ -1,4 +1,8 @@
-import { BASE_URL } from '@/site/config';
+import {
+ BASE_URL,
+ VERCEL_BYPASS_KEY,
+ VERCEL_BYPASS_SECRET,
+} from '@/site/config';
// Explicity defined next.config.js `imageSizes`
type NextCustomSize = 200;
@@ -14,6 +18,7 @@ export const getNextImageUrlForRequest = (
size: NextImageSize,
quality = 75,
baseUrl = BASE_URL,
+ addBypassSecret = false,
) => {
const url = new URL(`${baseUrl}/_next/image`);
@@ -21,10 +26,17 @@ export const getNextImageUrlForRequest = (
url.searchParams.append('w', size.toString());
url.searchParams.append('q', quality.toString());
+ if (addBypassSecret && VERCEL_BYPASS_SECRET) {
+ url.searchParams.append(VERCEL_BYPASS_KEY, VERCEL_BYPASS_SECRET);
+ }
+
return url.toString();
};
// Generate small, low-bandwidth images for quick manipulations such as
// generating blur data or image thumbnails for AI text generation
-export const getNextImageUrlForManipulation = (imageUrl: string) =>
- getNextImageUrlForRequest(imageUrl, 640, 90);
+export const getNextImageUrlForManipulation = (
+ imageUrl: string,
+ addBypassSecret = false,
+) =>
+ getNextImageUrlForRequest(imageUrl, 640, 90, undefined, addBypassSecret);
diff --git a/src/site/config.ts b/src/site/config.ts
index 55961890..7a632de7 100644
--- a/src/site/config.ts
+++ b/src/site/config.ts
@@ -35,6 +35,11 @@ export const IS_PRODUCTION = process.env.NODE_ENV === 'production' && (
!VERCEL_ENV
);
+export const IS_PREVIEW = VERCEL_ENV === 'preview';
+
+export const VERCEL_BYPASS_KEY = 'x-vercel-protection-bypass';
+export const VERCEL_BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
+
// User-facing domain, potential site title
const SITE_DOMAIN =
process.env.NEXT_PUBLIC_SITE_DOMAIN ||
diff --git a/src/utility/vercel.ts b/src/utility/vercel.ts
new file mode 100644
index 00000000..fd788f11
--- /dev/null
+++ b/src/utility/vercel.ts
@@ -0,0 +1,16 @@
+import {
+ IS_PREVIEW,
+ VERCEL_BYPASS_KEY,
+ VERCEL_BYPASS_SECRET,
+} from '@/site/config';
+
+export const fetchWithBypass: typeof fetch = (url, options) =>
+ IS_PREVIEW && VERCEL_BYPASS_SECRET
+ ? fetch(url, {
+ ...options,
+ headers: {
+ ...options?.headers,
+ [VERCEL_BYPASS_KEY]: VERCEL_BYPASS_SECRET,
+ },
+ })
+ : fetch(url, options);