From d21a76b8ead3a814b0b40d5ba29e6c21d0e620ca Mon Sep 17 00:00:00 2001 From: surmon-china Date: Wed, 28 Aug 2024 22:07:08 +0000 Subject: [PATCH] deploy: 1fad6985dc8dcc68d639ef2c20b8dce6026e2489 --- controllers/ExtensionController.html | 144 +++++++-- injectables/AWSService.html | 286 ++++++++++++++++-- injectables/DBBackupService.html | 18 +- interfaces/FileUploader.html | 57 +++- .../{UploadResult.html => S3FileObject.html} | 151 ++++++++- js/menu-wc.js | 6 +- js/menu-wc_es5.js | 2 +- js/search/search_index.js | 4 +- modules/CacheModule.html | 8 +- modules/CacheModule/dependencies.svg | 8 +- modules/CategoryModule.html | 40 +-- modules/CategoryModule/dependencies.svg | 40 +-- modules/DisqusModule.html | 8 +- modules/DisqusModule/dependencies.svg | 8 +- modules/ExtensionModule.html | 8 +- modules/ExtensionModule/dependencies.svg | 8 +- modules/HelperModule.html | 8 +- modules/HelperModule/dependencies.svg | 8 +- properties.html | 2 +- 19 files changed, 669 insertions(+), 145 deletions(-) rename interfaces/{UploadResult.html => S3FileObject.html} (75%) diff --git a/controllers/ExtensionController.html b/controllers/ExtensionController.html index 10275ff3..a057bb6c 100644 --- a/controllers/ExtensionController.html +++ b/controllers/ExtensionController.html @@ -140,6 +140,13 @@
Methods
@@ -195,6 +202,84 @@
Methods

Methods

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + Async + getStaticFileList + + +
+ + getStaticFileList(undefined: QueryParamsResult) +
+ Decorators : +
+ @Get('static/list')
@UseGuards(AdminOnlyGuard)
@('Get file list from cloud storage')
+
+ +
+ +
+ Parameters : + + + + + + + + + + + + + + + + + + +
NameTypeOptional
+ QueryParamsResult + + No +
+
+
+ Returns : unknown + +
+
+ +
+
@@ -303,8 +388,8 @@

@@ -377,8 +462,8 @@

@@ -451,8 +536,8 @@

@@ -546,22 +631,22 @@

@@ -569,14 +654,14 @@

@@ -649,8 +734,8 @@

import { AdminMaybeGuard } from '@app/guards/admin-maybe.guard' import { Responser } from '@app/decorators/responser.decorator' import { QueryParams, QueryParamsResult } from '@app/decorators/queryparams.decorator' -import { AWSService } from '@app/processors/helper/helper.service.aws' import { GoogleService } from '@app/processors/helper/helper.service.google' +import { AWSService } from '@app/processors/helper/helper.service.aws' import { StatisticService, Statistic } from './extension.service.statistic' import { DBBackupService } from './extension.service.dbbackup' import * as APP_CONFIG from '@app/app.config' @@ -678,11 +763,36 @@

