diff --git a/src/app/p/[photoId]/layout.tsx b/src/app/p/[photoId]/layout.tsx index 1a00868a..dea922f3 100644 --- a/src/app/p/[photoId]/layout.tsx +++ b/src/app/p/[photoId]/layout.tsx @@ -17,6 +17,8 @@ import { getPhotoIds } from '@/photo/db/query'; import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db'; import { ReactNode, cache } from 'react'; +export const maxDuration = 60; + const getPhotosNearIdCachedCached = cache((photoId: string) => getPhotosNearIdCached(photoId, { limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 })); diff --git a/src/app/page.tsx b/src/app/page.tsx index 60f23a12..de3bde77 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,7 @@ import { getPhotos, getPhotosMeta } from '@/photo/db/query'; import PhotosLargeInfinite from '@/photo/PhotosLargeInfinite'; export const dynamic = 'force-static'; +export const maxDuration = 60; const getPhotosCached = cache(() => getPhotos({ limit: INFINITE_SCROLL_LARGE_PHOTO_INITIAL, diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 80f69397..fa6d9970 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -56,10 +56,10 @@ export const createPhotoAction = async (formData: FormData) => const photo = convertFormDataToPhotoDbInsert(formData); - const updatedUrl = await convertUploadToPhoto( - photo.url, + const updatedUrl = await convertUploadToPhoto({ + urlOrigin: photo.url, shouldStripGpsData, - ); + }); if (updatedUrl) { photo.url = updatedUrl; @@ -141,11 +141,11 @@ export const addAllUploadsAction = async ({ addedUploadUrls: addedUploadUrls.join(','), }); - const updatedUrl = await convertUploadToPhoto( - url, - shouldStripGpsData, + const updatedUrl = await convertUploadToPhoto({ + urlOrigin: url, fileBytes, - ); + shouldStripGpsData, + }); if (updatedUrl) { const subhead = 'Adding to database'; stream.update({ @@ -179,16 +179,25 @@ export const updatePhotoAction = async (formData: FormData) => runAuthenticatedAdminServerAction(async () => { const photo = convertFormDataToPhotoDbInsert(formData); - let url: string | undefined; + let urlToDelete: string | undefined; if (photo.hidden && photo.url.includes(photo.id)) { // Backfill: // Anonymize storage url on update if necessary by // re-running image upload transfer logic - url = await convertUploadToPhoto(photo.url); - if (url) { photo.url = url; } + const url = await convertUploadToPhoto({ + urlOrigin: photo.url, + shouldDeleteOrigin: false, + }); + if (url) { + urlToDelete = photo.url; + photo.url = url; + } } - await updatePhoto(photo); + await updatePhoto(photo) + .then(async () => { + if (urlToDelete) { await deleteFile(urlToDelete); } + }); revalidatePhoto(photo.id); @@ -307,16 +316,21 @@ export const syncPhotoAction = async (photoId: string) => generateResizedImage: AI_TEXT_GENERATION_ENABLED, }); + let urlToDelete: string | undefined; if (photoFormExif) { if (photo.url.includes(photo.id) || shouldStripGpsData) { // Anonymize storage url on update if necessary by // re-running image upload transfer logic - const url = await convertUploadToPhoto( - photo.url, - shouldStripGpsData, + const url = await convertUploadToPhoto({ + urlOrigin: photo.url, fileBytes, - ); - if (url) { photo.url = url; } + shouldStripGpsData, + shouldDeleteOrigin: false, + }); + if (url) { + urlToDelete = photo.url; + photo.url = url; + } } const { @@ -340,7 +354,11 @@ export const syncPhotoAction = async (photoId: string) => { semanticDescription: aiSemanticDescription }, }); - await updatePhoto(photoFormDbInsert); + await updatePhoto(photoFormDbInsert) + .then(async () => { + if (urlToDelete) { await deleteFile(urlToDelete); } + }); + revalidateAllKeysAndPaths(); } } diff --git a/src/photo/storage.ts b/src/photo/storage.ts index b5396615..1999f6f1 100644 --- a/src/photo/storage.ts +++ b/src/photo/storage.ts @@ -1,4 +1,5 @@ import { + copyFile, deleteFile, generateRandomFileNameForPhoto, getExtensionFromStorageUrl, @@ -7,24 +8,32 @@ import { } from '@/services/storage'; import { removeGpsData } from './server'; -export const convertUploadToPhoto = async ( - urlOrigin: string, - stripGps?: boolean, - fileBytes?: ArrayBuffer, -) => { +export const convertUploadToPhoto = async ({ + urlOrigin, + fileBytes, + shouldStripGpsData, + shouldDeleteOrigin = true, +} : { + urlOrigin: string + fileBytes?: ArrayBuffer + shouldStripGpsData?: boolean + shouldDeleteOrigin?: boolean +}) => { const fileName = generateRandomFileNameForPhoto(); const fileExtension = getExtensionFromStorageUrl(urlOrigin); const photoPath = `${fileName}.${fileExtension || 'jpg'}`; - if (stripGps) { + if (shouldStripGpsData) { const fileWithoutGps = await removeGpsData( fileBytes ?? await fetch(urlOrigin, { cache: 'no-store' }) .then(res => res.arrayBuffer()) ); return putFile(fileWithoutGps, photoPath).then(async url => { - if (url) { await deleteFile(urlOrigin); } + if (url && shouldDeleteOrigin) { await deleteFile(urlOrigin); } return url; }); } else { - return moveFile(urlOrigin, photoPath); + return shouldDeleteOrigin + ? moveFile(urlOrigin, photoPath) + : copyFile(urlOrigin, photoPath); } };