mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-04-09 11:37:05 +00:00
Merge pull request #961 from hedgedoc/mediaBackend/imgur
This commit is contained in:
commit
b774958593
7 changed files with 124 additions and 1 deletions
|
@ -135,6 +135,7 @@ entity "media_upload" {
|
|||
*noteId : uuid <<FK note>>
|
||||
*userId : uuid <<FK user>>
|
||||
*backendType: text
|
||||
*fileUrl: text
|
||||
backendData: text
|
||||
*createdAt : date
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"@nestjs/typeorm": "7.1.5",
|
||||
"@types/bcrypt": "3.0.0",
|
||||
"@types/cron": "1.7.2",
|
||||
"@types/node-fetch": "^2.5.8",
|
||||
"@types/passport-http-bearer": "1.0.36",
|
||||
"bcrypt": "5.0.1",
|
||||
"class-transformer": "0.4.0",
|
||||
|
@ -43,6 +44,7 @@
|
|||
"file-type": "16.2.0",
|
||||
"joi": "17.4.0",
|
||||
"nest-router": "1.0.9",
|
||||
"node-fetch": "^2.6.1",
|
||||
"passport": "0.4.1",
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"raw-body": "2.4.1",
|
||||
|
|
102
src/media/backends/imgur-backend.ts
Normal file
102
src/media/backends/imgur-backend.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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, { Response } from 'node-fetch';
|
||||
import { URLSearchParams } from 'url';
|
||||
import { MediaBackendError } from '../../errors/errors';
|
||||
|
||||
type UploadResult = {
|
||||
data: {
|
||||
link: string;
|
||||
deletehash: string;
|
||||
};
|
||||
};
|
||||
|
||||
@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,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
headers: { Authorization: `Client-ID ${this.config.clientID}` },
|
||||
})
|
||||
.then((res) => ImgurBackend.checkStatus(res))
|
||||
.then((res) => res.json())) as UploadResult;
|
||||
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 as Error).message}`,
|
||||
(e as Error).stack,
|
||||
'saveFile',
|
||||
);
|
||||
throw new MediaBackendError(`Could not save '${fileName}' on imgur`);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(fileName: string, backendData: BackendData): Promise<void> {
|
||||
if (backendData === null) {
|
||||
throw new MediaBackendError(
|
||||
`We don't have any delete tokens for '${fileName}' and therefore can't delete this image on imgur`,
|
||||
);
|
||||
}
|
||||
try {
|
||||
const result = await fetch(
|
||||
`https://api.imgur.com/3/image/${backendData}`,
|
||||
{
|
||||
method: 'POST',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
headers: { Authorization: `Client-ID ${this.config.clientID}` },
|
||||
},
|
||||
).then((res) => ImgurBackend.checkStatus(res));
|
||||
this.logger.debug(`Response: ${result.toString()}`, 'saveFile');
|
||||
this.logger.log(`Deleted ${fileName}`, 'deleteFile');
|
||||
return;
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
`error: ${(e as Error).message}`,
|
||||
(e as Error).stack,
|
||||
'deleteFile',
|
||||
);
|
||||
throw new MediaBackendError(`Could not delete '${fileName}' on imgur`);
|
||||
}
|
||||
}
|
||||
|
||||
private static checkStatus(res: Response): Response {
|
||||
if (res.ok) {
|
||||
// res.status >= 200 && res.status < 300
|
||||
return res;
|
||||
} else {
|
||||
throw new MediaBackendError(res.statusText);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,9 @@ export class MediaUpload {
|
|||
})
|
||||
backendType: string;
|
||||
|
||||
@Column()
|
||||
fileUrl: string;
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
|
|
|
@ -13,6 +13,7 @@ import { UsersModule } from '../users/users.module';
|
|||
import { FilesystemBackend } from './backends/filesystem-backend';
|
||||
import { MediaUpload } from './media-upload.entity';
|
||||
import { MediaService } from './media.service';
|
||||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -22,7 +23,7 @@ import { MediaService } from './media.service';
|
|||
LoggerModule,
|
||||
ConfigModule,
|
||||
],
|
||||
providers: [MediaService, FilesystemBackend],
|
||||
providers: [MediaService, FilesystemBackend, ImgurBackend],
|
||||
exports: [MediaService],
|
||||
})
|
||||
export class MediaModule {}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { FilesystemBackend } from './backends/filesystem-backend';
|
|||
import { MediaBackend } from './media-backend.interface';
|
||||
import { MediaUpload } from './media-upload.entity';
|
||||
import { MediaUploadUrlDto } from './media-upload-url.dto';
|
||||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
|
||||
@Injectable()
|
||||
export class MediaService {
|
||||
|
@ -100,6 +101,7 @@ export class MediaService {
|
|||
mediaUpload.id,
|
||||
);
|
||||
mediaUpload.backendData = backendData;
|
||||
mediaUpload.fileUrl = url;
|
||||
await this.mediaUploadRepository.save(mediaUpload);
|
||||
return url;
|
||||
}
|
||||
|
@ -157,6 +159,8 @@ export class MediaService {
|
|||
switch (this.mediaConfig.backend.use) {
|
||||
case 'filesystem':
|
||||
return BackendType.FILESYSTEM;
|
||||
case 'imgur':
|
||||
return BackendType.IMGUR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +168,8 @@ export class MediaService {
|
|||
switch (type) {
|
||||
case BackendType.FILESYSTEM:
|
||||
return this.moduleRef.get(FilesystemBackend);
|
||||
case BackendType.IMGUR:
|
||||
return this.moduleRef.get(ImgurBackend);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1033,6 +1033,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
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@*":
|
||||
version "14.14.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b"
|
||||
|
|
Loading…
Add table
Reference in a new issue