From 12cf156af3c270669738e74a8c49c2fb59ca7341 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 16 Feb 2025 10:19:16 -0600 Subject: [PATCH] Combine /services and /platforms --- src/admin/AdminAppConfigurationClient.tsx | 2 +- src/admin/AdminNav.tsx | 2 +- src/admin/AdminPhotosClient.tsx | 2 +- src/admin/AdminUploadsClient.tsx | 2 +- src/admin/AdminUploadsTable.tsx | 2 +- src/admin/actions.ts | 8 ++++---- src/app-core/api.ts | 2 +- src/app-core/config.ts | 2 +- src/app/admin/photos/[photoId]/edit/page.tsx | 2 +- src/app/admin/photos/page.tsx | 2 +- src/app/admin/uploads/page.tsx | 2 +- src/app/api/storage/presigned-url/[key]/route.ts | 4 ++-- src/app/api/storage/vercel-blob/route.ts | 2 +- src/components/ImageInput.tsx | 2 +- src/image-response/CameraImageResponse.tsx | 2 +- src/image-response/FilmSimulationImageResponse.tsx | 2 +- src/image-response/FocalLengthImageResponse.tsx | 2 +- src/image-response/HomeImageResponse.tsx | 2 +- src/image-response/PhotoImageResponse.tsx | 2 +- src/image-response/TagImageResponse.tsx | 2 +- src/image-response/TemplateImageResponse.tsx | 2 +- src/image-response/components/ImagePhotoGrid.tsx | 2 +- src/image-response/index.ts | 2 +- src/photo/PhotoUpload.tsx | 2 +- src/photo/actions.ts | 4 ++-- src/photo/ai/server.ts | 2 +- src/photo/db/query.ts | 2 +- src/photo/form/PhotoForm.tsx | 2 +- src/photo/index.ts | 2 +- src/photo/server.ts | 2 +- src/photo/storage.ts | 2 +- src/{services => platforms}/kv.ts | 0 src/{services => platforms}/next-image.ts | 0 src/{services => platforms}/openai.ts | 0 src/{services => platforms}/postgres.ts | 0 src/{services => platforms}/storage/aws-s3.ts | 0 src/{services => platforms}/storage/cache.ts | 2 +- src/{services => platforms}/storage/cloudflare-r2.ts | 0 src/{services => platforms}/storage/index.ts | 0 src/{services => platforms}/storage/vercel-blob.ts | 0 40 files changed, 37 insertions(+), 37 deletions(-) rename src/{services => platforms}/kv.ts (100%) rename src/{services => platforms}/next-image.ts (100%) rename src/{services => platforms}/openai.ts (100%) rename src/{services => platforms}/postgres.ts (100%) rename src/{services => platforms}/storage/aws-s3.ts (100%) rename src/{services => platforms}/storage/cache.ts (81%) rename src/{services => platforms}/storage/cloudflare-r2.ts (100%) rename src/{services => platforms}/storage/index.ts (100%) rename src/{services => platforms}/storage/vercel-blob.ts (100%) diff --git a/src/admin/AdminAppConfigurationClient.tsx b/src/admin/AdminAppConfigurationClient.tsx index 51e2322f..1359f309 100644 --- a/src/admin/AdminAppConfigurationClient.tsx +++ b/src/admin/AdminAppConfigurationClient.tsx @@ -15,7 +15,7 @@ import { HiOutlineCog } from 'react-icons/hi'; import ChecklistGroup from '@/components/ChecklistGroup'; import { ConfigChecklistStatus } from '../app-core/config'; import StatusIcon from '@/components/StatusIcon'; -import { labelForStorage } from '@/services/storage'; +import { labelForStorage } from '@/platforms/storage'; import { HiSparkles } from 'react-icons/hi'; import { testConnectionsAction } from '@/admin/actions'; import ErrorNote from '@/components/ErrorNote'; diff --git a/src/admin/AdminNav.tsx b/src/admin/AdminNav.tsx index ceef18c9..18c330f6 100644 --- a/src/admin/AdminNav.tsx +++ b/src/admin/AdminNav.tsx @@ -1,4 +1,4 @@ -import { getStorageUploadUrlsNoStore } from '@/services/storage/cache'; +import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache'; import { getPhotosMetaCached, getPhotosMostRecentUpdateCached, diff --git a/src/admin/AdminPhotosClient.tsx b/src/admin/AdminPhotosClient.tsx index 35015494..1199ca0c 100644 --- a/src/admin/AdminPhotosClient.tsx +++ b/src/admin/AdminPhotosClient.tsx @@ -12,7 +12,7 @@ import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite'; import PathLoaderButton from '@/components/primitives/PathLoaderButton'; import { PATH_ADMIN_OUTDATED } from '@/app-core/paths'; import { Photo } from '@/photo'; -import { StorageListResponse } from '@/services/storage'; +import { StorageListResponse } from '@/platforms/storage'; import { useState } from 'react'; import { LiaBroomSolid } from 'react-icons/lia'; import AdminUploadsTable from './AdminUploadsTable'; diff --git a/src/admin/AdminUploadsClient.tsx b/src/admin/AdminUploadsClient.tsx index 924c3b94..89bb928e 100644 --- a/src/admin/AdminUploadsClient.tsx +++ b/src/admin/AdminUploadsClient.tsx @@ -1,6 +1,6 @@ 'use client'; -import { StorageListResponse } from '@/services/storage'; +import { StorageListResponse } from '@/platforms/storage'; import AdminAddAllUploads from './AdminAddAllUploads'; import { useMemo, useState } from 'react'; import { Tags } from '@/tag'; diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index 027c4a53..6f9967d5 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -2,7 +2,7 @@ import ImageSmall from '@/components/image/ImageSmall'; import Spinner from '@/components/Spinner'; -import { getIdFromStorageUrl } from '@/services/storage'; +import { getIdFromStorageUrl } from '@/platforms/storage'; import { clsx } from 'clsx/lite'; import { FaRegCircleCheck } from 'react-icons/fa6'; import { pathForAdminUploadUrl } from '@/app-core/paths'; diff --git a/src/admin/actions.ts b/src/admin/actions.ts index ee366c7a..4ef29686 100644 --- a/src/admin/actions.ts +++ b/src/admin/actions.ts @@ -1,10 +1,10 @@ 'use server'; import { runAuthenticatedAdminServerAction } from '@/auth'; -import { testKvConnection } from '@/services/kv'; -import { testOpenAiConnection } from '@/services/openai'; -import { testDatabaseConnection } from '@/services/postgres'; -import { testStorageConnection } from '@/services/storage'; +import { testKvConnection } from '@/platforms/kv'; +import { testOpenAiConnection } from '@/platforms/openai'; +import { testDatabaseConnection } from '@/platforms/postgres'; +import { testStorageConnection } from '@/platforms/storage'; import { APP_CONFIGURATION } from '@/app-core/config'; const scanForError = ( diff --git a/src/app-core/api.ts b/src/app-core/api.ts index 54ba5cbe..1a5a2d90 100644 --- a/src/app-core/api.ts +++ b/src/app-core/api.ts @@ -1,7 +1,7 @@ import { Photo } from '@/photo'; import { absolutePathForPhoto } from './paths'; import { formatDateFromPostgresString } from '@/utility/date'; -import { getNextImageUrlForRequest } from '@/services/next-image'; +import { getNextImageUrlForRequest } from '@/platforms/next-image'; export const API_PHOTO_REQUEST_LIMIT = 40; diff --git a/src/app-core/config.ts b/src/app-core/config.ts index 7dbdf6c9..e7a483d8 100644 --- a/src/app-core/config.ts +++ b/src/app-core/config.ts @@ -2,7 +2,7 @@ import { AI_AUTO_GENERATED_FIELDS_DEFAULT, parseAiAutoGeneratedFieldsString, } from '@/photo/ai'; -import type { StorageType } from '@/services/storage'; +import type { StorageType } from '@/platforms/storage'; import { makeUrlAbsolute, shortenUrl } from '@/utility/url'; // HARD-CODED GLOBAL CONFIGURATION diff --git a/src/app/admin/photos/[photoId]/edit/page.tsx b/src/app/admin/photos/[photoId]/edit/page.tsx index 25076704..c83c11a3 100644 --- a/src/app/admin/photos/[photoId]/edit/page.tsx +++ b/src/app/admin/photos/[photoId]/edit/page.tsx @@ -8,7 +8,7 @@ import { IS_PREVIEW, } from '@/app-core/config'; import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server'; -import { getNextImageUrlForManipulation } from '@/services/next-image'; +import { getNextImageUrlForManipulation } from '@/platforms/next-image'; export default async function PhotoEditPage({ params, diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index d8ef3d11..169c752d 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -1,4 +1,4 @@ -import { getStoragePhotoUrlsNoStore } from '@/services/storage/cache'; +import { getStoragePhotoUrlsNoStore } from '@/platforms/storage/cache'; import { getPhotos } from '@/photo/db/query'; import { getPhotosMetaCached } from '@/photo/cache'; import { OUTDATED_THRESHOLD } from '@/photo'; diff --git a/src/app/admin/uploads/page.tsx b/src/app/admin/uploads/page.tsx index 531512e6..41c46930 100644 --- a/src/app/admin/uploads/page.tsx +++ b/src/app/admin/uploads/page.tsx @@ -1,4 +1,4 @@ -import { getStorageUploadUrlsNoStore } from '@/services/storage/cache'; +import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache'; import SiteGrid from '@/components/SiteGrid'; import { getUniqueTagsCached } from '@/photo/cache'; import AdminUploadsClient from '@/admin/AdminUploadsClient'; diff --git a/src/app/api/storage/presigned-url/[key]/route.ts b/src/app/api/storage/presigned-url/[key]/route.ts index 0406b95c..99d078f5 100644 --- a/src/app/api/storage/presigned-url/[key]/route.ts +++ b/src/app/api/storage/presigned-url/[key]/route.ts @@ -2,11 +2,11 @@ import { auth } from '@/auth'; import { awsS3Client, awsS3PutObjectCommandForKey, -} from '@/services/storage/aws-s3'; +} from '@/platforms/storage/aws-s3'; import { cloudflareR2Client, cloudflareR2PutObjectCommandForKey, -} from '@/services/storage/cloudflare-r2'; +} from '@/platforms/storage/cloudflare-r2'; import { CURRENT_STORAGE } from '@/app-core/config'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; diff --git a/src/app/api/storage/vercel-blob/route.ts b/src/app/api/storage/vercel-blob/route.ts index 6cc63e9b..d32fbd08 100644 --- a/src/app/api/storage/vercel-blob/route.ts +++ b/src/app/api/storage/vercel-blob/route.ts @@ -4,7 +4,7 @@ import { ACCEPTED_PHOTO_FILE_TYPES, MAX_PHOTO_UPLOAD_SIZE_IN_BYTES, } from '@/photo'; -import { isUploadPathnameValid } from '@/services/storage'; +import { isUploadPathnameValid } from '@/platforms/storage'; import { handleUpload, type HandleUploadBody } from '@vercel/blob/client'; import { NextResponse } from 'next/server'; diff --git a/src/components/ImageInput.tsx b/src/components/ImageInput.tsx index 24fd2359..3bac3e98 100644 --- a/src/components/ImageInput.tsx +++ b/src/components/ImageInput.tsx @@ -7,7 +7,7 @@ import exifr from 'exifr'; import { clsx } from 'clsx/lite'; import { ACCEPTED_PHOTO_FILE_TYPES } from '@/photo'; import { FiUploadCloud } from 'react-icons/fi'; -import { MAX_IMAGE_SIZE } from '@/services/next-image'; +import { MAX_IMAGE_SIZE } from '@/platforms/next-image'; import ProgressButton from './primitives/ProgressButton'; const INPUT_ID = 'file'; diff --git a/src/image-response/CameraImageResponse.tsx b/src/image-response/CameraImageResponse.tsx index f94278f8..8ff9fcb9 100644 --- a/src/image-response/CameraImageResponse.tsx +++ b/src/image-response/CameraImageResponse.tsx @@ -9,7 +9,7 @@ import { isCameraApple, } from '@/camera'; import { IoMdCamera } from 'react-icons/io'; -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; import { AiFillApple } from 'react-icons/ai'; export default function CameraImageResponse({ diff --git a/src/image-response/FilmSimulationImageResponse.tsx b/src/image-response/FilmSimulationImageResponse.tsx index dd93ac18..3b458146 100644 --- a/src/image-response/FilmSimulationImageResponse.tsx +++ b/src/image-response/FilmSimulationImageResponse.tsx @@ -8,7 +8,7 @@ import { import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; import { FilmSimulation } from '@/simulation'; -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; export default function FilmSimulationImageResponse({ simulation, diff --git a/src/image-response/FocalLengthImageResponse.tsx b/src/image-response/FocalLengthImageResponse.tsx index fc78c4a1..ae43c17d 100644 --- a/src/image-response/FocalLengthImageResponse.tsx +++ b/src/image-response/FocalLengthImageResponse.tsx @@ -2,7 +2,7 @@ import type { Photo } from '../photo'; import ImageCaption from './components/ImageCaption'; import ImagePhotoGrid from './components/ImagePhotoGrid'; import ImageContainer from './components/ImageContainer'; -import type { NextImageSize } from '@/services/next-image'; +import type { NextImageSize } from '@/platforms/next-image'; import { TbCone } from 'react-icons/tb'; import { formatFocalLength } from '@/focal'; diff --git a/src/image-response/HomeImageResponse.tsx b/src/image-response/HomeImageResponse.tsx index d563f898..e6f3789b 100644 --- a/src/image-response/HomeImageResponse.tsx +++ b/src/image-response/HomeImageResponse.tsx @@ -3,7 +3,7 @@ import { Photo } from '../photo'; import ImageCaption from './components/ImageCaption'; import ImageContainer from './components/ImageContainer'; import ImagePhotoGrid from './components/ImagePhotoGrid'; -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; export default function HomeImageResponse({ photos, diff --git a/src/image-response/PhotoImageResponse.tsx b/src/image-response/PhotoImageResponse.tsx index 22c9202e..20eee2b0 100644 --- a/src/image-response/PhotoImageResponse.tsx +++ b/src/image-response/PhotoImageResponse.tsx @@ -4,7 +4,7 @@ import ImageCaption from './components/ImageCaption'; import ImagePhotoGrid from './components/ImagePhotoGrid'; import ImageContainer from './components/ImageContainer'; import { OG_TEXT_BOTTOM_ALIGNMENT } from '@/app-core/config'; -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; import { cameraFromPhoto, formatCameraText } from '@/camera'; export default function PhotoImageResponse({ diff --git a/src/image-response/TagImageResponse.tsx b/src/image-response/TagImageResponse.tsx index df63269c..b9e8c2c7 100644 --- a/src/image-response/TagImageResponse.tsx +++ b/src/image-response/TagImageResponse.tsx @@ -3,7 +3,7 @@ import { FaStar, FaTag } from 'react-icons/fa'; import ImageCaption from './components/ImageCaption'; import ImagePhotoGrid from './components/ImagePhotoGrid'; import ImageContainer from './components/ImageContainer'; -import type { NextImageSize } from '@/services/next-image'; +import type { NextImageSize } from '@/platforms/next-image'; import { formatTag, isTagFavs } from '@/tag'; export default function TagImageResponse({ diff --git a/src/image-response/TemplateImageResponse.tsx b/src/image-response/TemplateImageResponse.tsx index 1954d722..7a063b54 100644 --- a/src/image-response/TemplateImageResponse.tsx +++ b/src/image-response/TemplateImageResponse.tsx @@ -2,7 +2,7 @@ import { Photo } from '../photo'; import IconFeed from '@/app-core/IconFeed'; import IconGrid from '@/app-core/IconGrid'; import ImagePhotoGrid from './components/ImagePhotoGrid'; -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; export default function TemplateImageResponse({ photos, diff --git a/src/image-response/components/ImagePhotoGrid.tsx b/src/image-response/components/ImagePhotoGrid.tsx index 758e4ff9..6c253843 100644 --- a/src/image-response/components/ImagePhotoGrid.tsx +++ b/src/image-response/components/ImagePhotoGrid.tsx @@ -4,7 +4,7 @@ import { Photo } from '@/photo'; import { NextImageSize, getNextImageUrlForRequest, -} from '@/services/next-image'; +} from '@/platforms/next-image'; import { IS_PREVIEW } from '@/app-core/config'; export default function ImagePhotoGrid({ diff --git a/src/image-response/index.ts b/src/image-response/index.ts index 37fd8a6a..151d519b 100644 --- a/src/image-response/index.ts +++ b/src/image-response/index.ts @@ -1,4 +1,4 @@ -import { NextImageSize } from '@/services/next-image'; +import { NextImageSize } from '@/platforms/next-image'; import { getDimensionsFromSize } from '@/utility/size'; export const MAX_PHOTOS_TO_SHOW_OG = 12; diff --git a/src/photo/PhotoUpload.tsx b/src/photo/PhotoUpload.tsx index 338e584a..e19b43ae 100644 --- a/src/photo/PhotoUpload.tsx +++ b/src/photo/PhotoUpload.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -import { uploadPhotoFromClient } from '@/services/storage'; +import { uploadPhotoFromClient } from '@/platforms/storage'; import { useRouter } from 'next/navigation'; import { PATH_ADMIN_UPLOADS, pathForAdminUploadUrl } from '@/app-core/paths'; import ImageInput from '../components/ImageInput'; diff --git a/src/photo/actions.ts b/src/photo/actions.ts index ed728cd3..6b35c082 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -18,7 +18,7 @@ import { convertPhotoToFormData, } from './form'; import { redirect } from 'next/navigation'; -import { deleteFile } from '@/services/storage'; +import { deleteFile } from '@/platforms/storage'; import { getPhotosCached, getPhotosMetaCached, @@ -39,7 +39,7 @@ import { TAG_FAVS, isTagFavs } from '@/tag'; import { convertPhotoToPhotoDbInsert, Photo } from '.'; import { runAuthenticatedAdminServerAction } from '@/auth'; import { AiImageQuery, getAiImageQuery } from './ai'; -import { streamOpenAiImageQuery } from '@/services/openai'; +import { streamOpenAiImageQuery } from '@/platforms/openai'; import { AI_TEXT_AUTO_GENERATED_FIELDS, AI_TEXT_GENERATION_ENABLED, diff --git a/src/photo/ai/server.ts b/src/photo/ai/server.ts index 5355256e..e1ac4130 100644 --- a/src/photo/ai/server.ts +++ b/src/photo/ai/server.ts @@ -1,4 +1,4 @@ -import { generateOpenAiImageQuery } from '@/services/openai'; +import { generateOpenAiImageQuery } from '@/platforms/openai'; import { AiAutoGeneratedField, getAiImageQuery, diff --git a/src/photo/db/query.ts b/src/photo/db/query.ts index 8936060a..1180eda9 100644 --- a/src/photo/db/query.ts +++ b/src/photo/db/query.ts @@ -2,7 +2,7 @@ import { sql, query, convertArrayToPostgresString, -} from '@/services/postgres'; +} from '@/platforms/postgres'; import { PhotoDb, PhotoDbInsert, diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index d132021a..3d9d4db0 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -26,7 +26,7 @@ import Spinner from '@/components/Spinner'; import usePreventNavigation from '@/utility/usePreventNavigation'; import { useAppState } from '@/state/AppState'; import UpdateBlurDataButton from '../UpdateBlurDataButton'; -import { getNextImageUrlForManipulation } from '@/services/next-image'; +import { getNextImageUrlForManipulation } from '@/platforms/next-image'; import { BLUR_ENABLED, IS_PREVIEW } from '@/app-core/config'; import { PhotoDbInsert } from '..'; import ErrorNote from '@/components/ErrorNote'; diff --git a/src/photo/index.ts b/src/photo/index.ts index cf4ec16d..b2fd808e 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -1,7 +1,7 @@ import { Camera } from '@/camera'; import { formatFocalLength } from '@/focal'; import { Lens } from '@/lens'; -import { getNextImageUrlForRequest } from '@/services/next-image'; +import { getNextImageUrlForRequest } from '@/platforms/next-image'; import { FilmSimulation } from '@/simulation'; import { HIGH_DENSITY_GRID, diff --git a/src/photo/server.ts b/src/photo/server.ts index bd07b560..9206d95d 100644 --- a/src/photo/server.ts +++ b/src/photo/server.ts @@ -1,7 +1,7 @@ import { getExtensionFromStorageUrl, getIdFromStorageUrl, -} from '@/services/storage'; +} from '@/platforms/storage'; import { convertExifToFormData } from '@/photo/form'; import { getFujifilmSimulationFromMakerNote, diff --git a/src/photo/storage.ts b/src/photo/storage.ts index a28e4428..49f71052 100644 --- a/src/photo/storage.ts +++ b/src/photo/storage.ts @@ -5,7 +5,7 @@ import { getExtensionFromStorageUrl, moveFile, putFile, -} from '@/services/storage'; +} from '@/platforms/storage'; import { removeGpsData } from './server'; export const convertUploadToPhoto = async ({ diff --git a/src/services/kv.ts b/src/platforms/kv.ts similarity index 100% rename from src/services/kv.ts rename to src/platforms/kv.ts diff --git a/src/services/next-image.ts b/src/platforms/next-image.ts similarity index 100% rename from src/services/next-image.ts rename to src/platforms/next-image.ts diff --git a/src/services/openai.ts b/src/platforms/openai.ts similarity index 100% rename from src/services/openai.ts rename to src/platforms/openai.ts diff --git a/src/services/postgres.ts b/src/platforms/postgres.ts similarity index 100% rename from src/services/postgres.ts rename to src/platforms/postgres.ts diff --git a/src/services/storage/aws-s3.ts b/src/platforms/storage/aws-s3.ts similarity index 100% rename from src/services/storage/aws-s3.ts rename to src/platforms/storage/aws-s3.ts diff --git a/src/services/storage/cache.ts b/src/platforms/storage/cache.ts similarity index 81% rename from src/services/storage/cache.ts rename to src/platforms/storage/cache.ts index 4bcaf57e..54647c8c 100644 --- a/src/services/storage/cache.ts +++ b/src/platforms/storage/cache.ts @@ -1,5 +1,5 @@ import { unstable_noStore } from 'next/cache'; -import { getStoragePhotoUrls, getStorageUploadUrls } from '@/services/storage'; +import { getStoragePhotoUrls, getStorageUploadUrls } from '@/platforms/storage'; export const getStorageUploadUrlsNoStore: typeof getStorageUploadUrls = (...args) => { diff --git a/src/services/storage/cloudflare-r2.ts b/src/platforms/storage/cloudflare-r2.ts similarity index 100% rename from src/services/storage/cloudflare-r2.ts rename to src/platforms/storage/cloudflare-r2.ts diff --git a/src/services/storage/index.ts b/src/platforms/storage/index.ts similarity index 100% rename from src/services/storage/index.ts rename to src/platforms/storage/index.ts diff --git a/src/services/storage/vercel-blob.ts b/src/platforms/storage/vercel-blob.ts similarity index 100% rename from src/services/storage/vercel-blob.ts rename to src/platforms/storage/vercel-blob.ts