hedgedoc/src/media/media.service.ts
David Mehren 75b6d3cc2b
MediaService: Get media backend from configuration
Signed-off-by: David Mehren <git@herrmehren.de>
2021-01-13 21:45:23 +01:00

135 lines
4.3 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { InjectRepository } from '@nestjs/typeorm';
import * as FileType from 'file-type';
import applicationConfig, { AppConfig } from 'src/config/app.config';
import { Repository } from 'typeorm';
import { ClientError, NotInDBError, PermissionError } from '../errors/errors';
import { ConsoleLoggerService } from '../logger/console-logger.service';
import { NotesService } from '../notes/notes.service';
import { UsersService } from '../users/users.service';
import { BackendType } from './backends/backend-type.enum';
import { FilesystemBackend } from './backends/filesystem-backend';
import { MediaBackend } from './media-backend.interface';
import { MediaUpload } from './media-upload.entity';
@Injectable()
export class MediaService {
mediaBackend: MediaBackend;
mediaBackendType: BackendType;
constructor(
private readonly logger: ConsoleLoggerService,
@InjectRepository(MediaUpload)
private mediaUploadRepository: Repository<MediaUpload>,
private notesService: NotesService,
private usersService: UsersService,
private moduleRef: ModuleRef,
@Inject(applicationConfig.KEY)
private appConfig: AppConfig,
) {
this.logger.setContext(MediaService.name);
this.mediaBackendType = this.chooseBackendType();
this.mediaBackend = this.getBackendFromType(this.mediaBackendType);
}
private static isAllowedMimeType(mimeType: string): boolean {
const allowedTypes = [
'application/pdf',
'image/apng',
'image/bmp',
'image/gif',
'image/heif',
'image/heic',
'image/heif-sequence',
'image/heic-sequence',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/tiff',
'image/webp',
];
return allowedTypes.includes(mimeType);
}
public async saveFile(fileBuffer: Buffer, username: string, noteId: string) {
this.logger.debug(
`Saving file for note '${noteId}' and user '${username}'`,
'saveFile',
);
const note = await this.notesService.getNoteByIdOrAlias(noteId);
const user = await this.usersService.getUserByUsername(username);
const fileTypeResult = await FileType.fromBuffer(fileBuffer);
if (!fileTypeResult) {
throw new ClientError('Could not detect file type.');
}
if (!MediaService.isAllowedMimeType(fileTypeResult.mime)) {
throw new ClientError('MIME type not allowed.');
}
const mediaUpload = MediaUpload.create(
note,
user,
fileTypeResult.ext,
this.mediaBackendType,
);
this.logger.debug(`Generated filename: '${mediaUpload.id}'`, 'saveFile');
const [url, backendData] = await this.mediaBackend.saveFile(
fileBuffer,
mediaUpload.id,
);
mediaUpload.backendData = backendData;
await this.mediaUploadRepository.save(mediaUpload);
return url;
}
public async deleteFile(filename: string, username: string) {
this.logger.debug(
`Deleting '${filename}' for user '${username}'`,
'deleteFile',
);
const mediaUpload = await this.findUploadByFilename(filename);
if (mediaUpload.user.userName !== username) {
this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`,
'deleteFile',
);
throw new PermissionError(
`File '${filename}' is not owned by '${username}'`,
);
}
await this.mediaBackend.deleteFile(filename, mediaUpload.backendData);
await this.mediaUploadRepository.remove(mediaUpload);
}
public async findUploadByFilename(filename: string): Promise<MediaUpload> {
const mediaUpload = await this.mediaUploadRepository.findOne(filename, {
relations: ['user'],
});
if (mediaUpload === undefined) {
throw new NotInDBError(
`MediaUpload with filename '${filename}' not found`,
);
}
return mediaUpload;
}
private chooseBackendType(): BackendType {
switch (this.appConfig.media.backend.use) {
case 'filesystem':
return BackendType.FILEYSTEM;
}
}
private getBackendFromType(type: BackendType): MediaBackend {
switch (type) {
case BackendType.FILEYSTEM:
return this.moduleRef.get(FilesystemBackend);
}
}
}