return this.dbBackupService.backup() } - @Post('upload') + @Get('static/list') + @UseGuards(AdminOnlyGuard) + @Responser.handle('Get file list from cloud storage') + async getStaticFileList(@QueryParams() { query }: QueryParamsResult) { + const minLimit = 80 + const numberLimit = Number(query.limit) + const limit = Number.isInteger(numberLimit) ? numberLimit : minLimit + const result = await this.awsService.getFileList({ + limit: limit < minLimit ? minLimit : limit, + prefix: query.prefix, + marker: query.marker, + region: APP_CONFIG.AWS.s3StaticRegion, + bucket: APP_CONFIG.AWS.s3StaticBucket + }) + + return { + ...result, + files: result.files.map((file) => ({ + ...file, + url: `${APP_CONFIG.APP.STATIC_URL}/${file.key}`, + lastModified: file.lastModified?.getTime() + })) + } + } + + @Post('static/upload') @UseGuards(AdminOnlyGuard) @UseInterceptors(FileInterceptor('file')) @Responser.handle('Upload file to cloud storage') - async uploadStatic(@UploadedFile() file: Express.Multer.File, @Body() body) { + async uploadStaticFile(@UploadedFile() file: Express.Multer.File, @Body() body) { const result = await this.awsService.uploadFile({ name: body.name, file: file.buffer, diff --git a/injectables/AWSService.html b/injectables/AWSService.html index 0080df02..549b9040 100644 --- a/injectables/AWSService.html +++ b/injectables/AWSService.html @@ -139,6 +139,19 @@

Methods
PrivatecreateClient +
  • + Public + Async + deleteFile +
  • +
  • + Private + getAwsGeneralFileUrl +
  • +
  • + Public + getFileList +
  • Public getObjectAttributes @@ -187,8 +200,117 @@

  • + + + + + + + +
    - +
    - +
    - +
    - + Async - uploadStatic - + uploadStaticFile +
    - uploadStatic(file: Express.Multer.File, body) + uploadStaticFile(file: Express.Multer.File, body)
    Decorators :
    - @Post('upload')
    @UseGuards(AdminOnlyGuard)
    @UseInterceptors(undefined)
    @('Upload file to cloud storage')
    + @Post('static/upload')
    @UseGuards(AdminOnlyGuard)
    @UseInterceptors(undefined)
    @('Upload file to cloud storage')
    - +
    - + +
    + +
    + Parameters : + + + + + + + + + + + + + + + + + + + +
    NameTypeOptional
    region + string + + No +
    +
    +
    + Returns : any + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + +
    + + + Public + Async + deleteFile + + +
    + + deleteFile() +
    + +
    + +
    + Returns : any + +
    +
    + + + + + + + + + + + + @@ -219,6 +341,99 @@

    + +

    + + + + + + + + + + + + + + + + +
    + + + Private + getAwsGeneralFileUrl + + +
    + + getAwsGeneralFileUrl(region: string, bucket: string, key: string) +
    +
    bucket + string + + No +
    key + string + + No +
    + +
    + Returns : string + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + @@ -325,8 +540,8 @@

    @@ -362,7 +577,7 @@

    + + + Public + getFileList + + +
    + + getFileList(payload: literal type) +
    + +
    + +
    + Parameters : + + + + + + + + + + + + + + + + +
    NameTypeOptional
    payload + literal type + + No +
    @@ -256,8 +471,8 @@

    - +
    - +
    - Returns : Promise<UploadResult> + Returns : Promise<S3FileObject>
    @@ -381,6 +596,7 @@

    import {
       S3Client,
       PutObjectCommand,
    +  ListObjectsCommand,
       GetObjectAttributesCommand,
       ObjectAttributes,
       StorageClass,
    @@ -402,11 +618,13 @@ 

    encryption?: ServerSideEncryption } -export interface UploadResult { +export interface S3FileObject { key: string url: string eTag: string size: number + lastModified?: Date + storageClass?: StorageClass } @Injectable() @@ -421,6 +639,11 @@

    }) } + private getAwsGeneralFileUrl(region: string, bucket: string, key: string) { + // https://stackoverflow.com/questions/44400227/how-to-get-the-url-of-a-file-on-aws-s3-using-aws-sdk + return `https://${bucket}.s3.${region}.amazonaws.com/${key}` + } + public getObjectAttributes(payload: { region: string; bucket: string; key: string }) { const s3Client = this.createClient(payload.region) const command = new GetObjectAttributesCommand({ @@ -431,7 +654,7 @@

    return s3Client.send(command) } - public uploadFile(payload: FileUploader): Promise<UploadResult> { + public uploadFile(payload: FileUploader): Promise<S3FileObject> { const { region, bucket, name: key } = payload const s3Client = this.createClient(region) const command = new PutObjectCommand({ @@ -443,17 +666,44 @@

    ServerSideEncryption: payload.encryption }) return s3Client.send(command).then(() => { - return this.getObjectAttributes({ region, bucket, key }).then((attributes) => { - return { - key, - // https://stackoverflow.com/questions/44400227/how-to-get-the-url-of-a-file-on-aws-s3-using-aws-sdk - url: `https://${bucket}.s3.${region}.amazonaws.com/${key}`, - eTag: attributes.ETag!, - size: attributes.ObjectSize! - } - }) + return this.getObjectAttributes({ region, bucket, key }).then((attributes) => ({ + key, + url: this.getAwsGeneralFileUrl(region, bucket, key), + size: attributes.ObjectSize!, + eTag: attributes.ETag!, + lastModified: attributes.LastModified, + storageClass: attributes.StorageClass + })) + }) + } + + public getFileList(payload: { region: string; bucket: string; limit: number; prefix?: string; marker?: string }) { + const s3Client = this.createClient(payload.region) + const command = new ListObjectsCommand({ + Bucket: payload.bucket, + Marker: payload.marker, + Prefix: payload.prefix, + MaxKeys: payload.limit }) + + return s3Client.send(command).then((result) => ({ + name: result.Name, + limit: result.MaxKeys, + prefix: result.Prefix, + marker: result.Marker, + files: (result.Contents ?? []).map<S3FileObject>((object) => ({ + key: object.Key!, + url: this.getAwsGeneralFileUrl(payload.region, payload.bucket, object.Key!), + eTag: object.ETag!, + size: object.Size!, + lastModified: object.LastModified, + storageClass: object.StorageClass + })) + })) } + + // TODO + public async deleteFile() {} }

    diff --git a/injectables/DBBackupService.html b/injectables/DBBackupService.html index 0d16b9fb..1f71a490 100644 --- a/injectables/DBBackupService.html +++ b/injectables/DBBackupService.html @@ -288,8 +288,8 @@

    - + @@ -327,8 +327,8 @@

    - + @@ -412,7 +412,7 @@

    import { Injectable } from '@nestjs/common' import { EmailService } from '@app/processors/helper/helper.service.email' import { - UploadResult, + S3FileObject, AWSService, AWSStorageClass, AWSServerSideEncryption @@ -445,7 +445,11 @@

    public async backup() { try { const result = await this.doBackup() - const json = { ...result, size: (result.size / 1024).toFixed(2) + 'kb' } + const json = { + ...result, + lastModified: result.lastModified?.toLocaleString('zh'), + size: (result.size / 1024).toFixed(2) + 'kb' + } this.mailToAdmin('Database backup succeeded', JSON.stringify(json, null, 2), true) return result } catch (error) { @@ -464,7 +468,7 @@

    } private doBackup() { - return new Promise<UploadResult>((resolve, reject) => { + return new Promise<S3FileObject>((resolve, reject) => { if (!shell.which('mongodump')) { return reject('DB Backup script requires [mongodump]') } diff --git a/interfaces/FileUploader.html b/interfaces/FileUploader.html index 50346a8f..48fbc576 100644 --- a/interfaces/FileUploader.html +++ b/interfaces/FileUploader.html @@ -450,6 +450,7 @@

    Properties

    import {
       S3Client,
       PutObjectCommand,
    +  ListObjectsCommand,
       GetObjectAttributesCommand,
       ObjectAttributes,
       StorageClass,
    @@ -471,11 +472,13 @@ 

    Properties

    encryption?: ServerSideEncryption } -export interface UploadResult { +export interface S3FileObject { key: string url: string eTag: string size: number + lastModified?: Date + storageClass?: StorageClass } @Injectable() @@ -490,6 +493,11 @@

    Properties

    }) } + private getAwsGeneralFileUrl(region: string, bucket: string, key: string) { + // https://stackoverflow.com/questions/44400227/how-to-get-the-url-of-a-file-on-aws-s3-using-aws-sdk + return `https://${bucket}.s3.${region}.amazonaws.com/${key}` + } + public getObjectAttributes(payload: { region: string; bucket: string; key: string }) { const s3Client = this.createClient(payload.region) const command = new GetObjectAttributesCommand({ @@ -500,7 +508,7 @@

    Properties

    return s3Client.send(command) } - public uploadFile(payload: FileUploader): Promise<UploadResult> { + public uploadFile(payload: FileUploader): Promise<S3FileObject> { const { region, bucket, name: key } = payload const s3Client = this.createClient(region) const command = new PutObjectCommand({ @@ -512,17 +520,44 @@

    Properties

    ServerSideEncryption: payload.encryption }) return s3Client.send(command).then(() => { - return this.getObjectAttributes({ region, bucket, key }).then((attributes) => { - return { - key, - // https://stackoverflow.com/questions/44400227/how-to-get-the-url-of-a-file-on-aws-s3-using-aws-sdk - url: `https://${bucket}.s3.${region}.amazonaws.com/${key}`, - eTag: attributes.ETag!, - size: attributes.ObjectSize! - } - }) + return this.getObjectAttributes({ region, bucket, key }).then((attributes) => ({ + key, + url: this.getAwsGeneralFileUrl(region, bucket, key), + size: attributes.ObjectSize!, + eTag: attributes.ETag!, + lastModified: attributes.LastModified, + storageClass: attributes.StorageClass + })) }) } + + public getFileList(payload: { region: string; bucket: string; limit: number; prefix?: string; marker?: string }) { + const s3Client = this.createClient(payload.region) + const command = new ListObjectsCommand({ + Bucket: payload.bucket, + Marker: payload.marker, + Prefix: payload.prefix, + MaxKeys: payload.limit + }) + + return s3Client.send(command).then((result) => ({ + name: result.Name, + limit: result.MaxKeys, + prefix: result.Prefix, + marker: result.Marker, + files: (result.Contents ?? []).map<S3FileObject>((object) => ({ + key: object.Key!, + url: this.getAwsGeneralFileUrl(payload.region, payload.bucket, object.Key!), + eTag: object.ETag!, + size: object.Size!, + lastModified: object.LastModified, + storageClass: object.StorageClass + })) + })) + } + + // TODO + public async deleteFile() {} }
    diff --git a/interfaces/UploadResult.html b/interfaces/S3FileObject.html similarity index 75% rename from interfaces/UploadResult.html rename to interfaces/S3FileObject.html index d5a58e81..ed1389c2 100644 --- a/interfaces/UploadResult.html +++ b/interfaces/S3FileObject.html @@ -98,7 +98,7 @@ + S3FileObject