Make storage reporting more granular
This commit is contained in:
parent
5cdb726566
commit
2b9c0cbbe2
@ -18,16 +18,10 @@ const S3_ADMIN_ACCESS_KEY =
|
|||||||
const S3_ADMIN_SECRET_ACCESS_KEY =
|
const S3_ADMIN_SECRET_ACCESS_KEY =
|
||||||
process.env.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({
|
const client = () => new S3Client({
|
||||||
region: S3_REGION,
|
region: S3_REGION,
|
||||||
credentials: {
|
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,
|
accessKeyId: S3_ADMIN_ACCESS_KEY ?? S3_UPLOAD_ACCESS_KEY,
|
||||||
secretAccessKey: S3_ADMIN_SECRET_ACCESS_KEY ?? S3_UPLOAD_SECRET_ACCESS_KEY,
|
secretAccessKey: S3_ADMIN_SECRET_ACCESS_KEY ?? S3_UPLOAD_SECRET_ACCESS_KEY,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import {
|
|||||||
} from './vercel-blob';
|
} from './vercel-blob';
|
||||||
import {
|
import {
|
||||||
AWS_S3_BASE_URL,
|
AWS_S3_BASE_URL,
|
||||||
HAS_AWS_S3_STORAGE,
|
|
||||||
awsS3Copy,
|
awsS3Copy,
|
||||||
awsS3Delete,
|
awsS3Delete,
|
||||||
awsS3List,
|
awsS3List,
|
||||||
awsS3UploadFromClient,
|
awsS3UploadFromClient,
|
||||||
isUrlFromAwsS3,
|
isUrlFromAwsS3,
|
||||||
} from './aws-s3';
|
} from './aws-s3';
|
||||||
|
import { HAS_AWS_S3_STORAGE_CLIENT, HAS_AWS_S3_STORAGE } from '@/site/config';
|
||||||
|
|
||||||
const PREFIX_UPLOAD = 'upload';
|
const PREFIX_UPLOAD = 'upload';
|
||||||
const PREFIX_PHOTO = 'photo';
|
const PREFIX_PHOTO = 'photo';
|
||||||
@ -47,7 +47,7 @@ const getFileNameFromBlobUrl = (url: string) =>
|
|||||||
export const uploadPhotoFromClient = async (
|
export const uploadPhotoFromClient = async (
|
||||||
file: File | Blob,
|
file: File | Blob,
|
||||||
extension = 'jpg',
|
extension = 'jpg',
|
||||||
) => HAS_AWS_S3_STORAGE
|
) => HAS_AWS_S3_STORAGE_CLIENT
|
||||||
? awsS3UploadFromClient(file, PREFIX_UPLOAD, extension, true)
|
? awsS3UploadFromClient(file, PREFIX_UPLOAD, extension, true)
|
||||||
: vercelBlobUploadFromClient(file, `${PREFIX_UPLOAD}.${extension}`);
|
: vercelBlobUploadFromClient(file, `${PREFIX_UPLOAD}.${extension}`);
|
||||||
|
|
||||||
@ -59,12 +59,14 @@ export const convertUploadToPhoto = async (
|
|||||||
const fileExtension = getExtensionFromBlobUrl(uploadUrl);
|
const fileExtension = getExtensionFromBlobUrl(uploadUrl);
|
||||||
const photoUrl = `${fileName}.${fileExtension ?? 'jpg'}`;
|
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)
|
? awsS3Copy(uploadUrl, photoUrl, photoId === undefined)
|
||||||
: vercelBlobCopy(uploadUrl, photoUrl, photoId === undefined));
|
: vercelBlobCopy(uploadUrl, photoUrl, photoId === undefined));
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
await (HAS_AWS_S3_STORAGE
|
await (useAwsS3
|
||||||
? awsS3Delete(getFileNameFromBlobUrl(uploadUrl))
|
? awsS3Delete(getFileNameFromBlobUrl(uploadUrl))
|
||||||
: vercelBlobDelete(uploadUrl));
|
: vercelBlobDelete(uploadUrl));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useTransition } from 'react';
|
import { ComponentProps, ReactNode, useTransition } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { cc } from '@/utility/css';
|
import { cc } from '@/utility/css';
|
||||||
import ChecklistRow from '../components/ChecklistRow';
|
import ChecklistRow from '../components/ChecklistRow';
|
||||||
@ -18,10 +18,13 @@ import InfoBlock from '@/components/InfoBlock';
|
|||||||
import Checklist from '@/components/Checklist';
|
import Checklist from '@/components/Checklist';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { ConfigChecklistStatus } from './config';
|
import { ConfigChecklistStatus } from './config';
|
||||||
|
import StatusIcon from '@/components/StatusIcon';
|
||||||
|
|
||||||
export default function SiteChecklistClient({
|
export default function SiteChecklistClient({
|
||||||
hasPostgres,
|
hasPostgres,
|
||||||
hasBlob,
|
hasBlob,
|
||||||
|
hasVercelBlob,
|
||||||
|
hasAwsS3Storage,
|
||||||
hasAuth,
|
hasAuth,
|
||||||
hasAdminUser,
|
hasAdminUser,
|
||||||
hasTitle,
|
hasTitle,
|
||||||
@ -102,6 +105,17 @@ export default function SiteChecklistClient({
|
|||||||
{variables.map(renderEnvVar)}
|
{variables.map(renderEnvVar)}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
|
const renderSubStatus = (
|
||||||
|
type: ComponentProps<typeof StatusIcon>['type'],
|
||||||
|
label: ReactNode,
|
||||||
|
) =>
|
||||||
|
<div className="flex gap-1.5">
|
||||||
|
<StatusIcon {...{ type }} />
|
||||||
|
<span>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-sm max-w-xl space-y-6 w-full">
|
<div className="text-sm max-w-xl space-y-6 w-full">
|
||||||
<Checklist
|
<Checklist
|
||||||
@ -125,8 +139,9 @@ export default function SiteChecklistClient({
|
|||||||
status={hasBlob}
|
status={hasBlob}
|
||||||
isPending={isPendingPage}
|
isPending={isPendingPage}
|
||||||
>
|
>
|
||||||
<ol className="list-decimal list-inside">
|
{renderSubStatus(
|
||||||
<li>
|
hasVercelBlob ? 'checked' : 'optional',
|
||||||
|
<>
|
||||||
Vercel Blob:
|
Vercel Blob:
|
||||||
{' '}
|
{' '}
|
||||||
{renderLink(
|
{renderLink(
|
||||||
@ -135,16 +150,19 @@ export default function SiteChecklistClient({
|
|||||||
)}
|
)}
|
||||||
{' '}
|
{' '}
|
||||||
and connect to project
|
and connect to project
|
||||||
</li>
|
</>,
|
||||||
<li>
|
)}
|
||||||
|
{renderSubStatus(
|
||||||
|
hasAwsS3Storage ? 'checked' : 'optional',
|
||||||
|
<>
|
||||||
AWS S3:
|
AWS S3:
|
||||||
{' '}
|
{' '}
|
||||||
{renderLink(
|
{renderLink(
|
||||||
'https://github.com/sambecker/exif-photo-blog#aws-s3',
|
'https://github.com/sambecker/exif-photo-blog#aws-s3',
|
||||||
'create/configure bucket',
|
'create/configure bucket',
|
||||||
)}
|
)}
|
||||||
</li>
|
</>
|
||||||
</ol>
|
)}
|
||||||
</ChecklistRow>
|
</ChecklistRow>
|
||||||
</Checklist>
|
</Checklist>
|
||||||
<Checklist
|
<Checklist
|
||||||
|
|||||||
@ -30,14 +30,19 @@ export const BASE_URL = process.env.NODE_ENV === 'production'
|
|||||||
? makeUrlAbsolute(SITE_DOMAIN).toLowerCase()
|
? makeUrlAbsolute(SITE_DOMAIN).toLowerCase()
|
||||||
: 'http://localhost:3000';
|
: 'http://localhost:3000';
|
||||||
|
|
||||||
// STORAGE
|
// STORAGE: VERCEL BLOB
|
||||||
|
export const HAS_VERCEL_BLOB =
|
||||||
|
(process.env.BLOB_READ_WRITE_TOKEN ?? '').length > 0;
|
||||||
|
|
||||||
const hasVercelBlob = (process.env.BLOB_READ_WRITE_TOKEN ?? '').length > 0;
|
// STORAGE: AWS S3
|
||||||
const hasAwsS3Storage =
|
// 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_BUCKET ?? '').length > 0 &&
|
||||||
(process.env.NEXT_PUBLIC_S3_REGION ?? '').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_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_ACCESS_KEY ?? '').length > 0 &&
|
||||||
(process.env.S3_ADMIN_SECRET_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 = {
|
export const CONFIG_CHECKLIST_STATUS = {
|
||||||
hasPostgres: (process.env.POSTGRES_HOST ?? '').length > 0,
|
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,
|
hasAuth: (process.env.AUTH_SECRET ?? '').length > 0,
|
||||||
hasAdminUser: (
|
hasAdminUser: (
|
||||||
(process.env.ADMIN_EMAIL ?? '').length > 0 &&
|
(process.env.ADMIN_EMAIL ?? '').length > 0 &&
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user