feat: new provider - listmonk
This commit is contained in:
parent
ba6d035839
commit
df0077771a
|
|
@ -50,6 +50,10 @@ GITHUB_CLIENT_ID=""
|
||||||
GITHUB_CLIENT_SECRET=""
|
GITHUB_CLIENT_SECRET=""
|
||||||
BEEHIIVE_API_KEY=""
|
BEEHIIVE_API_KEY=""
|
||||||
BEEHIIVE_PUBLICATION_ID=""
|
BEEHIIVE_PUBLICATION_ID=""
|
||||||
|
LISTMONK_DOMAIN=""
|
||||||
|
LISTMONK_USER=""
|
||||||
|
LISTMONK_API_KEY=""
|
||||||
|
LISTMONK_LIST_ID=""
|
||||||
THREADS_APP_ID=""
|
THREADS_APP_ID=""
|
||||||
THREADS_APP_SECRET=""
|
THREADS_APP_SECRET=""
|
||||||
FACEBOOK_APP_ID=""
|
FACEBOOK_APP_ID=""
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/o
|
||||||
import { AuthService as AuthChecker } from '@gitroom/helpers/auth/auth.service';
|
import { AuthService as AuthChecker } from '@gitroom/helpers/auth/auth.service';
|
||||||
import { ProvidersFactory } from '@gitroom/backend/services/auth/providers/providers.factory';
|
import { ProvidersFactory } from '@gitroom/backend/services/auth/providers/providers.factory';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { NewsletterService } from '@gitroom/nestjs-libraries/services/newsletter.service';
|
|
||||||
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
|
import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service';
|
||||||
import { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto';
|
import { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto';
|
||||||
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
|
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
|
||||||
|
import { NewsletterService } from '@gitroom/nestjs-libraries/newsletter/newsletter.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -258,6 +258,7 @@ export const CustomVariables: FC<{
|
||||||
});
|
});
|
||||||
const submit = useCallback(
|
const submit = useCallback(
|
||||||
async (data: FieldValues) => {
|
async (data: FieldValues) => {
|
||||||
|
modals.closeAll();
|
||||||
gotoUrl(
|
gotoUrl(
|
||||||
`/integrations/social/${identifier}?state=nostate&code=${Buffer.from(
|
`/integrations/social/${identifier}?state=nostate&code=${Buffer.from(
|
||||||
JSON.stringify(data)
|
JSON.stringify(data)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PostComment,
|
||||||
|
withProvider,
|
||||||
|
} from '@gitroom/frontend/components/new-launch/providers/high.order.provider';
|
||||||
|
import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto';
|
||||||
|
import { Input } from '@gitroom/react/form/input';
|
||||||
|
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
|
||||||
|
import { SelectList } from '@gitroom/frontend/components/new-launch/providers/listmonk/select.list';
|
||||||
|
import { SelectTemplates } from '@gitroom/frontend/components/new-launch/providers/listmonk/select.templates';
|
||||||
|
|
||||||
|
const SettingsComponent = () => {
|
||||||
|
const form = useSettings();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Input label="Subject" {...form.register('subject')} />
|
||||||
|
<Input label="Preview" {...form.register('preview')} />
|
||||||
|
<SelectList {...form.register('list')} />
|
||||||
|
<SelectTemplates {...form.register('templates')} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProvider({
|
||||||
|
postComment: PostComment.POST,
|
||||||
|
minimumCharacters: [],
|
||||||
|
SettingsComponent: SettingsComponent,
|
||||||
|
CustomPreviewComponent: undefined,
|
||||||
|
dto: ListmonkDto,
|
||||||
|
checkValidity: async (posts) => {
|
||||||
|
if (
|
||||||
|
posts.some(
|
||||||
|
(p) => p.some((a) => a.path.indexOf('mp4') > -1) && p.length > 1
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return 'You can only upload one video per post.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posts.some((p) => p.length > 4)) {
|
||||||
|
return 'There can be maximum 4 pictures in a post.';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
maximumCharacters: 300000,
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Select } from '@gitroom/react/form/select';
|
||||||
|
import { useT } from '@gitroom/react/translation/get.transation.service.client';
|
||||||
|
import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function';
|
||||||
|
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
|
||||||
|
export const SelectList: FC<{
|
||||||
|
name: string;
|
||||||
|
onChange: (event: {
|
||||||
|
target: {
|
||||||
|
value: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}) => void;
|
||||||
|
}> = (props) => {
|
||||||
|
const { onChange, name } = props;
|
||||||
|
const t = useT();
|
||||||
|
const customFunc = useCustomProviderFunction();
|
||||||
|
const [orgs, setOrgs] = useState([]);
|
||||||
|
const { getValues } = useSettings();
|
||||||
|
const [currentMedia, setCurrentMedia] = useState<string | undefined>();
|
||||||
|
const onChangeInner = (event: {
|
||||||
|
target: {
|
||||||
|
value: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
|
setCurrentMedia(event.target.value);
|
||||||
|
onChange(event);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
customFunc.get('list').then((data) => setOrgs(data));
|
||||||
|
const settings = getValues()[props.name];
|
||||||
|
if (settings) {
|
||||||
|
setCurrentMedia(settings);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
if (!orgs.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
name={name}
|
||||||
|
label="Select List"
|
||||||
|
onChange={onChangeInner}
|
||||||
|
value={currentMedia}
|
||||||
|
>
|
||||||
|
<option value="">{t('select_1', '--Select--')}</option>
|
||||||
|
{orgs.map((org: any) => (
|
||||||
|
<option key={org.id} value={org.id}>
|
||||||
|
{org.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Select } from '@gitroom/react/form/select';
|
||||||
|
import { useT } from '@gitroom/react/translation/get.transation.service.client';
|
||||||
|
import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function';
|
||||||
|
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
|
||||||
|
export const SelectTemplates: FC<{
|
||||||
|
name: string;
|
||||||
|
onChange: (event: {
|
||||||
|
target: {
|
||||||
|
value: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}) => void;
|
||||||
|
}> = (props) => {
|
||||||
|
const { onChange, name } = props;
|
||||||
|
const t = useT();
|
||||||
|
const customFunc = useCustomProviderFunction();
|
||||||
|
const [orgs, setOrgs] = useState([]);
|
||||||
|
const { getValues } = useSettings();
|
||||||
|
const [currentMedia, setCurrentMedia] = useState<string | undefined>();
|
||||||
|
const onChangeInner = (event: {
|
||||||
|
target: {
|
||||||
|
value: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}) => {
|
||||||
|
setCurrentMedia(event.target.value);
|
||||||
|
onChange(event);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
customFunc.get('templates').then((data) => setOrgs(data));
|
||||||
|
const settings = getValues()[props.name];
|
||||||
|
if (settings) {
|
||||||
|
setCurrentMedia(settings);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
if (!orgs.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
name={name}
|
||||||
|
label="Select Template"
|
||||||
|
onChange={onChangeInner}
|
||||||
|
value={currentMedia}
|
||||||
|
>
|
||||||
|
<option value="">{t('select_1', '--Select--')}</option>
|
||||||
|
{orgs.map((org: any) => (
|
||||||
|
<option key={org.id} value={org.id}>
|
||||||
|
{org.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -31,6 +31,7 @@ import { Button } from '@gitroom/react/form/button';
|
||||||
import { useT } from '@gitroom/react/translation/get.transation.service.client';
|
import { useT } from '@gitroom/react/translation/get.transation.service.client';
|
||||||
import { PostComment } from '@gitroom/frontend/components/new-launch/providers/high.order.provider';
|
import { PostComment } from '@gitroom/frontend/components/new-launch/providers/high.order.provider';
|
||||||
import WordpressProvider from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.provider';
|
import WordpressProvider from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.provider';
|
||||||
|
import ListmonkProvider from '@gitroom/frontend/components/new-launch/providers/listmonk/listmonk.provider';
|
||||||
|
|
||||||
export const Providers = [
|
export const Providers = [
|
||||||
{
|
{
|
||||||
|
|
@ -133,6 +134,10 @@ export const Providers = [
|
||||||
identifier: 'wordpress',
|
identifier: 'wordpress',
|
||||||
component: WordpressProvider,
|
component: WordpressProvider,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
identifier: 'listmonk',
|
||||||
|
component: ListmonkProvider,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
export const ShowAllProviders = forwardRef((props, ref) => {
|
export const ShowAllProviders = forwardRef((props, ref) => {
|
||||||
const { date, current, global, selectedIntegrations, allIntegrations } =
|
const { date, current, global, selectedIntegrations, allIntegrations } =
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { MediumSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/provider
|
||||||
import { DevToSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.settings.dto';
|
import { DevToSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.settings.dto';
|
||||||
import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto';
|
import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto';
|
||||||
import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto';
|
import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto';
|
||||||
|
import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto';
|
||||||
|
|
||||||
export type ProviderExtension<T extends string, M> = { __type: T } & M;
|
export type ProviderExtension<T extends string, M> = { __type: T } & M;
|
||||||
export type AllProvidersSettings =
|
export type AllProvidersSettings =
|
||||||
|
|
@ -34,6 +35,7 @@ export type AllProvidersSettings =
|
||||||
| ProviderExtension<'devto', DevToSettingsDto>
|
| ProviderExtension<'devto', DevToSettingsDto>
|
||||||
| ProviderExtension<'hashnode', HashnodeSettingsDto>
|
| ProviderExtension<'hashnode', HashnodeSettingsDto>
|
||||||
| ProviderExtension<'wordpress', WordpressDto>
|
| ProviderExtension<'wordpress', WordpressDto>
|
||||||
|
| ProviderExtension<'listmonk', ListmonkDto>
|
||||||
| ProviderExtension<'facebook', None>
|
| ProviderExtension<'facebook', None>
|
||||||
| ProviderExtension<'threads', None>
|
| ProviderExtension<'threads', None>
|
||||||
| ProviderExtension<'mastodon', None>
|
| ProviderExtension<'mastodon', None>
|
||||||
|
|
@ -64,6 +66,7 @@ export const allProviders = (setEmpty?: any) => {
|
||||||
{ value: DevToSettingsDto, name: 'devto' },
|
{ value: DevToSettingsDto, name: 'devto' },
|
||||||
{ value: WordpressDto, name: 'wordpress' },
|
{ value: WordpressDto, name: 'wordpress' },
|
||||||
{ value: HashnodeSettingsDto, name: 'hashnode' },
|
{ value: HashnodeSettingsDto, name: 'hashnode' },
|
||||||
|
{ value: ListmonkDto, name: 'listmonk' },
|
||||||
{ value: setEmpty, name: 'facebook' },
|
{ value: setEmpty, name: 'facebook' },
|
||||||
{ value: setEmpty, name: 'threads' },
|
{ value: setEmpty, name: 'threads' },
|
||||||
{ value: setEmpty, name: 'mastodon' },
|
{ value: setEmpty, name: 'mastodon' },
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { IsOptional, IsString, MinLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class ListmonkDto {
|
||||||
|
@IsString()
|
||||||
|
@MinLength(1)
|
||||||
|
subject: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
preview: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
list: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
template: string;
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@ import { TelegramProvider } from '@gitroom/nestjs-libraries/integrations/social/
|
||||||
import { NostrProvider } from '@gitroom/nestjs-libraries/integrations/social/nostr.provider';
|
import { NostrProvider } from '@gitroom/nestjs-libraries/integrations/social/nostr.provider';
|
||||||
import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.provider';
|
import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.provider';
|
||||||
import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider';
|
import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider';
|
||||||
|
import { ListmonkProvider } from '@gitroom/nestjs-libraries/integrations/social/listmonk.provider';
|
||||||
|
|
||||||
export const socialIntegrationList: SocialProvider[] = [
|
export const socialIntegrationList: SocialProvider[] = [
|
||||||
new XProvider(),
|
new XProvider(),
|
||||||
|
|
@ -54,6 +55,7 @@ export const socialIntegrationList: SocialProvider[] = [
|
||||||
new DevToProvider(),
|
new DevToProvider(),
|
||||||
new HashnodeProvider(),
|
new HashnodeProvider(),
|
||||||
new WordpressProvider(),
|
new WordpressProvider(),
|
||||||
|
new ListmonkProvider(),
|
||||||
// new MastodonCustomProvider(),
|
// new MastodonCustomProvider(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
|
||||||
|
import { SocialAbstract } from '../social.abstract';
|
||||||
|
import {
|
||||||
|
AuthTokenDetails,
|
||||||
|
PostDetails,
|
||||||
|
PostResponse,
|
||||||
|
SocialProvider,
|
||||||
|
} from './social.integrations.interface';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { Integration } from '@prisma/client';
|
||||||
|
import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto';
|
||||||
|
import { AuthService } from '@gitroom/helpers/auth/auth.service';
|
||||||
|
import slugify from 'slugify';
|
||||||
|
|
||||||
|
export class ListmonkProvider extends SocialAbstract implements SocialProvider {
|
||||||
|
override maxConcurrentJob = 100; // Bluesky has moderate rate limits
|
||||||
|
identifier = 'listmonk';
|
||||||
|
name = 'ListMonk';
|
||||||
|
isBetweenSteps = false;
|
||||||
|
scopes = [] as string[];
|
||||||
|
editor = 'html' as const;
|
||||||
|
|
||||||
|
async customFields() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'url',
|
||||||
|
label: 'URL',
|
||||||
|
defaultValue: '',
|
||||||
|
validation: `/^(https?:\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?:localhost)|(?:\\d{1,3}(?:\\.\\d{1,3}){3})|(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63})(?::\\d{2,5})?(?:\\/[^\\s?#]*)?(?:\\?[^\\s#]*)?(?:#[^\\s]*)?$/`,
|
||||||
|
type: 'text' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'username',
|
||||||
|
label: 'Username',
|
||||||
|
validation: `/^.+$/`,
|
||||||
|
type: 'text' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'password',
|
||||||
|
label: 'Password',
|
||||||
|
validation: `/^.{3,}$/`,
|
||||||
|
type: 'password' as const,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
|
||||||
|
return {
|
||||||
|
refreshToken: '',
|
||||||
|
expiresIn: 0,
|
||||||
|
accessToken: '',
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
picture: '',
|
||||||
|
username: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateAuthUrl() {
|
||||||
|
const state = makeId(6);
|
||||||
|
return {
|
||||||
|
url: '',
|
||||||
|
codeVerifier: makeId(10),
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async authenticate(params: {
|
||||||
|
code: string;
|
||||||
|
codeVerifier: string;
|
||||||
|
refresh?: string;
|
||||||
|
}) {
|
||||||
|
const body: { url: string; username: string; password: string } =
|
||||||
|
JSON.parse(Buffer.from(params.code, 'base64').toString());
|
||||||
|
|
||||||
|
console.log(body);
|
||||||
|
try {
|
||||||
|
const basic = Buffer.from(body.username + ':' + body.password).toString(
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data } = await (
|
||||||
|
await this.fetch(body.url + '/api/settings', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: 'Basic ' + basic,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
return {
|
||||||
|
refreshToken: basic,
|
||||||
|
expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(),
|
||||||
|
accessToken: basic,
|
||||||
|
id: Buffer.from(body.url).toString('base64'),
|
||||||
|
name: data['app.site_name'],
|
||||||
|
picture: data['app.logo_url'] || '',
|
||||||
|
username: data['app.site_name'],
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return 'Invalid credentials';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async list(
|
||||||
|
token: string,
|
||||||
|
data: any,
|
||||||
|
internalId: string,
|
||||||
|
integration: Integration
|
||||||
|
) {
|
||||||
|
const body: { url: string; username: string; password: string } =
|
||||||
|
JSON.parse(
|
||||||
|
AuthService.fixedDecryption(integration.customInstanceDetails!)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${body.username}:${body.password}`).toString(
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
|
||||||
|
const postTypes = await (
|
||||||
|
await this.fetch(`${body.url}/api/lists`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
return postTypes.data.results.map((p: any) => ({ id: p.id, name: p.name }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async templates(
|
||||||
|
token: string,
|
||||||
|
data: any,
|
||||||
|
internalId: string,
|
||||||
|
integration: Integration
|
||||||
|
) {
|
||||||
|
const body: { url: string; username: string; password: string } =
|
||||||
|
JSON.parse(
|
||||||
|
AuthService.fixedDecryption(integration.customInstanceDetails!)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${body.username}:${body.password}`).toString(
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
|
||||||
|
const postTypes = await (
|
||||||
|
await this.fetch(`${body.url}/api/templates`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ id: 0, name: 'Default' },
|
||||||
|
...postTypes.data.map((p: any) => ({ id: p.id, name: p.name })),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async post(
|
||||||
|
id: string,
|
||||||
|
accessToken: string,
|
||||||
|
postDetails: PostDetails<ListmonkDto>[],
|
||||||
|
integration: Integration
|
||||||
|
): Promise<PostResponse[]> {
|
||||||
|
const body: { url: string; username: string; password: string } =
|
||||||
|
JSON.parse(
|
||||||
|
AuthService.fixedDecryption(integration.customInstanceDetails!)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${body.username}:${body.password}`).toString(
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
|
||||||
|
const sendBody = `
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="hidden-preheader"
|
||||||
|
style="display:none !important; visibility:hidden; opacity:0; overflow:hidden;
|
||||||
|
max-height:0; max-width:0; line-height:1px; font-size:1px; color:transparent;
|
||||||
|
mso-hide:all;">
|
||||||
|
<!-- A short visible decoy (optional): shows as "." or short text in preview -->
|
||||||
|
${postDetails?.[0]?.settings?.preview || ''}
|
||||||
|
<!-- Then invisible padding to eat up preview characters -->
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
<!-- Repeat the trio (zero-width space, zero-width non-joiner, nbsp, BOM) a bunch of times -->
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
​‌ ​‌ ​‌ ​‌ 
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
${postDetails[0].message}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { uuid: postId, id: campaignId },
|
||||||
|
} = await (
|
||||||
|
await this.fetch(body.url + '/api/campaigns', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: slugify(postDetails[0].settings.subject, {
|
||||||
|
lower: true,
|
||||||
|
strict: true,
|
||||||
|
trim: true,
|
||||||
|
}),
|
||||||
|
type: 'regular',
|
||||||
|
content_type: 'html',
|
||||||
|
subject: postDetails[0].settings.subject,
|
||||||
|
lists: [+postDetails[0].settings.list],
|
||||||
|
body: sendBody,
|
||||||
|
...(+postDetails?.[0]?.settings?.template
|
||||||
|
? { template_id: +postDetails[0].settings.template }
|
||||||
|
: {}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
await this.fetch(body.url + `/api/campaigns/${campaignId}/status`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
status: 'running',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
status: 'completed',
|
||||||
|
releaseURL: `${body.url}/api/campaigns/${campaignId}/preview`,
|
||||||
|
postId,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface NewsletterInterface {
|
||||||
|
name: string;
|
||||||
|
register(email: string): Promise<void>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { newsletterProviders } from '@gitroom/nestjs-libraries/newsletter/providers';
|
||||||
|
|
||||||
|
export class NewsletterService {
|
||||||
|
static getProvider() {
|
||||||
|
if (process.env.BEEHIIVE_API_KEY) {
|
||||||
|
return newsletterProviders.find((p) => p.name === 'beehiiv')!;
|
||||||
|
}
|
||||||
|
if (process.env.LISTMONK_API_KEY) {
|
||||||
|
return newsletterProviders.find((p) => p.name === 'listmonk')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsletterProviders.find((p) => p.name === 'empty')!;
|
||||||
|
}
|
||||||
|
static async register(email: string) {
|
||||||
|
if (email.indexOf('@') === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return NewsletterService.getProvider().register(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { BeehiivProvider } from '@gitroom/nestjs-libraries/newsletter/providers/beehiiv.provider';
|
||||||
|
import { EmailEmptyProvider } from '@gitroom/nestjs-libraries/newsletter/providers/email-empty.provider';
|
||||||
|
import { ListmonkProvider } from '@gitroom/nestjs-libraries/newsletter/providers/listmonk.provider';
|
||||||
|
|
||||||
|
export const newsletterProviders = [
|
||||||
|
new BeehiivProvider(),
|
||||||
|
new ListmonkProvider(),
|
||||||
|
new EmailEmptyProvider(),
|
||||||
|
];
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
export class NewsletterService {
|
import { NewsletterInterface } from '@gitroom/nestjs-libraries/newsletter/newsletter.interface';
|
||||||
static async register(email: string) {
|
|
||||||
if (
|
export class BeehiivProvider implements NewsletterInterface {
|
||||||
!process.env.BEEHIIVE_API_KEY ||
|
name = 'beehiiv';
|
||||||
!process.env.BEEHIIVE_PUBLICATION_ID ||
|
async register(email: string) {
|
||||||
process.env.NODE_ENV === 'development' ||
|
|
||||||
email.indexOf('@') === -1
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const body = {
|
const body = {
|
||||||
email,
|
email,
|
||||||
reactivate_existing: false,
|
reactivate_existing: false,
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { NewsletterInterface } from '@gitroom/nestjs-libraries/newsletter/newsletter.interface';
|
||||||
|
|
||||||
|
export class EmailEmptyProvider implements NewsletterInterface {
|
||||||
|
name = 'empty';
|
||||||
|
async register(email: string) {
|
||||||
|
console.log('Could have registered to newsletter:', email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { NewsletterInterface } from '@gitroom/nestjs-libraries/newsletter/newsletter.interface';
|
||||||
|
|
||||||
|
export class ListmonkProvider implements NewsletterInterface {
|
||||||
|
name = 'listmonk';
|
||||||
|
async register(email: string) {
|
||||||
|
const body = {
|
||||||
|
email,
|
||||||
|
status: 'enabled',
|
||||||
|
lists: [+process.env.LISTMONK_LIST_ID].filter((f) => f),
|
||||||
|
};
|
||||||
|
|
||||||
|
const authString = `${process.env.LISTMONK_USER}:${process.env.LISTMONK_API_KEY}`;
|
||||||
|
const headers = new Headers();
|
||||||
|
headers.set('Content-Type', 'application/json');
|
||||||
|
headers.set('Accept', 'application/json');
|
||||||
|
headers.set(
|
||||||
|
'Authorization',
|
||||||
|
'Basic ' + Buffer.from(authString).toString('base64')
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { id },
|
||||||
|
} = await (
|
||||||
|
await fetch(`${process.env.LISTMONK_DOMAIN}/api/subscribers`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
const welcomeEmail = {
|
||||||
|
subscriber_id: id,
|
||||||
|
template_id: +process.env.LISTMONK_WELCOME_TEMPLATE_ID,
|
||||||
|
subject: 'Welcome to Postiz 🚀',
|
||||||
|
};
|
||||||
|
|
||||||
|
await fetch(`${process.env.LISTMONK_DOMAIN}/api/tx`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(welcomeEmail),
|
||||||
|
});
|
||||||
|
} catch (err) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue