object storage related updates
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user