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,28 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"SMTP": "SMTP",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "Host",
"Email": "Email",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "Sender Name",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "E-Mails mit Simple Mail Transfer Protocol senden",
"Host": "Host",
"Email": "E-Mail",
"Password": "Kennwort",
"Port": "Port",
"Require TLS?": "TLS erforderlich?",
"Send Email": "E-Mail senden",
"Send an email using a custom SMTP server.": "Senden Sie eine E-Mail mit einem benutzerdefinierten SMTP-Server.",
"From Email": "Absender-E-Mail",
"Sender Name": "Absendername",
"To": "An",
"CC": "CC",
"Reply To": "Antwort an",
"BCC": "BCC",
"Subject": "Betreff",
"Body Type": "Körpertyp",
"Body": "Körper",
"Custom Headers": "Eigene Kopfzeilen",
"Attachments": "Anhänge",
"plain text": "schlichter Text",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Enviar correos electrónicos usando Simple Mail Transfer Protocol",
"Host": "Anfitrión",
"Email": "E-mail",
"Password": "Contraseña",
"Port": "Puerto",
"Require TLS?": "¿Requiere TLS?",
"Send Email": "Enviar Email",
"Send an email using a custom SMTP server.": "Enviar un correo electrónico usando un servidor SMTP personalizado.",
"From Email": "Desde Email",
"Sender Name": "Nombre del remitente",
"To": "A",
"CC": "CC",
"Reply To": "Responder a",
"BCC": "CCO",
"Subject": "Asunto",
"Body Type": "Tipo de cuerpo",
"Body": "Cuerpo",
"Custom Headers": "Cabeceras personalizadas",
"Attachments": "Adjuntos",
"plain text": "texto plano",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Envoyer des courriels en utilisant le protocole de transfert Simple Mail",
"Host": "Hôte",
"Email": "Courriel",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Exiger le TLS?",
"Send Email": "Envoyer un e-mail",
"Send an email using a custom SMTP server.": "Envoyer un email en utilisant un serveur SMTP personnalisé.",
"From Email": "De l'E-mail",
"Sender Name": "Nom de l'expéditeur",
"To": "À",
"CC": "CC",
"Reply To": "Répondre à",
"BCC": "Cci",
"Subject": "Sujet",
"Body Type": "Type de Corps",
"Body": "Corps",
"Custom Headers": "En-têtes personnalisés",
"Attachments": "Fichiers joints",
"plain text": "texte brut",
"html": "html"
}

View File

@@ -0,0 +1,28 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"SMTP": "SMTP",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "Host",
"Email": "Email",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "Sender Name",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,28 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"SMTP": "SMTP",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "Host",
"Email": "Email",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "Sender Name",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "シンプルなメール転送プロトコルを使用してメールを送信する",
"Host": "ホスト",
"Email": "Eメールアドレス",
"Password": "Password",
"Port": "ポート",
"Require TLS?": "TLSが必要ですか",
"Send Email": "メール送信",
"Send an email using a custom SMTP server.": "カスタムSMTPサーバーを使用してメールを送信します。",
"From Email": "差出人メールアドレス",
"Sender Name": "送信者名",
"To": "終了日",
"CC": "CC",
"Reply To": "返信先",
"BCC": "BCC",
"Subject": "件名",
"Body Type": "ボディタイプ",
"Body": "本文",
"Custom Headers": "カスタムヘッダー",
"Attachments": "添付ファイル",
"plain text": "プレーンテキスト",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Stuur e-mails met behulp van Simple Mail Transfer Protocol",
"Host": "Hostnaam",
"Email": "E-mail",
"Password": "Wachtwoord",
"Port": "Poort",
"Require TLS?": "TLS vereisen?",
"Send Email": "E-mail verzenden",
"Send an email using a custom SMTP server.": "Stuur een e-mail met behulp van een aangepaste SMTP-server.",
"From Email": "Van e-mail",
"Sender Name": "Naam afzender",
"To": "tot",
"CC": "CC",
"Reply To": "Antwoord aan",
"BCC": "Bcc",
"Subject": "Onderwerp",
"Body Type": "Type lichaam",
"Body": "Lichaam",
"Custom Headers": "Aangepaste headers",
"Attachments": "Bijlagen",
"plain text": "onopgemaakte tekst",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Enviar e-mails usando protocolo de transferência simples",
"Host": "Servidor",
"Email": "e-mail",
"Password": "Senha",
"Port": "Porta",
"Require TLS?": "Exigir TLS?",
"Send Email": "Enviar e-mail",
"Send an email using a custom SMTP server.": "Enviar um e-mail utilizando um servidor SMTP personalizado.",
"From Email": "E-mail do Remetente",
"Sender Name": "Nome do Remetente",
"To": "Para",
"CC": "CC",
"Reply To": "Responder a",
"BCC": "Cco",
"Subject": "Cargo",
"Body Type": "Tipo de Corpo",
"Body": "Conteúdo",
"Custom Headers": "Cabeçalhos Personalizados",
"Attachments": "Anexos",
"plain text": "texto simples",
"html": "HTML"
}

