187 lines
5.8 KiB
TypeScript
187 lines
5.8 KiB
TypeScript
import { Client } from 'minio'
|
|
import { env } from './env'
|
|
|
|
// Get MinIO credentials with dev-portfolio naming first, fallback to legacy names
|
|
const minioAccessKey = env.MINIO_KEY || env.MINIO_ACCESS_KEY
|
|
const minioSecretKey = env.MINIO_SECRET || env.MINIO_SECRET_KEY
|
|
const minioBucket = env.MINIO_IMAGE_BUCKET || env.MINIO_BUCKET
|
|
|
|
// Only initialize MinIO client if ALL required configuration is available
|
|
export const minioClient = (env.MINIO_ENDPOINT && minioAccessKey && minioSecretKey)
|
|
? new Client({
|
|
endPoint: env.MINIO_ENDPOINT,
|
|
port: env.MINIO_PORT || 9000,
|
|
useSSL: env.NODE_ENV === 'production',
|
|
accessKey: minioAccessKey,
|
|
secretKey: minioSecretKey,
|
|
})
|
|
: null
|
|
|
|
// Log MinIO initialization status
|
|
if (!minioClient) {
|
|
console.warn('⚠️ MinIO client not initialized. Missing configuration:', {
|
|
hasEndpoint: !!env.MINIO_ENDPOINT,
|
|
hasKey: !!minioAccessKey,
|
|
hasSecret: !!minioSecretKey,
|
|
checkedVars: ['MINIO_KEY/MINIO_ACCESS_KEY', 'MINIO_SECRET/MINIO_SECRET_KEY']
|
|
})
|
|
} else {
|
|
console.log('✅ MinIO client initialized successfully with endpoint:', env.MINIO_ENDPOINT)
|
|
}
|
|
|
|
export const BUCKET_NAME = minioBucket || 'nextjs-boilerplate'
|
|
|
|
// Initialize bucket if it doesn't exist
|
|
export async function initializeBucket() {
|
|
if (!minioClient) {
|
|
console.error('❌ MinIO client not configured for bucket initialization')
|
|
throw new Error(
|
|
'MinIO client not configured. Please set MINIO_ENDPOINT, MINIO_ACCESS_KEY, and MINIO_SECRET_KEY environment variables.'
|
|
)
|
|
}
|
|
|
|
try {
|
|
console.log('🔍 Checking if bucket exists:', BUCKET_NAME)
|
|
const bucketExists = await minioClient.bucketExists(BUCKET_NAME)
|
|
|
|
if (!bucketExists) {
|
|
console.log('🚀 Creating bucket:', BUCKET_NAME)
|
|
await minioClient.makeBucket(BUCKET_NAME, 'us-east-1')
|
|
console.log('✅ Bucket created successfully:', BUCKET_NAME)
|
|
} else {
|
|
console.log('✅ Bucket already exists:', BUCKET_NAME)
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Error initializing bucket:', {
|
|
error: error.message,
|
|
code: error.code,
|
|
statusCode: error.statusCode,
|
|
bucketName: BUCKET_NAME,
|
|
endpoint: env.MINIO_ENDPOINT,
|
|
port: env.MINIO_PORT
|
|
})
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Generate unique filename with timestamp and random string
|
|
export function generateUniqueFilename(originalName: string): string {
|
|
const timestamp = Date.now()
|
|
const randomString = Math.random().toString(36).substring(2)
|
|
const extension = originalName.split('.').pop()
|
|
return `${timestamp}-${randomString}.${extension}`
|
|
}
|
|
|
|
// Upload file to MinIO
|
|
export async function uploadFile(
|
|
buffer: Buffer,
|
|
filename: string,
|
|
contentType: string
|
|
): Promise<string> {
|
|
console.log('📤 Starting file upload:', { filename, contentType, bufferSize: buffer.length })
|
|
|
|
if (!minioClient) {
|
|
console.error('❌ MinIO client not configured')
|
|
throw new Error(
|
|
'MinIO client not configured. Please set MINIO_ENDPOINT, MINIO_ACCESS_KEY, and MINIO_SECRET_KEY environment variables.'
|
|
)
|
|
}
|
|
|
|
try {
|
|
console.log('🔧 Initializing bucket:', BUCKET_NAME)
|
|
await initializeBucket()
|
|
|
|
const uniqueFilename = generateUniqueFilename(filename)
|
|
const tempPath = `temp/${uniqueFilename}`
|
|
|
|
console.log('📤 Uploading to MinIO:', {
|
|
bucket: BUCKET_NAME,
|
|
path: tempPath,
|
|
size: buffer.length,
|
|
endpoint: env.MINIO_ENDPOINT,
|
|
port: env.MINIO_PORT,
|
|
useSSL: env.NODE_ENV === 'production'
|
|
})
|
|
|
|
await minioClient.putObject(BUCKET_NAME, tempPath, buffer, buffer.length, {
|
|
'Content-Type': contentType,
|
|
})
|
|
|
|
console.log('✅ File uploaded successfully:', tempPath)
|
|
return tempPath
|
|
} catch (error) {
|
|
console.error('❌ Error uploading file:', {
|
|
error: error.message,
|
|
code: error.code,
|
|
statusCode: error.statusCode,
|
|
resource: error.resource,
|
|
region: error.region,
|
|
bucketName: error.bucketName,
|
|
objectName: error.objectName,
|
|
})
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Move file from temp to permanent storage
|
|
export async function moveToPermStorage(tempPath: string, permanentPath: string): Promise<void> {
|
|
if (!minioClient) {
|
|
throw new Error(
|
|
'MinIO client not configured. Please set MINIO_ENDPOINT, MINIO_ACCESS_KEY, and MINIO_SECRET_KEY environment variables.'
|
|
)
|
|
}
|
|
|
|
try {
|
|
// Copy from temp to permanent location
|
|
await minioClient.copyObject(BUCKET_NAME, permanentPath, `${BUCKET_NAME}/${tempPath}`)
|
|
|
|
// Remove from temp location
|
|
await minioClient.removeObject(BUCKET_NAME, tempPath)
|
|
} catch (error) {
|
|
console.error('Error moving file to permanent storage:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Delete file from storage
|
|
export async function deleteFile(filePath: string): Promise<void> {
|
|
if (!minioClient) {
|
|
throw new Error(
|
|
'MinIO client not configured. Please set MINIO_ENDPOINT, MINIO_ACCESS_KEY, and MINIO_SECRET_KEY environment variables.'
|
|
)
|
|
}
|
|
|
|
try {
|
|
await minioClient.removeObject(BUCKET_NAME, filePath)
|
|
} catch (error) {
|
|
console.error('Error deleting file:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Get file URL (for serving files)
|
|
export async function getFileUrl(filePath: string): Promise<string> {
|
|
if (!minioClient) {
|
|
throw new Error(
|
|
'MinIO client not configured. Please set MINIO_ENDPOINT, MINIO_ACCESS_KEY, and MINIO_SECRET_KEY environment variables.'
|
|
)
|
|
}
|
|
|
|
try {
|
|
return await minioClient.presignedGetObject(BUCKET_NAME, filePath, 7 * 24 * 60 * 60) // 7 days
|
|
} catch (error) {
|
|
console.error('Error getting file URL:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Validate file type
|
|
export function validateFileType(mimetype: string, allowedTypes: string[]): boolean {
|
|
return allowedTypes.includes(mimetype)
|
|
}
|
|
|
|
// Validate file size (in bytes)
|
|
export function validateFileSize(size: number, maxSize: number): boolean {
|
|
return size <= maxSize
|
|
}
|