ImgurBackend: Add Imgur MediaBackend

Add node-fetch dependency.
This was chosen as other libs we use already use node-fetch.

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2021-02-26 16:16:00 +01:00 committed by David Mehren
parent c64ca9c012
commit a00a093a14
No known key found for this signature in database
GPG key ID: 185982BA4C42B7C3
5 changed files with 100 additions and 1 deletions

View file

@ -34,6 +34,7 @@
"@nestjs/typeorm": "7.1.5", "@nestjs/typeorm": "7.1.5",
"@types/bcrypt": "3.0.0", "@types/bcrypt": "3.0.0",
"@types/cron": "1.7.2", "@types/cron": "1.7.2",
"@types/node-fetch": "^2.5.8",
"@types/passport-http-bearer": "1.0.36", "@types/passport-http-bearer": "1.0.36",
"bcrypt": "5.0.1", "bcrypt": "5.0.1",
"class-transformer": "0.4.0", "class-transformer": "0.4.0",
@ -43,6 +44,7 @@
"file-type": "16.2.0", "file-type": "16.2.0",
"joi": "17.4.0", "joi": "17.4.0",
"nest-router": "1.0.9", "nest-router": "1.0.9",
"node-fetch": "^2.6.1",
"passport": "0.4.1", "passport": "0.4.1",
"passport-http-bearer": "1.0.1", "passport-http-bearer": "1.0.1",
"raw-body": "2.4.1", "raw-body": "2.4.1",

View file

@ -0,0 +1,83 @@
/*
* 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 fetch from 'node-fetch';
import { URLSearchParams } from 'url';
import { MediaBackendError } from '../../errors/errors';
@Injectable()
export class ImgurBackend implements MediaBackend {
private config: MediaConfig['backend']['imgur'];
constructor(
private readonly logger: ConsoleLoggerService,
@Inject(mediaConfiguration.KEY)
private mediaConfig: MediaConfig,
) {
this.logger.setContext(ImgurBackend.name);
this.config = mediaConfig.backend.imgur;
}
async saveFile(
buffer: Buffer,
fileName: string,
): Promise<[string, BackendData]> {
const params = new URLSearchParams();
params.append('image', buffer.toString('base64'));
params.append('type', 'base64');
try {
const result = await fetch('https://api.imgur.com/3/image', {
method: 'POST',
body: params,
headers: { Authorization: `Client-ID ${this.config.clientID}` },
})
.then(ImgurBackend.checkStatus)
.then((res) => res.json());
this.logger.debug(`Response: ${JSON.stringify(result)}`, 'saveFile');
this.logger.log(`Uploaded ${fileName}`, 'saveFile');
return [result.data.link, result.data.deletehash];
} catch (e) {
this.logger.error(`error: ${e.message}`, e.stack, 'saveFile');
throw new MediaBackendError(`Could not save '${fileName}' on imgur`);
}
}
async deleteFile(fileName: string, backendData: BackendData): Promise<void> {
if (backendData === null) {
throw new Error();
}
try {
const result = await fetch(
`https://api.imgur.com/3/image/${backendData}`,
{
method: 'POST',
headers: { Authorization: `Client-ID ${this.config.clientID}` },
},
).then(ImgurBackend.checkStatus);
this.logger.debug(`Response: ${result}`, 'saveFile');
this.logger.log(`Deleted ${fileName}`, 'deleteFile');
return;
} catch (e) {
this.logger.error(`error: ${e.message}`, e.stack, 'deleteFile');
throw new MediaBackendError(`Could not delete '${fileName}' on imgur`);
}
}
private static checkStatus(res) {
if (res.ok) {
// res.status >= 200 && res.status < 300
return res;
} else {
throw new MediaBackendError(res.statusText);
}
}
}

View file

@ -13,6 +13,7 @@ import { UsersModule } from '../users/users.module';
import { FilesystemBackend } from './backends/filesystem-backend'; import { FilesystemBackend } from './backends/filesystem-backend';
import { MediaUpload } from './media-upload.entity'; import { MediaUpload } from './media-upload.entity';
import { MediaService } from './media.service'; import { MediaService } from './media.service';
import { ImgurBackend } from './backends/imgur-backend';
@Module({ @Module({
imports: [ imports: [
@ -22,7 +23,7 @@ import { MediaService } from './media.service';
LoggerModule, LoggerModule,
ConfigModule, ConfigModule,
], ],
providers: [MediaService, FilesystemBackend], providers: [MediaService, FilesystemBackend, ImgurBackend],
exports: [MediaService], exports: [MediaService],
}) })
export class MediaModule {} export class MediaModule {}

View file

@ -19,6 +19,7 @@ import { FilesystemBackend } from './backends/filesystem-backend';
import { MediaBackend } from './media-backend.interface'; import { MediaBackend } from './media-backend.interface';
import { MediaUpload } from './media-upload.entity'; import { MediaUpload } from './media-upload.entity';
import { MediaUploadUrlDto } from './media-upload-url.dto'; import { MediaUploadUrlDto } from './media-upload-url.dto';
import { ImgurBackend } from './backends/imgur-backend';
@Injectable() @Injectable()
export class MediaService { export class MediaService {
@ -158,6 +159,8 @@ export class MediaService {
switch (this.mediaConfig.backend.use) { switch (this.mediaConfig.backend.use) {
case 'filesystem': case 'filesystem':
return BackendType.FILESYSTEM; return BackendType.FILESYSTEM;
case 'imgur':
return BackendType.IMGUR;
} }
} }
@ -165,6 +168,8 @@ export class MediaService {
switch (type) { switch (type) {
case BackendType.FILESYSTEM: case BackendType.FILESYSTEM:
return this.moduleRef.get(FilesystemBackend); return this.moduleRef.get(FilesystemBackend);
case BackendType.IMGUR:
return this.moduleRef.get(ImgurBackend);
} }
} }

View file

@ -1033,6 +1033,14 @@
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
"@types/node-fetch@^2.5.8":
version "2.5.8"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*": "@types/node@*":
version "14.14.28" version "14.14.28"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b"