View File

@@ -0,0 +1,28 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"SMTP": "SMTP",
"Send emails using Simple Mail Transfer Protocol": "Отправлять письма, используя простой протокол передачи почты",
"Host": "Хост",
"Email": "Почта",
"Password": "Password",
"Port": "Порт",
"Require TLS?": "Требовать TLS?",
"Send Email": "Отправить письмо",
"Send an email using a custom SMTP server.": "Отправить письмо с помощью пользовательского SMTP сервера.",
"From Email": "От E-mail",
"Sender Name": "Имя отправителя",
"To": "Кому",
"CC": "CC",
"Reply To": "Ответить",
"BCC": "BCC",
"Subject": "Тема",
"Body Type": "Тип тела",
"Body": "Тело",
"Custom Headers": "Пользовательские заголовки",
"Attachments": "Вложения",
"plain text": "простой текст",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "Host",
"Email": "Email",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "Sender Name",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,28 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"SMTP": "SMTP",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "Host",
"Email": "Email",
"Password": "Password",
"Port": "Port",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "Sender Name",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,27 @@
{
"25": "25",
"465": "465",
"587": "587",
"2525": "2525",
"Send emails using Simple Mail Transfer Protocol": "Send emails using Simple Mail Transfer Protocol",
"Host": "主机",
"Email": "电子邮件地址",
"Password": "密码",
"Port": "端口",
"Require TLS?": "Require TLS?",
"Send Email": "Send Email",
"Send an email using a custom SMTP server.": "Send an email using a custom SMTP server.",
"From Email": "From Email",
"Sender Name": "发件人姓名",
"To": "To",
"CC": "CC",
"Reply To": "Reply To",
"BCC": "BCC",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "正文内容",
"Custom Headers": "Custom Headers",
"Attachments": "Attachments",
"plain text": "plain text",
"html": "html"
}

View File

@@ -0,0 +1,101 @@
import {
PieceAuth,
Property,
createPiece,
} from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { sendEmail } from './lib/actions/send-email';
import { smtpCommon } from './lib/common';
const SMTPPorts = [25, 465, 587, 2525];
export const smtpAuth = PieceAuth.CustomAuth({
required: true,
props: {
host: Property.ShortText({
displayName: 'Host',
required: true,
}),
email: Property.ShortText({
displayName: 'Email',
required: true,
}),
password: PieceAuth.SecretText({
displayName: 'Password',
required: true,
}),
port: Property.StaticDropdown({
displayName: 'Port',
required: true,
options: {
disabled: false,
options: SMTPPorts.map((port) => {
return {
label: port.toString(),
value: port,
};
}),
},
}),
TLS: Property.Checkbox({
displayName: 'Require TLS?',
defaultValue: false,
required: true,
}),
},
validate: async ({ auth }) => {
try {
const transporter = smtpCommon.createSMTPTransport(auth);
return new Promise((resolve, reject) => {
transporter.verify(function (error, success) {
if (error) {
resolve({ valid: false, error: JSON.stringify(error) });
} else {
resolve({ valid: true });
}
});
});
} catch (e) {
const castedError = (e as Record<string, unknown>)
const code = castedError?.['code'];
switch (code) {
case 'EDNS':
return {
valid: false,
error: 'SMTP server not found or unreachable with error code: EDNS',
};
case 'CONN':
return {
valid: false,
error: 'SMTP server connection failed with error code: CONN',
};
default:
break;
}
return {
valid: false,
error: JSON.stringify(e),
};
}
},
});
export const smtp = createPiece({
displayName: 'SMTP',
description: 'Send emails using Simple Mail Transfer Protocol',
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/smtp.png',
categories: [PieceCategory.CORE],
authors: [
'tahboubali',
'abaza738',
'kishanprmr',
'MoShizzle',
'khaledmashaly',
'abuaboud',
'pfernandez98'
],
auth: smtpAuth,
actions: [sendEmail],
triggers: [],
});

