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 { common } from '../common';
import { googlePubsubAuth } from '../..';
export const publishToTopic = createAction({
name: 'publish_to_topic',
auth: googlePubsubAuth,
displayName: 'Publish to topic',
description: 'Publish message to topic',
props: {
message: Property.Object({
displayName: 'Message',
required: true,
}),
topic: Property.Dropdown({
displayName: 'Topic',
required: true,
refreshers: ['auth'],
auth: googlePubsubAuth,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please authenticate first',
};
}
const json = auth.props.json;
return common.getTopics(json);
},
}),
},
async run(context) {
const client = common.getClient(context.auth.props.json);
const topic = context.propsValue.topic;
const url = `https://pubsub.googleapis.com/v1/${topic}:publish`;
const json = JSON.stringify(context.propsValue.message);
const body = JSON.stringify({
messages: [{ data: Buffer.from(json).toString('base64') }],
});
const { data } = await client.request<{ messageIds: string[] }>({
url,
method: 'POST',
body,
});
console.debug(
`Message sended to topic[${topic}]: ${json}, ack: ${data.messageIds[0]}`
);
return json;
},
});

View File

@@ -0,0 +1,71 @@
import { JWT } from 'google-auth-library';
export const common = {
getClient(authJson: string) {
const email = common.getEmail(authJson);
const privateKey = common.getPrivateKey(authJson);
const gaxios = new JWT({
email,
key: privateKey.replace(/\\n/g, '\n'), // remove duplicate '\' from client side
scopes: ['https://www.googleapis.com/auth/pubsub'],
});
return gaxios;
},
getProjectId(json: string) {
return JSON.parse(json).project_id;
},
getPrivateKey(json: string) {
return JSON.parse(json).private_key;
},
getEmail(json: string) {
return JSON.parse(json).client_email;
},
/**
* @returns options topics, topic value contain project name: projects/{pname}/topics/{tname}
*/
async getTopics(json: string) {
const client = common.getClient(json);
const topics = {
disabled: true,
options: [] as { label: string; value: string }[],
placeholder: 'Need authentication' as string | undefined,
};
try {
const response = await client.request<ITopicsInfo>({
url: `https://pubsub.googleapis.com/v1/projects/${this.getProjectId(
json
)}/topics`,
method: 'GET',
});
topics.options = response.data.topics.map((topic) => {
const topicName = topic.name.split('topics/')[1];
return { label: `${topicName}`, value: topic.name };
});
delete topics.placeholder;
topics.disabled = false;
} catch (e: any) {
if ('response' in e) {
topics.placeholder = `Get topics error: ${e.response.data.error}`;
console.debug(e.response.data.error);
}
}
return topics;
},
};
export interface IAuth {
email: string;
privateKey: string;
projectId: string;
}
export interface ITopicsInfo {
topics: { name: string }[];
}

View File

@@ -0,0 +1,110 @@
import { Property, createTrigger } from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import { googlePubsubAuth } from '../..';
import { common } from '../common';
export const newMessageInTopic = createTrigger({
auth: googlePubsubAuth,
name: 'new_message_in_topic',
displayName: 'New Message',
description: 'Trigger when a new message is sended.',
props: {
subscription: Property.ShortText({
displayName: 'Subscription name',
required: true,
}),
topic: Property.Dropdown({
displayName: 'Topic',
required: true,
auth: googlePubsubAuth,
refreshers: ['auth'],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please authenticate first',
};
}
const json = auth.props.json;
return common.getTopics(json);
},
}),
ackDeadlineSeconds: Property.Number({
displayName: 'Ack Deadline Seconds',
required: true,
defaultValue: 100,
}),
},
type: TriggerStrategy.WEBHOOK,
onEnable: async (context) => {
const json = context.auth.props.json;
const client = common.getClient(json);
const { topic, subscription } = context.propsValue;
const project = common.getProjectId(context.auth.props.json);
const url = `https://pubsub.googleapis.com/v1/projects/${project}/subscriptions/${subscription}`;
const body = {
topic,
pushConfig: {
pushEndpoint: context.webhookUrl,
attributes: {},
},
ackDeadlineSeconds: context.propsValue.ackDeadlineSeconds,
};
await client.request({
url,
method: 'PUT',
data: JSON.stringify(body),
});
await context.store.put<ISubscriptionInfo>('_trigger', {
project,
subscription,
});
},
onDisable: async (context) => {
const response = await context.store.get<ISubscriptionInfo>('_trigger');
if (response !== null && response !== undefined) {
const json = context.auth.props.json;
const client = common.getClient(json);
const { project, subscription } = response;
const url = `https://pubsub.googleapis.com/v1/projects/${project}/subscriptions/${subscription}`;
await client.request({
url,
method: 'DELETE',
});
}
},
async run(context) {
console.debug('payload received', context.payload.body);
const payloadBody = context.payload.body as PayloadBody;
const { data } = payloadBody.message;
const object = data
? JSON.parse(Buffer.from(data, 'base64').toString())
: {};
return [object];
},
sampleData: {
x: 1.0,
y: -1.0,
text: 'Just text sample',
},
});
interface ISubscriptionInfo {
project: string;
subscription: string;
}
type PayloadBody = {
message: {
data: string;
};
};