mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-06-06 09:31:35 -04:00

Co-authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import {
|
|
BlobSASPermissions,
|
|
BlobServiceClient,
|
|
BlockBlobClient,
|
|
ContainerClient,
|
|
generateBlobSASQueryParameters,
|
|
StorageSharedKeyCredential,
|
|
} from '@azure/storage-blob';
|
|
import { MediaBackendType } from '@hedgedoc/commons';
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import { FileTypeResult } from 'file-type';
|
|
|
|
import mediaConfiguration, {
|
|
AzureMediaConfig,
|
|
MediaConfig,
|
|
} 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 AzureBackend implements MediaBackend {
|
|
private config: AzureMediaConfig['azure'];
|
|
private client: ContainerClient;
|
|
private readonly credential: StorageSharedKeyCredential;
|
|
|
|
constructor(
|
|
private readonly logger: ConsoleLoggerService,
|
|
@Inject(mediaConfiguration.KEY)
|
|
private mediaConfig: MediaConfig,
|
|
) {
|
|
this.logger.setContext(AzureBackend.name);
|
|
// only create the backend if azure is configured
|
|
if (this.mediaConfig.backend.use !== MediaBackendType.AZURE) {
|
|
return;
|
|
}
|
|
this.config = this.mediaConfig.backend.azure;
|
|
const blobServiceClient = BlobServiceClient.fromConnectionString(
|
|
this.config.connectionString,
|
|
);
|
|
this.credential =
|
|
blobServiceClient.credential as StorageSharedKeyCredential;
|
|
this.client = blobServiceClient.getContainerClient(this.config.container);
|
|
}
|
|
|
|
async saveFile(
|
|
uuid: string,
|
|
buffer: Buffer,
|
|
fileType: FileTypeResult,
|
|
): Promise<null> {
|
|
const blockBlobClient: BlockBlobClient =
|
|
this.client.getBlockBlobClient(uuid);
|
|
try {
|
|
await blockBlobClient.upload(buffer, buffer.length, {
|
|
blobHTTPHeaders: {
|
|
blobContentType: fileType.mime,
|
|
},
|
|
});
|
|
this.logger.log(`Uploaded file ${uuid}`, 'saveFile');
|
|
return null;
|
|
} catch (e) {
|
|
this.logger.error(
|
|
`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> {
|
|
const blockBlobClient: BlockBlobClient =
|
|
this.client.getBlockBlobClient(uuid);
|
|
try {
|
|
const response = await blockBlobClient.delete();
|
|
if (response.errorCode !== undefined) {
|
|
throw new MediaBackendError(
|
|
`Could not delete '${uuid}': ${response.errorCode}`,
|
|
);
|
|
}
|
|
this.logger.log(`Deleted file ${uuid}`, 'deleteFile');
|
|
} catch (e) {
|
|
this.logger.error(
|
|
`error: ${(e as Error).message}`,
|
|
(e as Error).stack,
|
|
'deleteFile',
|
|
);
|
|
throw new MediaBackendError(`Could not delete file ${uuid}`);
|
|
}
|
|
}
|
|
|
|
getFileUrl(uuid: string, _: unknown): Promise<string> {
|
|
const blockBlobClient: BlockBlobClient =
|
|
this.client.getBlockBlobClient(uuid);
|
|
const blobSAS = generateBlobSASQueryParameters(
|
|
{
|
|
containerName: this.config.container,
|
|
blobName: uuid,
|
|
permissions: BlobSASPermissions.parse('r'),
|
|
expiresOn: new Date(new Date().valueOf() + 3600 * 1000),
|
|
},
|
|
this.credential,
|
|
);
|
|
return Promise.resolve(`${blockBlobClient.url}?${blobSAS.toString()}`);
|
|
}
|
|
}
|