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,35 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { backBlazeS3Auth } from '../..';
import { createBackBlazeS3 } from '../common';
export const readBackBlazeFileAction = createAction({
auth: backBlazeS3Auth,
name: 'read-backblaze-file',
displayName: 'Read File',
description: 'Read a file from Backblaze bucket to use it in other steps.',
props: {
key: Property.ShortText({
displayName: 'Key',
description: 'The key of the file to read. include extension if file has any extension.',
required: true,
}),
},
async run(context) {
const { bucket } = context.auth.props;
const { key } = context.propsValue;
const s3 = createBackBlazeS3(context.auth.props);
const file = await s3.getObject({
Bucket: bucket,
Key: key,
});
const base64 = await file.Body?.transformToString('base64');
if (!base64) {
throw new Error(`Could not read file ${key} from bucket`);
}
return await context.files.write({
fileName: key,
data: Buffer.from(base64, 'base64'),
});
},
});

View File

@@ -0,0 +1,137 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { backBlazeS3Auth } from '../..';
import { createBackBlazeS3 } from '../common';
import { ObjectCannedACL } from '@aws-sdk/client-s3';
export const backBlazes3UploadFileAction = createAction({
auth: backBlazeS3Auth,
name: 'upload-backblaze-file',
displayName: 'Upload File',
description: 'Upload an File to bucket.',
props: {
file: Property.File({
displayName: 'File',
required: true,
}),
fileName: Property.ShortText({
displayName: 'File Name',
required: false,
description: 'my-file-name (no extension). write full path if you want to store in the directories or sub-directories.',
}),
acl: Property.StaticDropdown({
displayName: 'ACL',
required: false,
options: {
options: [
{
label: 'private',
value: 'private',
},
{
label: 'public-read',
value: 'public-read',
},
{
label: 'public-read-write',
value: 'public-read-write',
},
{
label: 'authenticated-read',
value: 'authenticated-read',
},
{
label: 'aws-exec-read',
value: 'aws-exec-read',
},
{
label: 'bucket-owner-read',
value: 'bucket-owner-read',
},
{
label: 'bucket-owner-full-control',
value: 'bucket-owner-full-control',
},
],
},
}),
type: Property.StaticDropdown({
displayName: 'Type',
required: true,
options: {
options: [
{
label: 'image/png',
value: 'image/png',
},
{
label: 'image/jpeg',
value: 'image/jpeg',
},
{
label: 'image/gif',
value: 'image/gif',
},
{
label: 'audio/mpeg',
value: 'audio/mpeg',
},
{
label: 'audio/wav',
value: 'audio/wav',
},
{
label: 'video/mp4',
value: 'video/mp4',
},
{
label: 'application/pdf',
value: 'application/pdf',
},
{
label: 'application/msword',
value: 'application/msword',
},
{
label: 'text/plain',
value: 'text/plain',
},
{
label: 'application/json',
value: 'application/json',
},
],
},
}),
},
async run(context) {
const { bucket } = context.auth.props;
const { file, fileName, acl, type } = context.propsValue;
const s3 = createBackBlazeS3(context.auth.props);
const contentType = type;
const [_, ext] = contentType.split('/');
const extension = '.' + ext;
const generatedName = new Date().toISOString() + Date.now() + extension;
const finalFileName = fileName ? fileName + extension : generatedName;
const uploadResponse = await s3.putObject({
Bucket: bucket,
Key: finalFileName,
ACL: acl as ObjectCannedACL | undefined,
ContentType: contentType,
Body: file.data,
});
const endpoint = context.auth.props.endpoint ? context.auth.props.endpoint :"";
const cleanEndpoint = endpoint.replace("https://","")
const url = `https://${bucket}.${cleanEndpoint}/${finalFileName}`
return {
fileName: finalFileName,
etag: uploadResponse.ETag,
url: url,
};
},
});

View File

@@ -0,0 +1,21 @@
import { isNil } from '@activepieces/shared';
import { S3 } from '@aws-sdk/client-s3';
export function createBackBlazeS3(auth: {
accessKeyId: string;
secretAccessKey: string;
region: string | undefined;
endpoint: string | undefined;
}) {
const s3 = new S3({
credentials: {
accessKeyId: auth.accessKeyId,
secretAccessKey: auth.secretAccessKey,
},
forcePathStyle: auth.endpoint ? true : undefined,
region: auth.region,
endpoint:
auth.endpoint === '' || isNil(auth.endpoint) ? undefined : auth.endpoint,
});
return s3;
}

View File

@@ -0,0 +1,95 @@
import {
AppConnectionValueForAuthProperty,
PiecePropValueSchema,
Property,
createTrigger,
} from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import {
DedupeStrategy,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import { backBlazeS3Auth } from '../..';
import { createBackBlazeS3 } from '../common';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof backBlazeS3Auth>,
{ folderPath?: string }
> = {
strategy: DedupeStrategy.LAST_ITEM,
items: async ({ auth, lastItemId, propsValue }) => {
const s3 = createBackBlazeS3(auth.props);
const params: any = {
Bucket: auth.props.bucket,
MaxKeys: 100,
StartAfter: lastItemId,
};
if (propsValue.folderPath)
params.Prefix = `${
propsValue.folderPath.endsWith('/')
? propsValue.folderPath.slice(0, -1)
: propsValue.folderPath
}`;
const currentValues = (await s3.listObjectsV2(params)).Contents ?? [];
const items = (currentValues as any[]).map((item: { Key: string }) => ({
id: item.Key,
data: item,
}));
return items;
},
};
export const newBackBlazeFileTrigger = createTrigger({
auth: backBlazeS3Auth,
name: 'new_backblaze_file',
displayName: 'New File',
description: 'Trigger when a new file is uploaded.',
props: {
folderPath: Property.ShortText({
displayName: 'Folder Path',
required: false,
}),
},
type: TriggerStrategy.POLLING,
onEnable: async (context) => {
await pollingHelper.onEnable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
onDisable: async (context) => {
await pollingHelper.onDisable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
run: async (context) => {
return await pollingHelper.poll(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
files: context.files,
});
},
test: async (context) => {
return await pollingHelper.test(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
files: context.files,
});
},
sampleData: {
Key: 'myfolder/100-3.png',
LastModified: '2023-08-04T13:51:26.000Z',
ETag: '"e9f16cce12352322272525f5af65a2e"',
Size: 40239,
StorageClass: 'STANDARD',
},
});