Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,54 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { mailerooAuth } from '../../';
import {
createCommonProps,
createFormData,
sendFormData,
} from '../common/send-utils';
export const sendEmail = createAction({
auth: mailerooAuth,
name: 'sendEmail',
displayName: 'Send Email',
description: 'Sends an email.',
props: {
...createCommonProps(),
content_type: Property.Dropdown<'text' | 'html',true,typeof mailerooAuth>({
auth: mailerooAuth,
displayName: 'Content Type',
refreshers: [],
required: true,
defaultValue: 'html',
options: async () => {
return {
disabled: false,
options: [
{ label: 'Plain Text', value: 'text' },
{ label: 'HTML', value: 'html' },
],
};
},
}),
content: Property.ShortText({
displayName: 'Content',
description: 'HTML is only allowed if you selected HTML as type',
required: true,
}),
},
async run(context) {
const formData = createFormData(context.propsValue);
const { content_type, content } = context.propsValue;
if (content_type === 'text') {
formData.append('plain', content);
} else if (content_type === 'html') {
formData.append('html', content);
}
const res = await sendFormData('send', formData, context.auth.props.apiKey);
return res.body;
},
});

View File

@@ -0,0 +1,45 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { mailerooAuth } from '../../';
import {
createCommonProps,
createFormData,
sendFormData,
} from '../common/send-utils';
export const sendFromTemplate = createAction({
auth: mailerooAuth,
name: 'sendFromTemplate',
displayName: 'Send Email using Template',
description: 'Sends an email from an existing template.',
props: {
...createCommonProps(),
template_id: Property.Number({
displayName: 'Template ID',
description: 'The ID of the template to use',
required: true,
}),
template_data: Property.Object({
displayName: 'Template Data',
description:
'Data to fill in the template. The string `{{name}}` in the template body will be replaced with the value of `name`',
required: true,
}),
},
async run(context) {
const formData = createFormData(context.propsValue);
const { template_id, template_data } = context.propsValue;
formData.append('template_id', template_id);
formData.append('template_data', JSON.stringify(template_data));
const res = await sendFormData(
'send-template',
formData,
context.auth.props.apiKey
);
return res.body;
},
});

View File

@@ -0,0 +1,25 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { checkEmail } from '../common/send-utils';
import { mailerooAuth } from '../..';
export const verifyEmail = createAction({
auth: mailerooAuth,
name: 'verifyEmail',
displayName: 'Verify Email',
description: 'Verifies an email address.',
props: {
content: Property.ShortText({
displayName: 'Email',
description: 'Email to verify',
required: true,
}),
},
async run(context) {
const result = await checkEmail(
context.propsValue.content,
context.auth.props.apiKey
);
return result.body;
},
});

View File

@@ -0,0 +1,99 @@
import { Property, StaticPropsValue } from '@activepieces/pieces-framework';
import FormData from 'form-data';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const createCommonProps = () => {
return {
to: Property.Array({
displayName: 'To',
description: 'Emails of the recipients(Supports comma-separated emails)',
required: true,
}),
from_name: Property.ShortText({
displayName: 'Sender Name',
description: 'Sender name',
required: true,
}),
from: Property.ShortText({
displayName: 'Sender Email(Your SMTP Email)',
description: 'Sender email. This should come from your SMTP accounts.',
required: true,
}),
subject: Property.ShortText({
displayName: 'Subject',
description: 'Subject of the email',
required: true,
}),
bcc: Property.Array({
displayName: 'BCC',
description: 'List of emails in bcc',
required: false,
}),
cc: Property.Array({
displayName: 'CC',
description: 'List of emails in cc',
required: false,
}),
reply_to: Property.ShortText({
displayName: 'Reply To',
description: 'Email to receive replies on (defaults to sender)',
required: false,
}),
attachment: Property.File({
displayName: 'Attachment',
description: 'File to attach to the email you want to send',
required: false,
}),
};
};
export const createFormData = (
propsValue: StaticPropsValue<ReturnType<typeof createCommonProps>>
): FormData => {
const { to, from, from_name, reply_to, subject, cc, bcc } = propsValue;
const formData = new FormData();
formData.append('from', `${from_name} <${from}>`);
formData.append('to', to.join(','));
formData.append('subject', subject);
formData.append('reply_to', reply_to ?? from);
if (cc) {
formData.append('cc', cc.join(','));
}
if (bcc) {
formData.append('bcc', bcc.join(','));
}
return formData;
};
export const sendFormData = async (
url: string,
formData: FormData,
auth: string
) => {
return httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://smtp.maileroo.com/${url}`,
body: formData,
headers: {
['X-API-Key']: auth,
...formData.getHeaders(),
},
});
};
export const checkEmail = async (email: string, apiKey: string) => {
return httpClient.sendRequest({
url: 'https://verify.maileroo.net/check',
method: HttpMethod.POST,
body: {
email_address: email,
},
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
});
};