From abf3fc34edecff6dd77f41850ba745db457a6c68 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 12:38:36 -0600 Subject: [PATCH 1/8] Only show photo info overlay in og images when exif data is present --- .../image-response/PhotoImageResponse.tsx | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/photo/image-response/PhotoImageResponse.tsx b/src/photo/image-response/PhotoImageResponse.tsx index 499ca283..5874e4dd 100644 --- a/src/photo/image-response/PhotoImageResponse.tsx +++ b/src/photo/image-response/PhotoImageResponse.tsx @@ -1,4 +1,4 @@ -import { Photo } from '..'; +import { Photo, photoHasExifData } from '..'; import { AiFillApple } from 'react-icons/ai'; import ImageCaption from './components/ImageCaption'; import ImagePhotoGrid from './components/ImagePhotoGrid'; @@ -30,25 +30,26 @@ export default function PhotoImageResponse({ height, ...OG_TEXT_BOTTOM_ALIGNMENT && { imagePosition: 'top' }, }} /> - - {photo.make === 'Apple' && + {photoHasExifData(photo) && + + {photo.make === 'Apple' && +
+ +
} + {model && +
+ {model} +
}
- -
} - {model && + {photo.focalLengthFormatted} +
- {model} -
} -
- {photo.focalLengthFormatted} -
-
- {photo.fNumberFormatted} -
-
- {photo.isoFormatted} -
-
+ {photo.fNumberFormatted} + +
+ {photo.isoFormatted} +
+
} ); }; From db878b79f785ef9514efaa46e7cf8a45ab623f27 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 16:50:15 -0600 Subject: [PATCH 2/8] Add configuration option for hiding EXIF data --- README.md | 1 + src/photo/PhotoLarge.tsx | 11 ++++++++--- src/photo/image-response/PhotoImageResponse.tsx | 4 ++-- src/photo/index.ts | 11 +++++++++-- src/site/SiteChecklistClient.tsx | 10 ++++++++++ src/site/config.ts | 2 ++ 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8ebd954d..1968658b 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ Installation - `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api` - `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo - `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar +- `NEXT_PUBLIC_HIDE_EXIF_DATA = 1` hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography) - `NEXT_PUBLIC_GRID_ASPECT_RATIO = 1.5` sets aspect ratio for grid tiles (defaults to `1`—setting to `0` removes the constraint) - `NEXT_PUBLIC_OG_TEXT_ALIGNMENT = BOTTOM` keeps OG image text bottom aligned (default is top) diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 935a3b55..466caf34 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -1,4 +1,9 @@ -import { Photo, photoHasCameraData, photoHasExifData, titleForPhoto } from '.'; +import { + Photo, + shouldShowCameraDataForPhoto, + shouldShowExifDataForPhoto, + titleForPhoto, +} from '.'; import SiteGrid from '@/components/SiteGrid'; import ImageLarge from '@/components/ImageLarge'; import { clsx } from 'clsx/lite'; @@ -92,7 +97,7 @@ export default function PhotoLarge({ {tags.length > 0 && } - {showCamera && photoHasCameraData(photo) && + {showCamera && shouldShowCameraDataForPhoto(photo) &&
} )} {renderMiniGrid(<> - {photoHasExifData(photo) && + {shouldShowExifDataForPhoto(photo) &&
  • {photo.focalLengthFormatted} diff --git a/src/photo/image-response/PhotoImageResponse.tsx b/src/photo/image-response/PhotoImageResponse.tsx index 5874e4dd..fcc28897 100644 --- a/src/photo/image-response/PhotoImageResponse.tsx +++ b/src/photo/image-response/PhotoImageResponse.tsx @@ -1,4 +1,4 @@ -import { Photo, photoHasExifData } from '..'; +import { Photo, shouldShowExifDataForPhoto } from '..'; import { AiFillApple } from 'react-icons/ai'; import ImageCaption from './components/ImageCaption'; import ImagePhotoGrid from './components/ImagePhotoGrid'; @@ -30,7 +30,7 @@ export default function PhotoImageResponse({ height, ...OG_TEXT_BOTTOM_ALIGNMENT && { imagePosition: 'top' }, }} /> - {photoHasExifData(photo) && + {shouldShowExifDataForPhoto(photo) && {photo.make === 'Apple' &&
    diff --git a/src/photo/index.ts b/src/photo/index.ts index a03b81ba..f7806cf6 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -1,4 +1,5 @@ import { FilmSimulation } from '@/simulation'; +import { SHOW_EXIF_DATA } from '@/site/config'; import { ABSOLUTE_PATH_FOR_HOME_IMAGE } from '@/site/paths'; import { formatDateFromPostgresString } from '@/utility/date'; import { @@ -221,14 +222,20 @@ export const dateRangeForPhotos = ( return { start, end, description }; }; -export const photoHasCameraData = (photo: Photo) => +const photoHasCameraData = (photo: Photo) => photo.make && photo.model; -export const photoHasExifData = (photo: Photo) => +const photoHasExifData = (photo: Photo) => photo.focalLength || photo.focalLengthIn35MmFormat || photo.fNumberFormatted || photo.isoFormatted || photo.exposureTimeFormatted || photo.exposureCompensationFormatted; + +export const shouldShowCameraDataForPhoto = (photo: Photo) => + SHOW_EXIF_DATA && photoHasCameraData(photo); + +export const shouldShowExifDataForPhoto = (photo: Photo) => + SHOW_EXIF_DATA && photoHasExifData(photo); diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 6ea9004e..43a86929 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -35,6 +35,7 @@ export default function SiteChecklistClient({ hasDomain, showRepoLink, showFilmSimulations, + showExifInfo, isProModeEnabled, isGeoPrivacyEnabled, isPriorityOrderEnabled, @@ -317,6 +318,15 @@ export default function SiteChecklistClient({ simulations showing up in /grid sidebar: {renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])} + + Set environment variable to {'"1"'} to hide EXIF data: + {renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])} + 0, showRepoLink: SHOW_REPO_LINK, showFilmSimulations: SHOW_FILM_SIMULATIONS, + showExifInfo: SHOW_EXIF_DATA, isProModeEnabled: PRO_MODE_ENABLED, isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED, isPriorityOrderEnabled: PRIORITY_ORDER_ENABLED, From 6bc406da364a2f2060b644e5ff7f5db346609a9e Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 16:53:04 -0600 Subject: [PATCH 3/8] Clarify storage config language --- src/site/SiteChecklistClient.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 43a86929..fb940901 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -147,7 +147,8 @@ export default function SiteChecklistClient({ title={!hasStorage ? 'Setup storage (one of the following)' : hasMultipleStorageProviders - ? `Setup storage (current: ${labelForStorage(currentStorage)})` + // eslint-disable-next-line max-len + ? `Setup storage (new uploads go to: ${labelForStorage(currentStorage)})` : 'Setup storage'} status={hasStorage} isPending={isPendingPage} From 6010e75aeb37c6544f7a21827d659c7530565598 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 18:24:30 -0600 Subject: [PATCH 4/8] Allow custom domains in Cloudflare R2 integration --- README.md | 25 ++++++++++++++----------- next.config.js | 4 +--- src/services/storage/cloudflare-r2.ts | 10 +++++----- src/site/config.ts | 2 +- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1968658b..848e9e11 100644 --- a/README.md +++ b/README.md @@ -83,24 +83,27 @@ Only one storage adapter—Vercel Blob, Cloudflare R2, or AWS S3—can be used a - Setup CORS under bucket settings: ```json [{ - "AllowedHeaders": ["*"] - "AllowedOrigins": [ - "http://localhost:3000", - "https://{VERCEL_PROJECT_NAME}*.vercel.app", - "{PRODUCTION_DOMAIN}" - ], + "AllowedHeaders": ["*"], "AllowedMethods": [ "GET", "PUT" ], + "AllowedOrigins": [ + "http://localhost:3000", + "https://{VERCEL_PROJECT_NAME}*.vercel.app", + "{PRODUCTION_DOMAIN}" + ] }] ``` + - Enable public hosting by doing one of the following: + - Select "Connect Custom Domain" and choose a Cloudflare domain + - Select "Allow Access" from R2.dev subdomain - Enable R2.dev subdomain (necessary in order to serve files publicly without a custom domain) - - Store configuration: + - Store public configuration: - `NEXT_PUBLIC_CLOUDFLARE_R2_BUCKET`: bucket name - `NEXT_PUBLIC_CLOUDFLARE_R2_ACCOUNT_ID`: account id (found on R2 overview page) - - `NEXT_PUBLIC_CLOUDFLARE_R2_DEV_SUBDOMAIN`: r2.dev subdomain, e.g., "pub-jf90908..." -2. Setup credentials + - `NEXT_PUBLIC_CLOUDFLARE_R2_PUBLIC_DOMAIN`: e.g., either "pub-jf90908...r2.dev" or "custom-domain.com" +2. Setup private credentials - Create API token by selecting "Manage R2 API Tokens," and clicking "Create API Token" - Select "Object Read & Write," choose "Apply to specific buckets only," and select the bucket created in Step 1. - Store credentials (⚠️ _Ensure access keys are not prefixed with `NEXT_PUBLIC`_): @@ -127,10 +130,10 @@ Only one storage adapter—Vercel Blob, Cloudflare R2, or AWS S3—can be used a "ExposeHeaders": [] }] ``` - - Store configuration + - Store public configuration - `NEXT_PUBLIC_AWS_S3_BUCKET`: bucket name - `NEXT_PUBLIC_AWS_S3_REGION`: bucket region, e.g., "us-east-1" -2. Setup credentials +2. Setup private credentials - [Create IAM policy](https://console.aws.amazon.com/iam/home#/policies) using JSON editor: ```json { diff --git a/next.config.js b/next.config.js index bd880f53..622bcafa 100644 --- a/next.config.js +++ b/next.config.js @@ -7,9 +7,7 @@ const VERCEL_BLOB_HOSTNAME = VERCEL_BLOB_STORE_ID : undefined; const CLOUDFLARE_R2_HOSTNAME = - process.env.NEXT_PUBLIC_CLOUDFLARE_R2_DEV_SUBDOMAIN - ? `${process.env.NEXT_PUBLIC_CLOUDFLARE_R2_DEV_SUBDOMAIN}.r2.dev` - : undefined; + process.env.NEXT_PUBLIC_CLOUDFLARE_R2_PUBLIC_DOMAIN; const AWS_S3_HOSTNAME = process.env.NEXT_PUBLIC_AWS_S3_BUCKET && diff --git a/src/services/storage/cloudflare-r2.ts b/src/services/storage/cloudflare-r2.ts index 7866005a..a38bf87e 100644 --- a/src/services/storage/cloudflare-r2.ts +++ b/src/services/storage/cloudflare-r2.ts @@ -11,8 +11,8 @@ const CLOUDFLARE_R2_BUCKET = process.env.NEXT_PUBLIC_CLOUDFLARE_R2_BUCKET ?? ''; const CLOUDFLARE_R2_ACCOUNT_ID = process.env.NEXT_PUBLIC_CLOUDFLARE_R2_ACCOUNT_ID ?? ''; -const CLOUDFLARE_R2_DEV_SUBDOMAIN = - process.env.NEXT_PUBLIC_CLOUDFLARE_R2_DEV_SUBDOMAIN ?? ''; +const CLOUDFLARE_R2_PUBLIC_DOMAIN = + process.env.NEXT_PUBLIC_CLOUDFLARE_R2_PUBLIC_DOMAIN ?? ''; const CLOUDFLARE_R2_ACCESS_KEY = process.env.CLOUDFLARE_R2_ACCESS_KEY ?? ''; const CLOUDFLARE_R2_SECRET_ACCESS_KEY = @@ -20,12 +20,12 @@ const CLOUDFLARE_R2_SECRET_ACCESS_KEY = const CLOUDFLARE_R2_ENDPOINT = `https://${CLOUDFLARE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com`; +export const CLOUDFLARE_R2_BASE_URL_PUBLIC = + `https://${CLOUDFLARE_R2_PUBLIC_DOMAIN}`; + export const CLOUDFLARE_R2_BASE_URL_PRIVATE = `${CLOUDFLARE_R2_ENDPOINT}/${CLOUDFLARE_R2_BUCKET}`; -export const CLOUDFLARE_R2_BASE_URL_PUBLIC = - `https://${CLOUDFLARE_R2_DEV_SUBDOMAIN}.r2.dev`; - export const cloudflareR2Client = () => new S3Client({ region: 'auto', endpoint: CLOUDFLARE_R2_ENDPOINT, diff --git a/src/site/config.ts b/src/site/config.ts index d6db47dd..ced3bf09 100644 --- a/src/site/config.ts +++ b/src/site/config.ts @@ -40,7 +40,7 @@ export const HAS_VERCEL_BLOB_STORAGE = export const HAS_CLOUDFLARE_R2_STORAGE_CLIENT = (process.env.NEXT_PUBLIC_CLOUDFLARE_R2_BUCKET ?? '').length > 0 && (process.env.NEXT_PUBLIC_CLOUDFLARE_R2_ACCOUNT_ID ?? '').length > 0 && - (process.env.NEXT_PUBLIC_CLOUDFLARE_R2_DEV_SUBDOMAIN ?? '').length > 0; + (process.env.NEXT_PUBLIC_CLOUDFLARE_R2_PUBLIC_DOMAIN ?? '').length > 0; export const HAS_CLOUDFLARE_R2_STORAGE = HAS_CLOUDFLARE_R2_STORAGE_CLIENT && (process.env.CLOUDFLARE_R2_ACCESS_KEY ?? '').length > 0 && From e4be4ad980fd6e4b275619c213b8a894890e93b0 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 18:28:51 -0600 Subject: [PATCH 5/8] Tweak public domain instructions for Cloudflare R2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 848e9e11..939fcc06 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,8 @@ Only one storage adapter—Vercel Blob, Cloudflare R2, or AWS S3—can be used a ``` - Enable public hosting by doing one of the following: - Select "Connect Custom Domain" and choose a Cloudflare domain + - OR - Select "Allow Access" from R2.dev subdomain - - Enable R2.dev subdomain (necessary in order to serve files publicly without a custom domain) - Store public configuration: - `NEXT_PUBLIC_CLOUDFLARE_R2_BUCKET`: bucket name - `NEXT_PUBLIC_CLOUDFLARE_R2_ACCOUNT_ID`: account id (found on R2 overview page) From 1801660e64f4aada911d6ddc9358128fbdfa92bc Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 19:23:58 -0600 Subject: [PATCH 6/8] Refine storage check logic --- src/services/storage/aws-s3.ts | 1 + src/services/storage/cloudflare-r2.ts | 7 +++++-- src/services/storage/index.ts | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/services/storage/aws-s3.ts b/src/services/storage/aws-s3.ts index 2ec518d5..8a658d37 100644 --- a/src/services/storage/aws-s3.ts +++ b/src/services/storage/aws-s3.ts @@ -25,6 +25,7 @@ export const awsS3Client = () => new S3Client({ const urlForKey = (key?: string) => `${AWS_S3_BASE_URL}/${key}`; export const isUrlFromAwsS3 = (url: string) => + Boolean(AWS_S3_BASE_URL) && url.startsWith(AWS_S3_BASE_URL); export const awsS3PutObjectCommandForKey = (Key: string) => diff --git a/src/services/storage/cloudflare-r2.ts b/src/services/storage/cloudflare-r2.ts index a38bf87e..f80265d7 100644 --- a/src/services/storage/cloudflare-r2.ts +++ b/src/services/storage/cloudflare-r2.ts @@ -40,8 +40,11 @@ const urlForKey = (key?: string, isPublic = true) => isPublic : `${CLOUDFLARE_R2_BASE_URL_PRIVATE}/${key}`; export const isUrlFromCloudflareR2 = (url: string) => - url.startsWith(CLOUDFLARE_R2_BASE_URL_PRIVATE) || - url.startsWith(CLOUDFLARE_R2_BASE_URL_PUBLIC); + Boolean(CLOUDFLARE_R2_BASE_URL_PRIVATE) && + Boolean(CLOUDFLARE_R2_BASE_URL_PUBLIC) && ( + url.startsWith(CLOUDFLARE_R2_BASE_URL_PRIVATE) || + url.startsWith(CLOUDFLARE_R2_BASE_URL_PUBLIC) + ); export const cloudflareR2PutObjectCommandForKey = (Key: string) => new PutObjectCommand({ Bucket: CLOUDFLARE_R2_BUCKET, Key }); diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index 2b58ebe3..9e7a1e1f 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -130,7 +130,7 @@ export const convertUploadToPhoto = async ( ): Promise => { const fileName = photoId ? `${PREFIX_PHOTO}-${photoId}` : `${PREFIX_PHOTO}`; const fileExtension = getExtensionFromStorageUrl(uploadUrl); - const photoUrl = `${fileName}.${fileExtension ?? 'jpg'}`; + const photoPath = `${fileName}.${fileExtension ?? 'jpg'}`; const storageType = storageTypeFromUrl(uploadUrl); @@ -139,17 +139,17 @@ export const convertUploadToPhoto = async ( // Copy file switch (storageType) { case 'vercel-blob': - url = await vercelBlobCopy(uploadUrl, photoUrl, photoId === undefined); + url = await vercelBlobCopy(uploadUrl, photoPath, photoId === undefined); break; case 'cloudflare-r2': url = await cloudflareR2Copy( getFileNameFromStorageUrl(uploadUrl), - photoUrl, + photoPath, photoId === undefined, ); break; case 'aws-s3': - url = await awsS3Copy(uploadUrl, photoUrl, photoId === undefined); + url = await awsS3Copy(uploadUrl, photoPath, photoId === undefined); break; } From 9867bfa4a07e916b47c4d0d5e5eb3ff43ce5a10a Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 20:56:13 -0600 Subject: [PATCH 7/8] Refine storage check logic further --- src/services/storage/aws-s3.ts | 9 +++++---- src/services/storage/cloudflare-r2.ts | 28 +++++++++++++++------------ src/services/storage/vercel-blob.ts | 6 ++++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/services/storage/aws-s3.ts b/src/services/storage/aws-s3.ts index 8a658d37..07bc3dc9 100644 --- a/src/services/storage/aws-s3.ts +++ b/src/services/storage/aws-s3.ts @@ -11,8 +11,10 @@ const AWS_S3_BUCKET = process.env.NEXT_PUBLIC_AWS_S3_BUCKET ?? ''; const AWS_S3_REGION = process.env.NEXT_PUBLIC_AWS_S3_REGION ?? ''; const AWS_S3_ACCESS_KEY = process.env.AWS_S3_ACCESS_KEY ?? ''; const AWS_S3_SECRET_ACCESS_KEY = process.env.AWS_S3_SECRET_ACCESS_KEY ?? ''; -export const AWS_S3_BASE_URL = - `https://${AWS_S3_BUCKET}.s3.${AWS_S3_REGION}.amazonaws.com`; + +export const AWS_S3_BASE_URL = AWS_S3_BUCKET && AWS_S3_REGION + ? `https://${AWS_S3_BUCKET}.s3.${AWS_S3_REGION}.amazonaws.com` + : undefined; export const awsS3Client = () => new S3Client({ region: AWS_S3_REGION, @@ -25,8 +27,7 @@ export const awsS3Client = () => new S3Client({ const urlForKey = (key?: string) => `${AWS_S3_BASE_URL}/${key}`; export const isUrlFromAwsS3 = (url: string) => - Boolean(AWS_S3_BASE_URL) && - url.startsWith(AWS_S3_BASE_URL); + AWS_S3_BASE_URL && url.startsWith(AWS_S3_BASE_URL); export const awsS3PutObjectCommandForKey = (Key: string) => new PutObjectCommand({ Bucket: AWS_S3_BUCKET, Key, ACL: 'public-read' }); diff --git a/src/services/storage/cloudflare-r2.ts b/src/services/storage/cloudflare-r2.ts index f80265d7..cf4ac5d2 100644 --- a/src/services/storage/cloudflare-r2.ts +++ b/src/services/storage/cloudflare-r2.ts @@ -17,14 +17,17 @@ const CLOUDFLARE_R2_ACCESS_KEY = process.env.CLOUDFLARE_R2_ACCESS_KEY ?? ''; const CLOUDFLARE_R2_SECRET_ACCESS_KEY = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY ?? ''; -const CLOUDFLARE_R2_ENDPOINT = - `https://${CLOUDFLARE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com`; - -export const CLOUDFLARE_R2_BASE_URL_PUBLIC = - `https://${CLOUDFLARE_R2_PUBLIC_DOMAIN}`; +const CLOUDFLARE_R2_ENDPOINT = CLOUDFLARE_R2_ACCOUNT_ID + ? `https://${CLOUDFLARE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com` + : undefined; +export const CLOUDFLARE_R2_BASE_URL_PUBLIC = CLOUDFLARE_R2_PUBLIC_DOMAIN + ? `https://${CLOUDFLARE_R2_PUBLIC_DOMAIN}` + : undefined; export const CLOUDFLARE_R2_BASE_URL_PRIVATE = - `${CLOUDFLARE_R2_ENDPOINT}/${CLOUDFLARE_R2_BUCKET}`; + CLOUDFLARE_R2_ENDPOINT && CLOUDFLARE_R2_BUCKET + ? `${CLOUDFLARE_R2_ENDPOINT}/${CLOUDFLARE_R2_BUCKET}` + : undefined; export const cloudflareR2Client = () => new S3Client({ region: 'auto', @@ -39,12 +42,13 @@ const urlForKey = (key?: string, isPublic = true) => isPublic ? `${CLOUDFLARE_R2_BASE_URL_PUBLIC}/${key}` : `${CLOUDFLARE_R2_BASE_URL_PRIVATE}/${key}`; -export const isUrlFromCloudflareR2 = (url: string) => - Boolean(CLOUDFLARE_R2_BASE_URL_PRIVATE) && - Boolean(CLOUDFLARE_R2_BASE_URL_PUBLIC) && ( - url.startsWith(CLOUDFLARE_R2_BASE_URL_PRIVATE) || - url.startsWith(CLOUDFLARE_R2_BASE_URL_PUBLIC) - ); +export const isUrlFromCloudflareR2 = (url: string) => ( + CLOUDFLARE_R2_BASE_URL_PRIVATE && + url.startsWith(CLOUDFLARE_R2_BASE_URL_PRIVATE) +) || ( + CLOUDFLARE_R2_BASE_URL_PUBLIC && + url.startsWith(CLOUDFLARE_R2_BASE_URL_PUBLIC) +); export const cloudflareR2PutObjectCommandForKey = (Key: string) => new PutObjectCommand({ Bucket: CLOUDFLARE_R2_BUCKET, Key }); diff --git a/src/services/storage/vercel-blob.ts b/src/services/storage/vercel-blob.ts index a8b5094f..fb6f013c 100644 --- a/src/services/storage/vercel-blob.ts +++ b/src/services/storage/vercel-blob.ts @@ -6,10 +6,12 @@ const VERCEL_BLOB_STORE_ID = process.env.BLOB_READ_WRITE_TOKEN?.match( /^vercel_blob_rw_([a-z0-9]+)_[a-z0-9]+$/i, )?.[1].toLowerCase(); -export const VERCEL_BLOB_BASE_URL = - `https://${VERCEL_BLOB_STORE_ID}.public.blob.vercel-storage.com`; +export const VERCEL_BLOB_BASE_URL = VERCEL_BLOB_STORE_ID + ? `https://${VERCEL_BLOB_STORE_ID}.public.blob.vercel-storage.com` + : undefined; export const isUrlFromVercelBlob = (url: string) => + VERCEL_BLOB_BASE_URL && url.startsWith(VERCEL_BLOB_BASE_URL); export const vercelBlobUploadFromClient = async ( From 30393b625beca271aa70528cca21961d7dacbc75 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Jan 2024 21:11:00 -0600 Subject: [PATCH 8/8] Improve handling of long site titles in nav --- src/site/NavClient.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/NavClient.tsx b/src/site/NavClient.tsx index 066ba107..e15e3590 100644 --- a/src/site/NavClient.tsx +++ b/src/site/NavClient.tsx @@ -68,7 +68,7 @@ export default function NavClient({ showAdmin={showAdmin} />
    -
    +
    {renderLink(SITE_DOMAIN_OR_TITLE, PATH_ROOT)}
    ]