Skip to content

Commit

Permalink
feat: v4.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
surmon-china committed Aug 28, 2024
1 parent 40f68cd commit 1fad698
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 18 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nodepress",
"version": "4.10.0",
"version": "4.11.0",
"description": "RESTful API service for Surmon.me blog",
"author": "Surmon",
"license": "MIT",
Expand Down
31 changes: 28 additions & 3 deletions src/modules/extension/extension.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { AdminOnlyGuard } from '@app/guards/admin-only.guard'
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'
Expand Down Expand Up @@ -39,11 +39,36 @@ export class ExtensionController {
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,
Expand Down
10 changes: 7 additions & 3 deletions src/modules/extension/extension.service.dbbackup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import schedule from 'node-schedule'
import { Injectable } from '@nestjs/common'
import { EmailService } from '@app/processors/helper/helper.service.email'
import {
UploadResult,
S3FileObject,
AWSService,
AWSStorageClass,
AWSServerSideEncryption
Expand Down Expand Up @@ -45,7 +45,11 @@ export class DBBackupService {
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) {
Expand All @@ -64,7 +68,7 @@ export class DBBackupService {
}

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]')
}
Expand Down
57 changes: 46 additions & 11 deletions src/processors/helper/helper.service.aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import {
S3Client,
PutObjectCommand,
ListObjectsCommand,
GetObjectAttributesCommand,
ObjectAttributes,
StorageClass,
Expand All @@ -28,11 +29,13 @@ export interface FileUploader {
encryption?: ServerSideEncryption
}

export interface UploadResult {
export interface S3FileObject {
key: string
url: string
eTag: string
size: number
lastModified?: Date
storageClass?: StorageClass
}

@Injectable()
Expand All @@ -47,6 +50,11 @@ export class AWSService {
})
}

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({
Expand All @@ -57,7 +65,7 @@ export class AWSService {
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({
Expand All @@ -69,15 +77,42 @@ export class AWSService {
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() {}
}

0 comments on commit 1fad698

Please sign in to comment.