feat: new provider - listmonk
This commit is contained in:
parent
ba6d035839
commit
df0077771a
|
|
@ -50,6 +50,10 @@ GITHUB_CLIENT_ID=""
|
|||
GITHUB_CLIENT_SECRET=""
|
||||
BEEHIIVE_API_KEY=""
|
||||
BEEHIIVE_PUBLICATION_ID=""
|
||||
LISTMONK_DOMAIN=""
|
||||
LISTMONK_USER=""
|
||||
LISTMONK_API_KEY=""
|
||||
LISTMONK_LIST_ID=""
|
||||
THREADS_APP_ID=""
|
||||
THREADS_APP_SECRET=""
|
||||
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 { ProvidersFactory } from '@gitroom/backend/services/auth/providers/providers.factory';
|
||||
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 { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto';
|
||||
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
|
||||
import { NewsletterService } from '@gitroom/nestjs-libraries/newsletter/newsletter.service';
|
||||
|
||||
@Injectable()
|
||||
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(
|
||||
async (data: FieldValues) => {
|
||||
modals.closeAll();
|
||||
gotoUrl(
|
||||
`/integrations/social/${identifier}?state=nostate&code=${Buffer.from(
|
||||
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 { PostComment } from '@gitroom/frontend/components/new-launch/providers/high.order.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 = [
|
||||
{
|
||||
|
|
@ -133,6 +134,10 @@ export const Providers = [
|
|||
identifier: 'wordpress',
|
||||
component: WordpressProvider,
|
||||
},
|
||||
{
|
||||
identifier: 'listmonk',
|
||||
component: ListmonkProvider,
|
||||
},
|
||||
];
|
||||
export const ShowAllProviders = forwardRef((props, ref) => {
|
||||
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 { 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 { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto';
|
||||
|
||||
export type ProviderExtension<T extends string, M> = { __type: T } & M;
|
||||
export type AllProvidersSettings =
|
||||
|
|
@ -34,6 +35,7 @@ export type AllProvidersSettings =
|
|||
| ProviderExtension<'devto', DevToSettingsDto>
|
||||
| ProviderExtension<'hashnode', HashnodeSettingsDto>
|
||||
| ProviderExtension<'wordpress', WordpressDto>
|
||||
| ProviderExtension<'listmonk', ListmonkDto>
|
||||
| ProviderExtension<'facebook', None>
|
||||
| ProviderExtension<'threads', None>
|
||||
| ProviderExtension<'mastodon', None>
|
||||
|
|
@ -64,6 +66,7 @@ export const allProviders = (setEmpty?: any) => {
|
|||
{ value: DevToSettingsDto, name: 'devto' },
|
||||
{ value: WordpressDto, name: 'wordpress' },
|
||||
{ value: HashnodeSettingsDto, name: 'hashnode' },
|
||||
{ value: ListmonkDto, name: 'listmonk' },
|
||||
{ value: setEmpty, name: 'facebook' },
|
||||
{ value: setEmpty, name: 'threads' },
|
||||
{ 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 { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.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[] = [
|
||||
new XProvider(),
|
||||
|
|
@ -54,6 +55,7 @@ export const socialIntegrationList: SocialProvider[] = [
|
|||
new DevToProvider(),
|
||||
new HashnodeProvider(),
|
||||
new WordpressProvider(),
|
||||
new ListmonkProvider(),
|
||||
// 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 {
|
||||
static async register(email: string) {
|
||||
if (
|
||||
!process.env.BEEHIIVE_API_KEY ||
|
||||
!process.env.BEEHIIVE_PUBLICATION_ID ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
email.indexOf('@') === -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
import { NewsletterInterface } from '@gitroom/nestjs-libraries/newsletter/newsletter.interface';
|
||||
|
||||
export class BeehiivProvider implements NewsletterInterface {
|
||||
name = 'beehiiv';
|
||||
async register(email: string) {
|
||||
const body = {
|
||||
email,
|
||||
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