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,64 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxCopyFile = createAction({
auth: dropboxAuth,
name: 'copy_dropbox_file',
description: 'Copy a file',
displayName: 'Copy file',
props: {
from_path: Property.ShortText({
displayName: 'From Path',
description: 'The source path of the file (e.g. /folder1/sourcefile.txt)',
required: true,
}),
to_path: Property.ShortText({
displayName: 'To Path',
description:
'The destination path for the copied (e.g. /folder2/destinationfile.txt)',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, have the Dropbox server try to autorename the file to avoid conflict.",
defaultValue: false,
required: false,
}),
allow_ownership_transfer: Property.Checkbox({
displayName: 'Allow Ownership Transfer',
description:
'Allows copy by owner even if it would result in an ownership transfer.',
defaultValue: false,
required: false,
}),
},
async run(context) {
const params = {
from_path: context.propsValue.from_path,
to_path: context.propsValue.to_path,
autorename: context.propsValue.autorename,
allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/copy_v2`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,64 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxCopyFolder = createAction({
auth: dropboxAuth,
name: 'copy_dropbox_folder',
description: 'Copy a folder',
displayName: 'Copy folder',
props: {
from_path: Property.ShortText({
displayName: 'From Path',
description: 'The source path of the folder (e.g. /folder1/sourceFolder)',
required: true,
}),
to_path: Property.ShortText({
displayName: 'To Path',
description:
'The destination path for the copied folder (e.g. /folder2/destinationFolder)',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, have the Dropbox server try to autorename the folder to avoid conflict.",
defaultValue: false,
required: false,
}),
allow_ownership_transfer: Property.Checkbox({
displayName: 'Allow Ownership Transfer',
description:
'Allows copy by owner even if it would result in an ownership transfer.',
defaultValue: false,
required: false,
}),
},
async run(context) {
const params = {
from_path: context.propsValue.from_path,
to_path: context.propsValue.to_path,
autorename: context.propsValue.autorename,
allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/copy_v2`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,53 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
HttpRequest,
HttpMethod,
AuthenticationType,
httpClient,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxCreateNewFolder = createAction({
auth: dropboxAuth,
name: 'create_new_dropbox_folder',
description: 'Create a new empty folder',
displayName: 'Create New Folder',
props: {
path: Property.ShortText({
displayName: 'Path',
description: 'The path of the new folder e.g. /Homework/math',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, have the Dropbox server try to autorename the folder to avoid the conflict. The default for this field is False.",
required: false,
}),
},
async run(context) {
const body = {
autorename: context.propsValue.autorename ? true : false,
path: context.propsValue.path,
};
const request: HttpRequest = {
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/create_folder_v2`,
body,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
};
const result = await httpClient.sendRequest(request);
console.debug('Folder creation response', result);
if (result.status == 200) {
return result.body;
} else {
return result;
}
},
});

View File

@@ -0,0 +1,77 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
HttpRequest,
HttpMethod,
AuthenticationType,
httpClient,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxCreateNewTextFile = createAction({
auth: dropboxAuth,
name: 'create_new_dropbox_text_file',
description: 'Create a new text file from text input',
displayName: 'Create New Text File',
props: {
path: Property.ShortText({
displayName: 'Path',
description: 'The path of the new folder e.g. /Homework/math',
required: true,
}),
text: Property.LongText({
displayName: 'Text',
description: 'The text to write into the file.',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Autorename',
description:
"If there's a conflict, have the Dropbox server try to autorename the folder to avoid the conflict. The default for this field is False.",
required: false,
}),
mute: Property.Checkbox({
displayName: 'Mute',
description:
"Normally, users are made aware of any file modifications in their Dropbox account via notifications in the client software. If true, this tells the clients that this modification shouldn't result in a user notification.",
required: false,
}),
strict_conflict: Property.Checkbox({
displayName: 'Strict conflict',
description:
'Be more strict about how each WriteMode detects conflict. For example, always return a conflict error when mode = WriteMode.update and the given "rev" doesn\'t match the existing file\'s "rev", even if the existing file has been deleted.',
required: false,
}),
},
async run(context) {
const params = {
autorename: context.propsValue.autorename,
path: context.propsValue.path,
mode: 'add',
mute: context.propsValue.mute,
strict_conflict: context.propsValue.strict_conflict,
};
const request: HttpRequest = {
method: HttpMethod.POST,
url: `https://content.dropboxapi.com/2/files/upload`,
body: Buffer.from(context.propsValue.text, 'utf-8'),
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
headers: {
'Dropbox-API-Arg': JSON.stringify(params),
'Content-Type': 'application/octet-stream',
},
};
const result = await httpClient.sendRequest(request);
console.debug('Folder creation response', result);
if (result.status == 200) {
return result.body;
} else {
return result;
}
},
});

View File

@@ -0,0 +1,42 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxDeleteFile = createAction({
auth: dropboxAuth,
name: 'delete_dropbox_file',
description: 'Delete a file',
displayName: 'Delete file',
props: {
path: Property.ShortText({
displayName: 'Path',
description:
'The path of the file to be deleted (e.g. /folder1/file.txt)',
required: true,
}),
},
async run(context) {
const params = {
path: context.propsValue.path,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/delete_v2`,
headers: {
'Content-Type': 'application/json',
},
body: { path: context.propsValue.path },
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,41 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxDeleteFolder = createAction({
auth: dropboxAuth,
name: 'delete_dropbox_folder',
description: 'Delete a folder',
displayName: 'Delete folder',
props: {
path: Property.ShortText({
displayName: 'Path',
description: 'The path of the folder to be deleted (e.g. /folder1)',
required: true,
}),
},
async run(context) {
const params = {
path: context.propsValue.path,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/delete_v2`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,49 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../..';
export const dropboxDownloadFile = createAction({
auth: dropboxAuth,
name: 'downloadFile',
displayName: 'Download File',
description: 'Download a File from Dropbox',
props: {
path: Property.ShortText({
displayName: "Path",
description: "The path of the file (e.g. /folder1/file.txt)",
required: true
})
},
async run(context) {
// Capture file name, if unsuccesfull default to output.pdf
const fileName = (context.propsValue.path.match(/[^/]+$/) ?? ['output.pdf'])[0]
// For information about Dropbox JSON encoding, see https://www.dropbox.com/developers/reference/json-encoding
const dropboxApiArg = JSON.stringify({path:context.propsValue.path}).replace(/[\u007f-\uffff]/g, (c) => '\\u'+('000'+c.charCodeAt(0).toString(16)).slice(-4));
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://content.dropboxapi.com/2/files/download`,
headers: {
'Content-Type': 'application/octet-stream',
'Dropbox-API-Arg': dropboxApiArg
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
responseType:'arraybuffer'
});
return {
file: await context.files.write({
fileName: fileName,
data: Buffer.from(result.body)
})
}
},
});

View File

@@ -0,0 +1,41 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../..';
export const dropboxGetFileLink = createAction({
auth: dropboxAuth,
name: 'get_dropbox_file_link',
description: 'Get a temporary file link',
displayName: 'Get temporary file link',
props: {
path: Property.ShortText({
displayName: 'Path',
description: 'The path of the file (e.g. /folder1/file.txt)',
required: true,
}),
},
async run(context) {
const params = {
path: context.propsValue.path,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/get_temporary_link`,
headers: {
'Content-Type': 'application/json',
},
body: { path: context.propsValue.path },
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,57 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxListAFolder = createAction({
auth: dropboxAuth,
name: 'list_dropbox_folder',
description: 'List the contents of a folder',
displayName: 'List a folder',
props: {
path: Property.ShortText({
displayName: 'Path',
description:
'The path of the folder to be listed (e.g. /folder1). Use an empty string for the root folder.',
required: true,
}),
recursive: Property.Checkbox({
displayName: 'Recursive',
description:
'If set to true, the list folder operation will be applied recursively to all subfolders and the response will contain contents of all subfolders.',
defaultValue: false,
required: false,
}),
limit: Property.Number({
displayName: 'Limit',
description:
'The maximum number of results to return (between 1 and 2000). Default is 2000 if not specified.',
required: false,
}),
},
async run(context) {
const params = {
path: context.propsValue.path,
recursive: context.propsValue.recursive,
limit: context.propsValue.limit || 2000,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/list_folder`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,63 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxMoveFile = createAction({
auth: dropboxAuth,
name: 'move_dropbox_file',
description: 'Move a file',
displayName: 'Move file',
props: {
from_path: Property.ShortText({
displayName: 'From Path',
description: 'The current path of the file (e.g. /folder1/oldfile.txt)',
required: true,
}),
to_path: Property.ShortText({
displayName: 'To Path',
description: 'The new path for the file (e.g. /folder2/newfile.txt)',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, have the Dropbox server try to autorename the file to avoid conflict.",
defaultValue: false,
required: false,
}),
allow_ownership_transfer: Property.Checkbox({
displayName: 'Allow Ownership Transfer',
description:
'Allows moves by owner even if it would result in an ownership transfer.',
defaultValue: false,
required: false,
}),
},
async run(context) {
const params = {
from_path: context.propsValue.from_path,
to_path: context.propsValue.to_path,
autorename: context.propsValue.autorename,
allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/move_v2`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,65 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxMoveFolder = createAction({
auth: dropboxAuth,
name: 'move_dropbox_folder',
description: 'Move a folder',
displayName: 'Move folder',
props: {
from_path: Property.ShortText({
displayName: 'From Path',
description:
'The current path of the folder (e.g. /folder1/sourceFolder)',
required: true,
}),
to_path: Property.ShortText({
displayName: 'To Path',
description:
'The new path for the folder (e.g. /folder2/destinationFolder)',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, have the Dropbox server try to autorename the folder to avoid conflict.",
defaultValue: false,
required: false,
}),
allow_ownership_transfer: Property.Checkbox({
displayName: 'Allow Ownership Transfer',
description:
'Allows moves by owner even if it would result in an ownership transfer.',
defaultValue: false,
required: false,
}),
},
async run(context) {
const params = {
from_path: context.propsValue.from_path,
to_path: context.propsValue.to_path,
autorename: context.propsValue.autorename,
allow_ownership_transfer: context.propsValue.allow_ownership_transfer,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/move_v2`,
headers: {
'Content-Type': 'application/json',
},
body: params,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,115 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../..';
export const dropboxSearch = createAction({
auth: dropboxAuth,
name: 'search_dropbox',
description: 'Search for files and folders',
displayName: 'Search',
props: {
query: Property.ShortText({
displayName: 'Query',
description: 'The search string. Must be at least 3 characters.',
required: true,
}),
path: Property.ShortText({
displayName: 'Path',
description:
'The path to search in. If not specified, the search is performed from the root.',
required: false,
}),
max_results: Property.Number({
displayName: 'Max Results',
description:
'The maximum number of search results to return (up to 1000). Default is 100 if not specified.',
required: false,
}),
order_by: Property.StaticDropdown({
displayName: 'Order By',
description: 'Specified property of the order of search results.',
options: {
options: [
{ label: 'Relevance', value: 'relevance' },
{ label: 'Modified Time', value: 'modified_time' },
],
},
defaultValue: 'relevance',
required: false,
}),
file_status: Property.StaticDropdown({
displayName: 'File Status',
description: 'Restricts search to the given file status.',
options: {
options: [
{ label: 'Active', value: 'active' },
{ label: 'Deleted', value: 'deleted' },
],
},
defaultValue: 'active',
required: false,
}),
filename_only: Property.Checkbox({
displayName: 'Filename Only',
description: 'Restricts search to only match on filenames.',
defaultValue: false,
required: false,
}),
file_extensions: Property.ShortText({
displayName: 'File Extensions',
description:
'Restricts search to only the extensions specified (comma-separated).',
required: false,
}),
file_categories: Property.ShortText({
displayName: 'File Categories',
description:
'Restricts search to only the file categories specified (comma-separated).',
required: false,
}),
account_id: Property.ShortText({
displayName: 'Account ID',
description: 'Restricts results to the given account id.',
required: false,
}),
},
async run(context) {
const options = {
path: context.propsValue.path || '',
max_results: context.propsValue.max_results || 100,
file_status: context.propsValue.file_status,
filename_only: context.propsValue.filename_only,
file_extensions: context.propsValue.file_extensions
? context.propsValue.file_extensions.split(',')
: undefined,
file_categories: context.propsValue.file_categories
? context.propsValue.file_categories.split(',')
: undefined,
account_id: context.propsValue.account_id,
};
const requestBody = {
query: context.propsValue.query,
options: options,
};
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.dropboxapi.com/2/files/search_v2`,
headers: {
'Content-Type': 'application/json',
},
body: requestBody,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});

View File

@@ -0,0 +1,77 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { dropboxAuth } from '../../';
export const dropboxUploadFile = createAction({
auth: dropboxAuth,
name: 'upload_dropbox_file',
description: 'Upload a file',
displayName: 'Upload file',
props: {
path: Property.ShortText({
displayName: 'Path',
description:
'The path where the file should be saved (e.g. /folder1/file.txt)',
required: true,
}),
file: Property.File({
displayName: 'File',
description: 'The file URL or base64 to upload',
required: true,
}),
autorename: Property.Checkbox({
displayName: 'Auto Rename',
description:
"If there's a conflict, as determined by mode, have the Dropbox server try to autorename the file to avoid conflict.",
defaultValue: false,
required: false,
}),
mute: Property.Checkbox({
displayName: 'Mute',
description:
"Normally, users are made aware of any file modifications in their Dropbox account via notifications in the client software. If true, this tells the clients that this modification shouldn't result in a user notification.",
required: false,
}),
strict_conflict: Property.Checkbox({
displayName: 'Strict conflict',
description:
'Be more strict about how each WriteMode detects conflict. For example, always return a conflict error when mode = WriteMode.update and the given "rev" doesn\'t match the existing file\'s "rev", even if the existing file has been deleted.',
required: false,
}),
},
async run(context) {
const fileData = context.propsValue.file;
const params = {
autorename: context.propsValue.autorename,
path: context.propsValue.path,
mode: 'add',
mute: context.propsValue.mute,
strict_conflict: context.propsValue.strict_conflict,
};
const fileBuffer = Buffer.from(fileData.base64, 'base64');
// For information about Dropbox JSON encoding, see https://www.dropbox.com/developers/reference/json-encoding
const dropboxApiArg = JSON.stringify(params).replace(/[\u007f-\uffff]/g, (c) => '\\u'+('000'+c.charCodeAt(0).toString(16)).slice(-4));
const result = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://content.dropboxapi.com/2/files/upload`,
body: fileBuffer,
headers: {
'Dropbox-API-Arg': dropboxApiArg,
'Content-Type': 'application/octet-stream',
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.access_token,
},
});
return result.body;
},
});