mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-24 12:07:08 -04:00
S3Backend: Add S3 MediaBackend
Add minio dependency Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
cf6c08e3d6
commit
2d98e2f8b4
5 changed files with 186 additions and 11 deletions
79
src/media/backends/s3-backend.ts
Normal file
79
src/media/backends/s3-backend.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import mediaConfiguration from '../../config/media.config';
|
||||
import { ConsoleLoggerService } from '../../logger/console-logger.service';
|
||||
import { MediaBackend } from '../media-backend.interface';
|
||||
import { BackendData } from '../media-upload.entity';
|
||||
import { MediaConfig } from '../../config/media.config';
|
||||
import { Client } from 'minio';
|
||||
import { BackendType } from './backend-type.enum';
|
||||
import { MediaBackendError } from '../../errors/errors';
|
||||
|
||||
@Injectable()
|
||||
export class S3Backend implements MediaBackend {
|
||||
private config: MediaConfig['backend']['s3'];
|
||||
private client: Client;
|
||||
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
@Inject(mediaConfiguration.KEY)
|
||||
private mediaConfig: MediaConfig,
|
||||
) {
|
||||
this.logger.setContext(S3Backend.name);
|
||||
if (mediaConfig.backend.use === BackendType.S3) {
|
||||
this.config = mediaConfig.backend.s3;
|
||||
const url = new URL(this.config.endPoint);
|
||||
const secure = url.protocol === 'https:'; // url.protocol contains a trailing ':'
|
||||
const endpoint = `${url.hostname}${url.pathname}`;
|
||||
let port = parseInt(url.port);
|
||||
if (isNaN(port)) {
|
||||
port = secure ? 443 : 80;
|
||||
}
|
||||
this.client = new Client({
|
||||
endPoint: endpoint.substr(0, endpoint.length - 1), // remove trailing '/'
|
||||
port: port,
|
||||
useSSL: secure,
|
||||
accessKey: this.config.accessKeyId,
|
||||
secretKey: this.config.secretAccessKey,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async saveFile(
|
||||
buffer: Buffer,
|
||||
fileName: string,
|
||||
): Promise<[string, BackendData]> {
|
||||
try {
|
||||
await this.client.putObject(this.config.bucket, fileName, buffer);
|
||||
this.logger.log(`Uploaded file ${fileName}`, 'saveFile');
|
||||
return [this.getUrl(fileName), null];
|
||||
} catch (e) {
|
||||
this.logger.error((e as Error).message, (e as Error).stack, 'saveFile');
|
||||
throw new MediaBackendError(`Could not save '${fileName}' on S3`);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(fileName: string, _: BackendData): Promise<void> {
|
||||
try {
|
||||
await this.client.removeObject(this.config.bucket, fileName);
|
||||
const url = this.getUrl(fileName);
|
||||
this.logger.log(`Deleted ${url}`, 'saveFile');
|
||||
return;
|
||||
} catch (e) {
|
||||
this.logger.error((e as Error).message, (e as Error).stack, 'saveFile');
|
||||
throw new MediaBackendError(`Could not delete '${fileName}' on S3`);
|
||||
}
|
||||
}
|
||||
|
||||
private getUrl(fileName: string): string {
|
||||
const url = new URL(this.config.endPoint);
|
||||
const port = url.port !== '' ? `:${url.port}` : '';
|
||||
const bucket = this.config.bucket;
|
||||
return `${url.protocol}//${url.hostname}${port}${url.pathname}${bucket}/${fileName}`;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import { UsersModule } from '../users/users.module';
|
|||
import { FilesystemBackend } from './backends/filesystem-backend';
|
||||
import { MediaUpload } from './media-upload.entity';
|
||||
import { MediaService } from './media.service';
|
||||
import { S3Backend } from './backends/s3-backend';
|
||||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
import { AzureBackend } from './backends/azure-backend';
|
||||
|
||||
|
@ -24,7 +25,13 @@ import { AzureBackend } from './backends/azure-backend';
|
|||
LoggerModule,
|
||||
ConfigModule,
|
||||
],
|
||||
providers: [MediaService, FilesystemBackend, AzureBackend, ImgurBackend],
|
||||
providers: [
|
||||
MediaService,
|
||||
FilesystemBackend,
|
||||
AzureBackend,
|
||||
ImgurBackend,
|
||||
S3Backend,
|
||||
],
|
||||
exports: [MediaService],
|
||||
})
|
||||
export class MediaModule {}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { FilesystemBackend } from './backends/filesystem-backend';
|
|||
import { MediaBackend } from './media-backend.interface';
|
||||
import { MediaUpload } from './media-upload.entity';
|
||||
import { MediaUploadUrlDto } from './media-upload-url.dto';
|
||||
import { S3Backend } from './backends/s3-backend';
|
||||
import { AzureBackend } from './backends/azure-backend';
|
||||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
|
||||
|
@ -164,6 +165,8 @@ export class MediaService {
|
|||
return BackendType.AZURE;
|
||||
case 'imgur':
|
||||
return BackendType.IMGUR;
|
||||
case 's3':
|
||||
return BackendType.S3;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +174,8 @@ export class MediaService {
|
|||
switch (type) {
|
||||
case BackendType.FILESYSTEM:
|
||||
return this.moduleRef.get(FilesystemBackend);
|
||||
case BackendType.S3:
|
||||
return this.moduleRef.get(S3Backend);
|
||||
case BackendType.AZURE:
|
||||
return this.moduleRef.get(AzureBackend);
|
||||
case BackendType.IMGUR:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue