Add MediaService

This service is responsible for operations regarding uploaded media. It should perform save, get and delete operations with the configured backend.
The service also checks, if the mime type of the uploaded media is allowed.

Signed-off-by: David Mehren <git@herrmehren.de>
This commit is contained in:
David Mehren 2020-10-16 22:35:53 +02:00
parent 23ba2026cc
commit c0cbcc863a
No known key found for this signature in database
GPG key ID: 185982BA4C42B7C3
5 changed files with 109 additions and 1 deletions

View file

@ -30,6 +30,7 @@
"class-transformer": "^0.2.3",
"class-validator": "^0.12.2",
"connect-typeorm": "^1.1.4",
"file-type": "^15.0.1",
"raw-body": "^2.4.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",

View file

@ -1,8 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NotesModule } from '../notes/notes.module';
import { UsersModule } from '../users/users.module';
import { MediaUpload } from './media-upload.entity';
import { MediaService } from './media.service';
@Module({
imports: [TypeOrmModule.forFeature([MediaUpload])],
imports: [TypeOrmModule.forFeature([MediaUpload]), NotesModule, UsersModule],
providers: [MediaService],
exports: [MediaService],
})
export class MediaModule {}

View file

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MediaService } from './media.service';
describe('MediaService', () => {
let service: MediaService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MediaService],
}).compile();
service = module.get<MediaService>(MediaService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View file

@ -0,0 +1,52 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as FileType from 'file-type';
import { Repository } from 'typeorm';
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 { MediaUpload } from './media-upload.entity';
import { MulterFile } from './multer-file.interface';
@Injectable()
export class MediaService {
constructor(
@InjectRepository(MediaUpload)
private mediaUploadRepository: Repository<MediaUpload>,
private notesService: NotesService,
private usersService: UsersService,
) {}
public async saveFile(file: MulterFile, username: string, noteId: string) {
const note = await this.notesService.getNoteByIdOrAlias(noteId);
const user = await this.usersService.getUserByUsername(username);
const fileTypeResult = await FileType.fromBuffer(file.buffer);
if (!fileTypeResult) {
throw new Error('Could not detect file type.');
}
if (!MediaService.isAllowedMimeType(fileTypeResult.mime)) {
throw new Error('MIME type not allowed');
}
//TODO: Choose backend according to config
const mediaUpload = MediaUpload.create(
note,
user,
fileTypeResult.ext,
BackendType.FILEYSTEM,
);
const backend = new FilesystemBackend();
const [url, backendData] = await backend.saveFile(
file.buffer,
mediaUpload.id,
);
mediaUpload.backendData = backendData;
await this.mediaUploadRepository.save(mediaUpload);
return url;
}
private static isAllowedMimeType(mimeType: string): boolean {
//TODO: Which mimetypes are allowed?
return true;
}
}

View file

@ -0,0 +1,32 @@
import { Readable } from 'stream';
// Type from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/multer/index.d.ts
export interface MulterFile {
/** Name of the form field associated with this file. */
fieldname: string;
/** Name of the file on the uploader's computer. */
originalname: string;
/**
* Value of the `Content-Transfer-Encoding` header for this file.
* @deprecated since July 2015
* @see RFC 7578, Section 4.7
*/
encoding: string;
/** Value of the `Content-Type` header for this file. */
mimetype: string;
/** Size of the file in bytes. */
size: number;
/**
* A readable stream of this file. Only available to the `_handleFile`
* callback for custom `StorageEngine`s.
*/
stream: Readable;
/** `DiskStorage` only: Directory to which this file has been uploaded. */
destination: string;
/** `DiskStorage` only: Name of this file within `destination`. */
filename: string;
/** `DiskStorage` only: Full path to the uploaded file. */
path: string;
/** `MemoryStorage` only: A Buffer containing the entire file. */
buffer: Buffer;
}