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);