ai-wpa/lib/minio.ts

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
}