import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; // S3 Configuration export const S3_CONFIG = { endpoint: 'https://dome-api.slm-lab.net', region: 'us-east-1', bucket: process.env.S3_BUCKET || 'pages', accessKey: '4aBT4CMb7JIMMyUtp4Pl', secretKey: 'HGn39XhCIlqOjmDVzRK9MED2Fci2rYvDDgbLFElg' }; // Initialize S3 client for Minio export const s3Client = new S3Client({ region: S3_CONFIG.region, endpoint: S3_CONFIG.endpoint, credentials: { accessKeyId: S3_CONFIG.accessKey, secretAccessKey: S3_CONFIG.secretKey }, forcePathStyle: true // Required for MinIO }); /** * Upload a file to S3 */ export async function putObject( key: string, content: string | Buffer, contentType?: string ): Promise<{ key: string; url?: string }> { const command = new PutObjectCommand({ Bucket: S3_CONFIG.bucket, Key: key, Body: typeof content === 'string' ? Buffer.from(content, 'utf-8') : content, ContentType: contentType || 'text/plain', }); await s3Client.send(command); return { key }; } /** * Get object content from S3 */ export async function getObjectContent(key: string): Promise { try { const command = new GetObjectCommand({ Bucket: S3_CONFIG.bucket, Key: key, }); const response = await s3Client.send(command); if (!response.Body) { return null; } const chunks: Uint8Array[] = []; for await (const chunk of response.Body as any) { chunks.push(chunk); } const buffer = Buffer.concat(chunks); return buffer.toString('utf-8'); } catch (error) { console.error('Error getting object content:', error); return null; } } /** * Delete an object from S3 */ export async function deleteObject(key: string): Promise { const command = new DeleteObjectCommand({ Bucket: S3_CONFIG.bucket, Key: key, }); await s3Client.send(command); } /** * List objects for a user in a specific folder */ export async function listUserObjects(userId: string, folder: string): Promise> { const prefix = `user-${userId}/${folder}/`; const command = new ListObjectsV2Command({ Bucket: S3_CONFIG.bucket, Prefix: prefix, Delimiter: '/' }); const response = await s3Client.send(command); const objects = response.Contents || []; return objects .filter(obj => obj.Key && !obj.Key.endsWith('/') && !obj.Key.includes('.placeholder')) .map(obj => ({ key: obj.Key!, name: obj.Key!.split('/').pop() || obj.Key!, size: obj.Size, lastModified: obj.LastModified })); } /** * Get public URL for an object * Simple URL construction for MinIO/S3 objects */ export function getPublicUrl(filePath: string, bucket?: string): string { if (!filePath) return ''; if (filePath.startsWith('http')) return filePath; // Already a full URL // Remove leading slash if present const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath; // Construct the full URL const endpoint = S3_CONFIG.endpoint?.replace(/\/$/, ''); // Remove trailing slash if present const bucketName = bucket || S3_CONFIG.bucket; // Return original path if no endpoint is configured if (!endpoint) return cleanPath; // Construct and return the full URL return `${endpoint}/${bucketName}/${cleanPath}`; } /** * Create standard folder structure for a user */ export async function createUserFolderStructure(userId: string): Promise { const folders = ['notes', 'diary', 'health', 'contacts']; for (const folder of folders) { try { // Create folder path (prefix in S3) const folderKey = `user-${userId}/${folder}/`; await putObject(folderKey, '', 'application/x-directory'); // Create placeholder file to ensure folder is visible const placeholderKey = `user-${userId}/${folder}/.placeholder`; await putObject(placeholderKey, 'Folder placeholder', 'text/plain'); console.log(`Created folder: ${folderKey}`); } catch (error) { console.error(`Error creating folder ${folder} for user ${userId}:`, error); // Continue with other folders even if one fails } } } export async function uploadMissionFile({ missionId, file, type, // 'logo' or 'attachment' }: { missionId: string; file: File; type: 'logo' | 'attachment'; }): Promise<{ success: boolean; data?: any; error?: string }> { const formData = new FormData(); formData.append('missionId', missionId); formData.append('type', type); formData.append('file', file); const res = await fetch('/api/missions/upload', { method: 'POST', body: formData, }); if (!res.ok) { const err = await res.json().catch(() => ({})); return { success: false, error: err.error || 'Upload failed' }; } const data = await res.json(); return { success: true, data }; }