View File

@@ -0,0 +1,142 @@
import { ApFile, Property, createAction } from '@activepieces/pieces-framework';
import { smtpAuth } from '../..';
import { smtpCommon } from '../common';
import { Attachment, Headers } from 'nodemailer/lib/mailer';
import mime from 'mime-types';
export const sendEmail = createAction({
auth: smtpAuth,
name: 'send-email',
displayName: 'Send Email',
description: 'Send an email using a custom SMTP server.',
props: {
from: Property.ShortText({
displayName: 'From Email',
required: true,
}),
senderName: Property.ShortText({
displayName: "Sender Name",
required: false,
}),
to: Property.Array({
displayName: 'To',
required: true,
}),
cc: Property.Array({
displayName: 'CC',
required: false,
}),
replyTo: Property.ShortText({
displayName: 'Reply To',
required: false,
}),
bcc: Property.Array({
displayName: 'BCC',
required: false,
}),
subject: Property.ShortText({
displayName: 'Subject',
required: true,
}),
body_type: Property.StaticDropdown({
displayName: 'Body Type',
required: true,
defaultValue: 'plain_text',
options: {
disabled: false,
options: [
{
label: 'plain text',
value: 'plain_text',
},
{
label: 'html',
value: 'html',
},
],
},
}),
body: Property.LongText({
displayName: 'Body',
required: true,
}),
customHeaders: Property.Object({
displayName: 'Custom Headers',
required: false,
}),
attachments: Property.Array({
displayName: 'Attachments',
required: false,
properties: {
file: Property.File({
displayName: 'File',
description: 'File to attach to the email you want to send',
required: true,
}),
name: Property.ShortText({
displayName: 'Attachment Name',
description: 'In case you want to change the name of the attachment',
required: false,
}),
}
}),
},
run: async ({ auth, propsValue }) => {
const transporter = smtpCommon.createSMTPTransport(auth.props);
const attachments = propsValue['attachments'] as {file: ApFile; name: string | undefined; }[];
const attachment_data: Attachment[] = attachments.map(({file, name}) => {
const lookupResult = mime.lookup(
file.extension ? file.extension : ''
);
return {
filename: name ?? file.filename,
content: file?.base64,
contentType: lookupResult ? lookupResult : undefined,
encoding: 'base64',
};
});
const mailOptions = {
from: getFrom(propsValue.senderName, propsValue.from),
to: propsValue.to.join(','),
cc: propsValue.cc?.join(','),
inReplyTo: propsValue.replyTo,
bcc: propsValue.bcc?.join(','),
subject: propsValue.subject,
text: propsValue.body_type === 'plain_text' ? propsValue.body : undefined,
html: propsValue.body_type === 'html' ? propsValue.body : undefined,
attachments: attachment_data ? attachment_data : undefined,
headers: propsValue.customHeaders as Headers,
};
return await sendWithRetry(transporter, mailOptions);
},
});
async function sendWithRetry(transporter: any, mailOptions: any) {
const maxRetries = 3;
let retryCount = 0;
while (retryCount < maxRetries) {
try {
const info = await transporter.sendMail(mailOptions);
return info;
} catch (error: any) {
if ('code' in error && error.code === 'ECONNRESET' && retryCount < maxRetries - 1) {
retryCount++;
await new Promise(resolve => setTimeout(resolve, 3000));
continue;
}
throw error;
}
}
}
function getFrom(senderName: string|undefined, from: string) {
if (senderName) {
return `"${senderName}" <${from}>`
}
return from;
}

View File

@@ -0,0 +1,30 @@
import nodemailer from 'nodemailer';
export const smtpCommon = {
constructConfig(auth: smtpAuthParams) {
return {
host: auth.host,
port: auth.port,
requireTLS: auth.TLS,
auth: {
user: auth.email,
pass: auth.password,
},
connectionTimeout: 60000,
secure: auth.port === 465,
};
},
createSMTPTransport(auth: smtpAuthParams) {
const smtpOptions = smtpCommon.constructConfig(auth);
const transporter = nodemailer.createTransport(smtpOptions);
return transporter;
},
};
export type smtpAuthParams = {
host: string;
email: string;
password: string;
port: number;
TLS: boolean;
};