postiz/libraries/nestjs-libraries/src/integrations/social/medium.provider.ts

137 lines
3.4 KiB
TypeScript

import {
AuthTokenDetails,
PostDetails,
PostResponse,
SocialProvider,
} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
import dayjs from 'dayjs';
import { Integration } from '@prisma/client';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
export class MediumProvider extends SocialAbstract implements SocialProvider {
override maxConcurrentJob = 3; // Medium has lenient publishing limits
identifier = 'medium';
name = 'Medium';
isBetweenSteps = false;
scopes = [] as string[];
editor = 'markdown' as const;
async generateAuthUrl() {
const state = makeId(6);
return {
url: '',
codeVerifier: makeId(10),
state,
};
}
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
return {
refreshToken: '',
expiresIn: 0,
accessToken: '',
id: '',
name: '',
picture: '',
username: '',
};
}
async customFields() {
return [
{
key: 'apiKey',
label: 'API key',
validation: `/^.{3,}$/`,
type: 'password' as const,
},
];
}
async authenticate(params: {
code: string;
codeVerifier: string;
refresh?: string;
}) {
const body = JSON.parse(Buffer.from(params.code, 'base64').toString());
try {
const {
data: { name, id, imageUrl, username },
} = await (
await fetch('https://api.medium.com/v1/me', {
headers: {
Authorization: `Bearer ${body.apiKey}`,
},
})
).json();
return {
refreshToken: '',
expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(),
accessToken: body.apiKey,
id,
name,
picture: imageUrl || '',
username,
};
} catch (err) {
return 'Invalid credentials';
}
}
async publications(accessToken: string, _: any, id: string) {
const { data } = await (
await fetch(`https://api.medium.com/v1/users/${id}/publications`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
).json();
return data;
}
async post(
id: string,
accessToken: string,
postDetails: PostDetails[],
integration: Integration
): Promise<PostResponse[]> {
const { settings } = postDetails?.[0] || { settings: {} };
const { data } = await (
await fetch(
settings?.publication
? `https://api.medium.com/v1/publications/${settings?.publication}/posts`
: `https://api.medium.com/v1/users/${id}/posts`,
{
method: 'POST',
body: JSON.stringify({
title: settings.title,
contentFormat: 'markdown',
content: postDetails?.[0].message,
...(settings.canonical ? { canonicalUrl: settings.canonical } : {}),
...(settings?.tags?.length
? { tags: settings?.tags?.map((p: any) => p.value) }
: {}),
publishStatus: settings?.publication ? 'draft' : 'public',
}),
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
}
)
).json();
return [
{
id: postDetails?.[0].id,
status: 'completed',
postId: data.id,
releaseURL: data.url,
},
];
}
}