diff --git a/src/services/blob/aws-s3.ts b/src/services/blob/aws-s3.ts index 715a6003..2971dada 100644 --- a/src/services/blob/aws-s3.ts +++ b/src/services/blob/aws-s3.ts @@ -18,16 +18,10 @@ const S3_ADMIN_ACCESS_KEY = const S3_ADMIN_SECRET_ACCESS_KEY = process.env.S3_ADMIN_SECRET_ACCESS_KEY; -export const HAS_AWS_S3_STORAGE = - S3_BUCKET.length > 0 && - S3_REGION.length > 0 && - S3_UPLOAD_ACCESS_KEY.length > 0 && - S3_UPLOAD_SECRET_ACCESS_KEY.length > 0; - const client = () => new S3Client({ region: S3_REGION, credentials: { - // Fall back on upload credentials if admin credentials are not available + // Fall back on upload credentials when admin credentials aren't available accessKeyId: S3_ADMIN_ACCESS_KEY ?? S3_UPLOAD_ACCESS_KEY, secretAccessKey: S3_ADMIN_SECRET_ACCESS_KEY ?? S3_UPLOAD_SECRET_ACCESS_KEY, }, diff --git a/src/services/blob/index.ts b/src/services/blob/index.ts index f6849731..42e4f8d5 100644 --- a/src/services/blob/index.ts +++ b/src/services/blob/index.ts @@ -7,13 +7,13 @@ import { } from './vercel-blob'; import { AWS_S3_BASE_URL, - HAS_AWS_S3_STORAGE, awsS3Copy, awsS3Delete, awsS3List, awsS3UploadFromClient, isUrlFromAwsS3, } from './aws-s3'; +import { HAS_AWS_S3_STORAGE_CLIENT, HAS_AWS_S3_STORAGE } from '@/site/config'; const PREFIX_UPLOAD = 'upload'; const PREFIX_PHOTO = 'photo'; @@ -47,7 +47,7 @@ const getFileNameFromBlobUrl = (url: string) => export const uploadPhotoFromClient = async ( file: File | Blob, extension = 'jpg', -) => HAS_AWS_S3_STORAGE +) => HAS_AWS_S3_STORAGE_CLIENT ? awsS3UploadFromClient(file, PREFIX_UPLOAD, extension, true) : vercelBlobUploadFromClient(file, `${PREFIX_UPLOAD}.${extension}`); @@ -59,12 +59,14 @@ export const convertUploadToPhoto = async ( const fileExtension = getExtensionFromBlobUrl(uploadUrl); const photoUrl = `${fileName}.${fileExtension ?? 'jpg'}`; - const url = await (HAS_AWS_S3_STORAGE + const useAwsS3 = HAS_AWS_S3_STORAGE && isUrlFromAwsS3(uploadUrl); + + const url = await (useAwsS3 ? awsS3Copy(uploadUrl, photoUrl, photoId === undefined) : vercelBlobCopy(uploadUrl, photoUrl, photoId === undefined)); if (url) { - await (HAS_AWS_S3_STORAGE + await (useAwsS3 ? awsS3Delete(getFileNameFromBlobUrl(uploadUrl)) : vercelBlobDelete(uploadUrl)); } diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 0832e998..52520acf 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useTransition } from 'react'; +import { ComponentProps, ReactNode, useTransition } from 'react'; import { useRouter } from 'next/navigation'; import { cc } from '@/utility/css'; import ChecklistRow from '../components/ChecklistRow'; @@ -18,10 +18,13 @@ import InfoBlock from '@/components/InfoBlock'; import Checklist from '@/components/Checklist'; import { toastSuccess } from '@/toast'; import { ConfigChecklistStatus } from './config'; +import StatusIcon from '@/components/StatusIcon'; export default function SiteChecklistClient({ hasPostgres, hasBlob, + hasVercelBlob, + hasAwsS3Storage, hasAuth, hasAdminUser, hasTitle, @@ -102,6 +105,17 @@ export default function SiteChecklistClient({ {variables.map(renderEnvVar)} ; + const renderSubStatus = ( + type: ComponentProps['type'], + label: ReactNode, + ) => +
+ + + {label} + +
; + return (
-
    -
  1. + {renderSubStatus( + hasVercelBlob ? 'checked' : 'optional', + <> Vercel Blob: {' '} {renderLink( @@ -135,16 +150,19 @@ export default function SiteChecklistClient({ )} {' '} and connect to project -
  2. -
  3. + , + )} + {renderSubStatus( + hasAwsS3Storage ? 'checked' : 'optional', + <> AWS S3: {' '} {renderLink( 'https://github.com/sambecker/exif-photo-blog#aws-s3', 'create/configure bucket', )} -
  4. -
+ + )}
0; -const hasVercelBlob = (process.env.BLOB_READ_WRITE_TOKEN ?? '').length > 0; -const hasAwsS3Storage = +// STORAGE: AWS S3 +// Includes separate check for client-side usage, i.e., uploading, +export const HAS_AWS_S3_STORAGE_CLIENT = (process.env.NEXT_PUBLIC_S3_BUCKET ?? '').length > 0 && (process.env.NEXT_PUBLIC_S3_REGION ?? '').length > 0 && (process.env.NEXT_PUBLIC_S3_UPLOAD_ACCESS_KEY ?? '').length > 0 && - (process.env.NEXT_PUBLIC_S3_UPLOAD_SECRET_ACCESS_KEY ?? '').length > 0 && + (process.env.NEXT_PUBLIC_S3_UPLOAD_SECRET_ACCESS_KEY ?? '').length > 0; +export const HAS_AWS_S3_STORAGE = + HAS_AWS_S3_STORAGE_CLIENT && (process.env.S3_ADMIN_ACCESS_KEY ?? '').length > 0 && (process.env.S3_ADMIN_SECRET_ACCESS_KEY ?? '').length > 0; @@ -54,7 +59,9 @@ export const OG_TEXT_BOTTOM_ALIGNMENT = export const CONFIG_CHECKLIST_STATUS = { hasPostgres: (process.env.POSTGRES_HOST ?? '').length > 0, - hasBlob: hasVercelBlob || hasAwsS3Storage, + hasBlob: HAS_VERCEL_BLOB || HAS_AWS_S3_STORAGE, + hasVercelBlob: HAS_VERCEL_BLOB, + hasAwsS3Storage: HAS_AWS_S3_STORAGE, hasAuth: (process.env.AUTH_SECRET ?? '').length > 0, hasAdminUser: ( (process.env.ADMIN_EMAIL ?? '').length > 0 &&