postiz/libraries/nestjs-libraries/src/integrations/social.abstract.ts

177 lines
3.8 KiB
TypeScript

import { timer } from '@gitroom/helpers/utils/timer';
import { Integration } from '@prisma/client';
import { ApplicationFailure } from '@temporalio/activity';
export class RefreshToken extends ApplicationFailure {
constructor(identifier: string, json: string, body: BodyInit, message = '') {
super(message, 'refresh_token', true, [
{
identifier,
json,
body,
},
]);
}
}
export class BadBody extends ApplicationFailure {
constructor(identifier: string, json: string, body: BodyInit, message = '') {
super(message, 'bad_body', true, [
{
identifier,
json,
body,
},
]);
}
}
export class NotEnoughScopes {
constructor(public message = 'Not enough scopes') {}
}
export abstract class SocialAbstract {
abstract identifier: string;
maxConcurrentJob = 1;
public handleErrors(
body: string
):
| { type: 'refresh-token' | 'bad-body' | 'retry'; value: string }
| undefined {
return undefined;
}
public async mention(
token: string,
d: { query: string },
id: string,
integration: Integration
): Promise<
| { id: string; label: string; image: string; doNotCache?: boolean }[]
| { none: true }
> {
return { none: true };
}
async runInConcurrent<T>(
func: (...args: any[]) => Promise<T>,
ignoreConcurrency?: boolean
) {
let value: any;
try {
value = await func();
} catch (err) {
const handle = this.handleErrors(JSON.stringify(err));
value = { err: true, value: 'Unknown Error', ...(handle || {}) };
}
if (value && value?.err && value?.value) {
if (value.type === 'refresh-token') {
throw new RefreshToken(
'',
JSON.stringify({}),
{} as any,
value.value || ''
);
}
throw new BadBody('', JSON.stringify({}), {} as any, value.value || '');
}
return value;
}
async fetch(
url: string,
options: RequestInit = {},
identifier = '',
totalRetries = 0,
ignoreConcurrency = false
): Promise<Response> {
const request = await fetch(url, options);
if (request.status === 200 || request.status === 201) {
return request;
}
if (totalRetries > 2) {
throw new BadBody(identifier, '{}', options.body || '{}');
}
let json = '{}';
try {
json = await request.text();
} catch (err) {
json = '{}';
}
if (
request.status === 429 ||
request.status === 500 ||
json.includes('rate_limit_exceeded') ||
json.includes('Rate limit')
) {
await timer(5000);
return this.fetch(
url,
options,
identifier,
totalRetries + 1,
ignoreConcurrency
);
}
const handleError = this.handleErrors(json || '{}');
if (handleError?.type === 'retry') {
await timer(5000);
return this.fetch(
url,
options,
identifier,
totalRetries + 1,
ignoreConcurrency
);
}
if (
request.status === 401 &&
(handleError?.type === 'refresh-token' || !handleError)
) {
throw new RefreshToken(
identifier,
json,
options.body!,
handleError?.value
);
}
throw new BadBody(
identifier,
json,
options.body!,
handleError?.value || ''
);
}
checkScopes(required: string[], got: string | string[]) {
if (Array.isArray(got)) {
if (!required.every((scope) => got.includes(scope))) {
throw new NotEnoughScopes();
}
return true;
}
const newGot = decodeURIComponent(got);
const splitType = newGot.indexOf(',') > -1 ? ',' : ' ';
const gotArray = newGot.split(splitType);
if (!required.every((scope) => gotArray.includes(scope))) {
throw new NotEnoughScopes();
}
return true;
}
}