fix: file access

This commit is contained in:
Oleg Proskurin 2025-09-28 23:15:24 +07:00
parent 5c8bc90bcc
commit 0b6bb5662c
4 changed files with 47 additions and 51 deletions

View File

@ -65,6 +65,7 @@ services:
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
MINIO_BROWSER_REDIRECT_URL: http://localhost:9001
MINIO_SERVER_URL: http://localhost:9000
MINIO_DOMAIN: localhost
# CRITICAL: SNMD command for full S3 compatibility
command: server /data{1...4} --console-address ":9001"
healthcheck:

View File

@ -1,5 +1,11 @@
@base = http://localhost:3000
### Health
GET {{base}}/health
### Info
GET {{base}}/api/info
@ -30,15 +36,8 @@ POST {{base}}/api/text-to-image
Content-Type: application/json
{
"prompt": "банановый стимпанк. много стимпанк машин и меаханизмов посвященных бананм и работающих на бананах. банановая феерия",
"filename": "banatie-party",
"autoEnhance": true,
"enhancementOptions": {
"imageStyle": "photorealistic",
"aspectRatio": "landscape",
"mood": "peaceful",
"lighting": "golden hour"
}
"prompt": "A majestic eagle soaring over snow-capped mountains",
"filename": "test-eagle"
}

View File

@ -24,54 +24,37 @@ imagesRouter.get(
const storageService = StorageFactory.getInstance();
try {
// Method 1: Redirect to presigned URL (24 hour expiry)
const presignedUrl = await storageService.getPresignedDownloadUrl(
// Stream the file directly through our API (more reliable than presigned URL redirects)
const fileBuffer = await storageService.downloadFile(
orgId,
projectId,
category as 'uploads' | 'generated' | 'references',
filename,
24 * 60 * 60 // 24 hours
filename
);
// Redirect to the presigned URL
return res.redirect(302, presignedUrl);
// Determine content type from filename
const ext = filename.toLowerCase().split('.').pop();
const contentType = {
'png': 'image/png',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'webp': 'image/webp',
'svg': 'image/svg+xml'
}[ext || ''] || 'application/octet-stream';
res.setHeader('Content-Type', contentType);
res.setHeader('Cache-Control', 'public, max-age=86400'); // 24 hours
res.setHeader('Content-Length', fileBuffer.length);
return res.send(fileBuffer);
} catch (error) {
console.error('Failed to generate presigned URL:', error);
try {
// Method 2: Fallback - Stream the file directly through our API
const fileBuffer = await storageService.downloadFile(
orgId,
projectId,
category as 'uploads' | 'generated' | 'references',
filename
);
// Determine content type from filename
const ext = filename.toLowerCase().split('.').pop();
const contentType = {
'png': 'image/png',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'webp': 'image/webp',
'svg': 'image/svg+xml'
}[ext || ''] || 'application/octet-stream';
res.setHeader('Content-Type', contentType);
res.setHeader('Cache-Control', 'public, max-age=86400'); // 24 hours
res.setHeader('Content-Length', fileBuffer.length);
return res.send(fileBuffer);
} catch (streamError) {
console.error('Failed to stream file:', streamError);
return res.status(404).json({
success: false,
message: 'File not found'
});
}
console.error('Failed to stream file:', error);
return res.status(404).json({
success: false,
message: 'File not found'
});
}
})
);

View File

@ -181,7 +181,20 @@ export class MinioStorageService implements StorageService {
expirySeconds: number = 86400 // 24 hours default
): Promise<string> {
const filePath = this.getFilePath(orgId, projectId, category, filename);
return await this.client.presignedGetObject(this.bucketName, filePath, expirySeconds);
const presignedUrl = await this.client.presignedGetObject(this.bucketName, filePath, expirySeconds);
// Replace internal Docker hostname with public URL if configured
if (this.publicUrl) {
const clientEndpoint = this.client.host + (this.client.port ? `:${this.client.port}` : '');
const publicEndpoint = this.publicUrl.replace(/^https?:\/\//, '');
return presignedUrl.replace(
`${this.client.protocol}//${clientEndpoint}`,
this.publicUrl
);
}
return presignedUrl;
}
async listProjectFiles(