Initial push of Plunk Next
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import {
|
||||
S3Client,
|
||||
PutObjectCommand,
|
||||
CreateBucketCommand,
|
||||
HeadBucketCommand,
|
||||
PutBucketPolicyCommand,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import crypto from 'crypto';
|
||||
import {
|
||||
S3_ENDPOINT,
|
||||
S3_ACCESS_KEY_ID,
|
||||
S3_ACCESS_KEY_SECRET,
|
||||
S3_BUCKET,
|
||||
S3_PUBLIC_URL,
|
||||
S3_FORCE_PATH_STYLE,
|
||||
S3_ENABLED,
|
||||
} from '../app/constants.js';
|
||||
|
||||
/**
|
||||
* S3-compatible storage client for Minio
|
||||
*/
|
||||
let s3Client: S3Client | null = null;
|
||||
|
||||
if (S3_ENABLED) {
|
||||
s3Client = new S3Client({
|
||||
region: 'us-east-1', // Minio doesn't use regions, but AWS SDK requires this parameter
|
||||
endpoint: S3_ENDPOINT,
|
||||
credentials: {
|
||||
accessKeyId: S3_ACCESS_KEY_ID,
|
||||
secretAccessKey: S3_ACCESS_KEY_SECRET,
|
||||
},
|
||||
forcePathStyle: S3_FORCE_PATH_STYLE, // Required for Minio (uses path-style URLs)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the S3 bucket if it doesn't exist
|
||||
*/
|
||||
export async function initializeBucket(): Promise<void> {
|
||||
if (!s3Client) {
|
||||
throw new Error('S3 is not enabled');
|
||||
}
|
||||
|
||||
let bucketExists = true;
|
||||
|
||||
try {
|
||||
// Check if bucket exists
|
||||
await s3Client.send(
|
||||
new HeadBucketCommand({
|
||||
Bucket: S3_BUCKET,
|
||||
}),
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const isNotFoundError =
|
||||
error &&
|
||||
typeof error === 'object' &&
|
||||
(('name' in error && error.name === 'NotFound') ||
|
||||
('$metadata' in error &&
|
||||
error.$metadata &&
|
||||
typeof error.$metadata === 'object' &&
|
||||
'httpStatusCode' in error.$metadata &&
|
||||
error.$metadata.httpStatusCode === 404));
|
||||
if (isNotFoundError) {
|
||||
bucketExists = false;
|
||||
// Bucket doesn't exist, create it
|
||||
try {
|
||||
await s3Client.send(
|
||||
new CreateBucketCommand({
|
||||
Bucket: S3_BUCKET,
|
||||
}),
|
||||
);
|
||||
console.log(`[S3] Created bucket: ${S3_BUCKET}`);
|
||||
} catch (createError) {
|
||||
console.error('[S3] Failed to create bucket:', createError);
|
||||
throw createError;
|
||||
}
|
||||
} else {
|
||||
console.error('[S3] Failed to check bucket:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Set public read policy for the bucket (both for new and existing buckets)
|
||||
try {
|
||||
const bucketPolicy = {
|
||||
Version: '2012-10-17',
|
||||
Statement: [
|
||||
{
|
||||
Sid: 'PublicReadGetObject',
|
||||
Effect: 'Allow',
|
||||
Principal: '*',
|
||||
Action: ['s3:GetObject'],
|
||||
Resource: [`arn:aws:s3:::${S3_BUCKET}/*`],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await s3Client.send(
|
||||
new PutBucketPolicyCommand({
|
||||
Bucket: S3_BUCKET,
|
||||
Policy: JSON.stringify(bucketPolicy),
|
||||
}),
|
||||
);
|
||||
|
||||
if (!bucketExists) {
|
||||
console.log(`[S3] Set public read policy for bucket: ${S3_BUCKET}`);
|
||||
}
|
||||
} catch (policyError) {
|
||||
console.error('[S3] Failed to set bucket policy:', policyError);
|
||||
// Don't throw - bucket was created but policy failed
|
||||
}
|
||||
}
|
||||
|
||||
interface UploadFileParams {
|
||||
file: Buffer;
|
||||
filename: string;
|
||||
contentType: string;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
interface UploadFileResult {
|
||||
url: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file to S3/Minio
|
||||
*/
|
||||
export async function uploadFile(params: UploadFileParams): Promise<UploadFileResult> {
|
||||
if (!s3Client) {
|
||||
throw new Error('S3 is not enabled');
|
||||
}
|
||||
|
||||
const {file, filename, contentType, projectId} = params;
|
||||
|
||||
// Generate a unique key for the file
|
||||
const timestamp = Date.now();
|
||||
const randomString = crypto.randomBytes(8).toString('hex');
|
||||
const extension = filename.split('.').pop();
|
||||
const key = `${projectId}/${timestamp}-${randomString}.${extension}`;
|
||||
|
||||
// Upload to S3/Minio
|
||||
await s3Client.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: S3_BUCKET,
|
||||
Key: key,
|
||||
Body: file,
|
||||
ContentType: contentType,
|
||||
}),
|
||||
);
|
||||
|
||||
// Construct public URL
|
||||
const url = `${S3_PUBLIC_URL}/${key}`;
|
||||
|
||||
return {url, key};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if S3 is enabled and configured
|
||||
*/
|
||||
export function isS3Enabled(): boolean {
|
||||
return S3_ENABLED;
|
||||
}
|
||||
Reference in New Issue
Block a user