mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2026-06-23 04:10:17 +00:00
a880864b2c
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
127 lines
3.9 KiB
TypeScript
127 lines
3.9 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import { MediaBackendType } from '@hedgedoc/commons';
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import fetch, { Response } from 'node-fetch';
|
|
import { URLSearchParams } from 'url';
|
|
|
|
import mediaConfiguration, {
|
|
ImgurMediaConfig,
|
|
MediaConfig,
|
|
} from '../../config/media.config';
|
|
import { MediaBackendError } from '../../errors/errors';
|
|
import { ConsoleLoggerService } from '../../logger/console-logger.service';
|
|
import { MediaBackend } from '../media-backend.interface';
|
|
|
|
type UploadResult = {
|
|
data: {
|
|
link: string;
|
|
deletehash: string | null;
|
|
};
|
|
};
|
|
|
|
interface ImgurBackendData {
|
|
url: string;
|
|
deleteHash: string | null;
|
|
}
|
|
|
|
@Injectable()
|
|
export class ImgurBackend implements MediaBackend {
|
|
private config: ImgurMediaConfig['imgur'];
|
|
|
|
constructor(
|
|
private readonly logger: ConsoleLoggerService,
|
|
@Inject(mediaConfiguration.KEY)
|
|
private mediaConfig: MediaConfig,
|
|
) {
|
|
this.logger.setContext(ImgurBackend.name);
|
|
// only create the backend if imgur is configured
|
|
if (this.mediaConfig.backend.type !== MediaBackendType.IMGUR) {
|
|
return;
|
|
}
|
|
this.config = this.mediaConfig.backend.imgur;
|
|
}
|
|
|
|
async saveFile(uuid: string, buffer: Buffer): Promise<string> {
|
|
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,
|
|
// oxlint-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 file ${uuid}`, 'saveFile');
|
|
const backendData: ImgurBackendData = {
|
|
url: result.data.link,
|
|
deleteHash: result.data.deletehash,
|
|
};
|
|
return JSON.stringify(backendData);
|
|
} 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, jsonBackendData: string): Promise<void> {
|
|
const backendData = JSON.parse(jsonBackendData) as ImgurBackendData;
|
|
if (backendData.deleteHash === null) {
|
|
throw new MediaBackendError(
|
|
`We don't have any delete tokens for file ${uuid} and therefore can't delete this image`,
|
|
);
|
|
}
|
|
try {
|
|
const result = await fetch(
|
|
`https://api.imgur.com/3/image/${backendData.deleteHash}`,
|
|
{
|
|
method: 'DELETE',
|
|
// oxlint-disable-next-line @typescript-eslint/naming-convention
|
|
headers: { Authorization: `Client-ID ${this.config.clientId}` },
|
|
},
|
|
);
|
|
ImgurBackend.checkStatus(result);
|
|
// oxlint-disable-next-line @typescript-eslint/no-base-to-string
|
|
this.logger.log(`Deleted file ${uuid}`, 'deleteFile');
|
|
return;
|
|
} 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, backendData: string | null): Promise<string> {
|
|
if (backendData === null) {
|
|
throw new MediaBackendError(
|
|
`We don't have any data for file ${uuid} and therefore can't get the url of this image`,
|
|
);
|
|
}
|
|
const data = JSON.parse(backendData) as ImgurBackendData;
|
|
return Promise.resolve(data.url);
|
|
}
|
|
|
|
private static checkStatus(res: Response): Response {
|
|
if (res.ok) {
|
|
// res.status >= 200 && res.status < 300
|
|
return res;
|
|
} else {
|
|
throw new MediaBackendError(res.statusText);
|
|
}
|
|
}
|
|
}
|