mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2026-06-23 04:10:17 +00:00
a880864b2c
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import { MediaBackendType } from '@hedgedoc/commons';
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import { FileTypeResult } from 'file-type';
|
|
import { Client } from 'minio';
|
|
import { URL } from 'url';
|
|
|
|
import mediaConfiguration, {
|
|
MediaConfig,
|
|
S3MediaConfig,
|
|
} from '../../config/media.config';
|
|
import { MediaBackendError } from '../../errors/errors';
|
|
import { ConsoleLoggerService } from '../../logger/console-logger.service';
|
|
import { MediaBackend } from '../media-backend.interface';
|
|
|
|
@Injectable()
|
|
export class S3Backend implements MediaBackend {
|
|
private config: S3MediaConfig['s3'];
|
|
private client: Client;
|
|
|
|
private static determinePort(url: URL): number | undefined {
|
|
const port = parseInt(url.port);
|
|
return isNaN(port) ? undefined : port;
|
|
}
|
|
|
|
constructor(
|
|
private readonly logger: ConsoleLoggerService,
|
|
@Inject(mediaConfiguration.KEY)
|
|
private mediaConfig: MediaConfig,
|
|
) {
|
|
this.logger.setContext(S3Backend.name);
|
|
// only create the backend if s3 is configured
|
|
if (this.mediaConfig.backend.type !== MediaBackendType.S3) {
|
|
return;
|
|
}
|
|
this.config = this.mediaConfig.backend.s3;
|
|
const url = new URL(this.config.endpoint);
|
|
const isSecure = url.protocol === 'https:';
|
|
this.client = new Client({
|
|
endPoint: url.hostname,
|
|
port: S3Backend.determinePort(url),
|
|
useSSL: isSecure,
|
|
accessKey: this.config.accessKeyId,
|
|
secretKey: this.config.secretAccessKey,
|
|
pathStyle: this.config.pathStyle,
|
|
region: this.config.region,
|
|
});
|
|
}
|
|
|
|
async saveFile(
|
|
uuid: string,
|
|
buffer: Buffer,
|
|
fileType: FileTypeResult,
|
|
): Promise<null> {
|
|
try {
|
|
await this.client.putObject(
|
|
this.config.bucket,
|
|
uuid,
|
|
buffer,
|
|
buffer.length,
|
|
{
|
|
// oxlint-disable-next-line @typescript-eslint/naming-convention
|
|
'Content-Type': fileType.mime,
|
|
},
|
|
);
|
|
this.logger.log(`Uploaded file ${uuid}`, 'saveFile');
|
|
return null;
|
|
} catch (e) {
|
|
this.logger.error((e as Error).message, (e as Error).stack, 'saveFile');
|
|
throw new MediaBackendError(`Could not save file ${uuid}`);
|
|
}
|
|
}
|
|
|
|
async deleteFile(uuid: string, _: unknown): Promise<void> {
|
|
try {
|
|
await this.client.removeObject(this.config.bucket, uuid);
|
|
this.logger.log(`Deleted uploaded file ${uuid}`, 'deleteFile');
|
|
} catch (e) {
|
|
this.logger.error((e as Error).message, (e as Error).stack, 'deleteFile');
|
|
throw new MediaBackendError(`Could not delete file ${uuid}`);
|
|
}
|
|
}
|
|
|
|
async getFileUrl(uuid: string, _: unknown): Promise<string> {
|
|
try {
|
|
return await this.client.presignedGetObject(this.config.bucket, uuid);
|
|
} catch (e) {
|
|
this.logger.error((e as Error).message, (e as Error).stack, 'getFileUrl');
|
|
throw new MediaBackendError(`Could not get URL for file ${uuid}`);
|
|
}
|
|
}
|
|
}
|