update uploader
This commit is contained in:
@@ -13,28 +13,23 @@ export class S3Service {
|
|||||||
private readonly logger = new Logger(S3Service.name);
|
private readonly logger = new Logger(S3Service.name);
|
||||||
private readonly s3Client: S3Client;
|
private readonly s3Client: S3Client;
|
||||||
private readonly bucketName: string;
|
private readonly bucketName: string;
|
||||||
private readonly region: string; // Region is often 'us-east-1' or similar for Liara, but depends on your config
|
private readonly region: string;
|
||||||
private readonly endpointUrl: string; // Renamed 'url' to 'endpointUrl' for clarity
|
private readonly endpointUrl: string;
|
||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {
|
constructor(private readonly configService: ConfigService) {
|
||||||
// --- Configuration for Liara S3 ---
|
|
||||||
this.bucketName = this.configService.getOrThrow<string>('BUCKET_NAME');
|
this.bucketName = this.configService.getOrThrow<string>('BUCKET_NAME');
|
||||||
// Liara S3 usually requires a specific endpoint URL
|
this.endpointUrl = this.configService.getOrThrow<string>('PARSPACK_S3_ENDPOINT');
|
||||||
this.endpointUrl = this.configService.getOrThrow<string>('LIARA_S3_ENDPOINT');
|
|
||||||
// The region for Liara S3 is often 'us-east-1' or just a placeholder,
|
|
||||||
// but we'll keep it configurable for consistency.
|
|
||||||
this.region = this.configService.getOrThrow<string>('BUCKET_REGION');
|
this.region = this.configService.getOrThrow<string>('BUCKET_REGION');
|
||||||
|
|
||||||
this.s3Client = new S3Client({
|
this.s3Client = new S3Client({
|
||||||
region: this.region, // Use the configured region
|
region: this.region,
|
||||||
endpoint: this.endpointUrl, // This is the crucial change for Liara S3
|
endpoint: this.endpointUrl,
|
||||||
forcePathStyle: true, // Often required when using custom S3 endpoints like Liara
|
forcePathStyle: true,
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: this.configService.getOrThrow<string>('BUCKET_ACCESS_KEY'),
|
accessKeyId: this.configService.getOrThrow<string>('BUCKET_ACCESS_KEY'),
|
||||||
secretAccessKey: this.configService.getOrThrow<string>('BUCKET_SECRET_KEY'),
|
secretAccessKey: this.configService.getOrThrow<string>('BUCKET_SECRET_KEY'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// ------------------------------------
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,17 +44,14 @@ export class S3Service {
|
|||||||
try {
|
try {
|
||||||
const sanitizedMetadata = metadata
|
const sanitizedMetadata = metadata
|
||||||
? Object.entries(metadata).reduce(
|
? Object.entries(metadata).reduce(
|
||||||
(acc, [key, value]) => {
|
(acc, [metaKey, value]) => {
|
||||||
// Encode all values to be ASCII-safe
|
// S3 user metadata must be US-ASCII; base64 keeps signing stable for Unicode names
|
||||||
acc[key] = encodeURIComponent(value);
|
acc[metaKey] = Buffer.from(value, 'utf8').toString('base64');
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{} as Record<string, string>,
|
{} as Record<string, string>,
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
// NOTE: 'ACL: public-read' might not be supported or necessary for Liara S3,
|
|
||||||
// depending on your bucket configuration on Liara.
|
|
||||||
// If uploads fail, try removing this line.
|
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: this.bucketName,
|
Bucket: this.bucketName,
|
||||||
Key: key,
|
Key: key,
|
||||||
@@ -71,9 +63,8 @@ export class S3Service {
|
|||||||
|
|
||||||
await this.s3Client.send(command);
|
await this.s3Client.send(command);
|
||||||
|
|
||||||
// --- URL for Liara S3 ---
|
const encodedKey = key.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
||||||
// The public URL for Liara S3 files is typically: ${endpoint}/${bucketName}/${key}
|
const url = `${this.endpointUrl}/${this.bucketName}/${encodedKey}`;
|
||||||
const url = `${this.endpointUrl}/${this.bucketName}/${key}`;
|
|
||||||
|
|
||||||
this.logger.log(`File uploaded to S3: ${key}`);
|
this.logger.log(`File uploaded to S3: ${key}`);
|
||||||
|
|
||||||
@@ -88,8 +79,6 @@ export class S3Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Other methods remain the same as they use the S3Client which is now configured for Liara ---
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file stream from S3
|
* Get file stream from S3
|
||||||
*/
|
*/
|
||||||
@@ -156,7 +145,7 @@ export class S3Service {
|
|||||||
generateFileKey(originalName: string, fileType: FileType): string {
|
generateFileKey(originalName: string, fileType: FileType): string {
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const randomSuffix = randomInt(1000000, 9999999);
|
const randomSuffix = randomInt(1000000, 9999999);
|
||||||
const extension = originalName.split('.').pop();
|
const extension = this.extractSafeExtension(originalName);
|
||||||
|
|
||||||
const basePathMap: Record<FileType, string> = {
|
const basePathMap: Record<FileType, string> = {
|
||||||
image: 'images',
|
image: 'images',
|
||||||
@@ -170,4 +159,21 @@ export class S3Service {
|
|||||||
const basePath = basePathMap[fileType];
|
const basePath = basePathMap[fileType];
|
||||||
return `${basePath}/${timestamp}-${randomSuffix}.${extension}`;
|
return `${basePath}/${timestamp}-${randomSuffix}.${extension}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a safe ASCII extension so Persian/spaced names never end up in the S3 object key.
|
||||||
|
*/
|
||||||
|
private extractSafeExtension(originalName: string): string {
|
||||||
|
const basename = originalName.replace(/\\/g, '/').split('/').pop() ?? 'file';
|
||||||
|
const lastDot = basename.lastIndexOf('.');
|
||||||
|
|
||||||
|
if (lastDot > 0 && lastDot < basename.length - 1) {
|
||||||
|
const ext = basename.slice(lastDot + 1).toLowerCase();
|
||||||
|
if (/^[a-z0-9]{1,16}$/.test(ext)) {
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'bin';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ export const adminsData: AdminData[] = [
|
|||||||
lastName: 'مرتضایی',
|
lastName: 'مرتضایی',
|
||||||
roleName: 'admin',
|
roleName: 'admin',
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
phone: '09121724095',
|
||||||
|
firstName: 'آقای',
|
||||||
|
lastName: 'نواعتقاد',
|
||||||
|
roleName: 'admin',
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
phone: '09185290775',
|
phone: '09185290775',
|
||||||
@@ -21,9 +28,9 @@ export const adminsData: AdminData[] = [
|
|||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
phone: '09129283395',
|
phone: '09180000000',
|
||||||
firstName: 'مهرداد',
|
firstName: 'ندا',
|
||||||
lastName: 'مظفری',
|
lastName: 'سعیدی',
|
||||||
roleName: 'designer',
|
roleName: 'designer',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user