object storage related updates

This commit is contained in:
2026-08-04 03:34:52 -05:00
parent 60c5ca23b2
commit 4df4d8c9b5
2 changed files with 44 additions and 12 deletions

View File

@@ -21,8 +21,13 @@ async function bootstrap() {
const appEnv = configService.get<string>('app.env') || 'development'; const appEnv = configService.get<string>('app.env') || 'development';
const corsOrigins = configService.get<string[]>('cors.origins') || []; const corsOrigins = configService.get<string[]>('cors.origins') || [];
// Security // Security — allow cross-origin API reads from frontend/admin domains
app.use(helmet()); app.use(
helmet({
crossOriginResourcePolicy: { policy: 'cross-origin' },
crossOriginOpenerPolicy: { policy: 'same-origin-allow-popups' },
}),
);
app.use(cookieParser()); app.use(cookieParser());
// CORS // CORS

View File

@@ -16,12 +16,31 @@ export class UploadService {
private bucket: string; private bucket: string;
private region: string; private region: string;
private folderPrefix: string; private folderPrefix: string;
/** Endpoint used for SDK signing/calls (no /s3 path prefix). */
private endpoint: string; private endpoint: string;
/** Public base shown in browser URLs (may include /s3 path prefix). */
private publicEndpoint: string;
constructor(private configService: ConfigService) { constructor(private configService: ConfigService) {
this.region = this.configService.get<string>('AWS_REGION') || 'us-east-1'; this.region = this.configService.get<string>('AWS_REGION') || 'us-east-1';
this.bucket = this.configService.get<string>('AWS_S3_BUCKET') || ''; this.bucket = this.configService.get<string>('AWS_S3_BUCKET') || '';
this.endpoint = this.configService.get<string>('S3_ENDPOINT') || ''; const rawEndpoint = (this.configService.get<string>('S3_ENDPOINT') || '').replace(
/\/$/,
'',
);
// Path-prefix public URL (e.g. https://re-quest.com/s3): sign against the host
// root so SigV4 path matches what MinIO sees after nginx strips /s3.
if (rawEndpoint.endsWith('/s3')) {
this.publicEndpoint = rawEndpoint;
this.endpoint = rawEndpoint.slice(0, -3);
} else {
this.endpoint = rawEndpoint;
this.publicEndpoint =
(this.configService.get<string>('S3_PUBLIC_ENDPOINT') || rawEndpoint).replace(
/\/$/,
'',
);
}
// Use S3_FOLDER_PREFIX from env, defaults to 'development' for dev environment // Use S3_FOLDER_PREFIX from env, defaults to 'development' for dev environment
this.folderPrefix = this.configService.get<string>('S3_FOLDER_PREFIX') || 'development'; this.folderPrefix = this.configService.get<string>('S3_FOLDER_PREFIX') || 'development';
@@ -52,6 +71,14 @@ export class UploadService {
this.s3Client = new S3Client(s3Config); this.s3Client = new S3Client(s3Config);
} }
/** Rewrite SDK URLs to the public /s3 path when configured. */
private toPublicUrl(url: string): string {
if (!this.endpoint || !this.publicEndpoint || this.endpoint === this.publicEndpoint) {
return url;
}
return url.replace(this.endpoint, this.publicEndpoint);
}
/** /**
* Get the full S3 key with environment prefix * Get the full S3 key with environment prefix
*/ */
@@ -139,11 +166,9 @@ export class UploadService {
expiresIn: 3600, // 1 hour expiresIn: 3600, // 1 hour
}); });
const publicUrl = `https://${this.bucket}.s3.${this.region}.amazonaws.com/${key}`;
return { return {
uploadUrl, uploadUrl: this.toPublicUrl(uploadUrl),
publicUrl, publicUrl: this.getPublicUrl(key),
key, key,
}; };
} }
@@ -163,15 +188,17 @@ export class UploadService {
Key: key, Key: key,
}); });
return getSignedUrl(this.s3Client, command, { const url = await getSignedUrl(this.s3Client, command, {
expiresIn: 3600, // 1 hour expiresIn: 3600, // 1 hour
}); });
return this.toPublicUrl(url);
} }
getPublicUrl(key: string): string { getPublicUrl(key: string): string {
const base = this.publicEndpoint || this.endpoint;
// For custom S3-compatible endpoints (Contabo, MinIO, etc.) // For custom S3-compatible endpoints (Contabo, MinIO, etc.)
if (this.endpoint) { if (base) {
return `${this.endpoint}/${this.bucket}/${key}`; return `${base}/${this.bucket}/${key}`;
} }
// For AWS S3 // For AWS S3
return `https://${this.bucket}.s3.${this.region}.amazonaws.com/${key}`; return `https://${this.bucket}.s3.${this.region}.amazonaws.com/${key}`;
@@ -242,7 +269,7 @@ export class UploadService {
const publicUrl = this.getPublicUrl(key); const publicUrl = this.getPublicUrl(key);
return { return {
uploadUrl, uploadUrl: this.toPublicUrl(uploadUrl),
publicUrl, publicUrl,
key, key,
}; };
@@ -279,7 +306,7 @@ export class UploadService {
const publicUrl = this.getPublicUrl(key); const publicUrl = this.getPublicUrl(key);
return { return {
uploadUrl, uploadUrl: this.toPublicUrl(uploadUrl),
publicUrl, publicUrl,
key, key,
}; };