NeahNew/lib/s3.ts
2026-01-02 14:32:36 +01:00

179 lines
5.0 KiB
TypeScript

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<string | null> {
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<void> {
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<Array<{ key: string; name: string; size?: number; lastModified?: Date }>> {
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<void> {
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 };
}