import { createHash } from 'node:crypto';
import { ApifyClient } from 'apify-client@^2.19.0';
type ApifyApiKey = {
api_key: string;
};
type Apify = {
token: string;
};
type WebhookEventTypes =
| 'ACTOR.RUN.SUCCEEDED'
| 'ACTOR.RUN.FAILED'
| 'ACTOR.RUN.TIMED_OUT'
| 'ACTOR.RUN.ABORTED'
type ApifyWebhookConfig = {
url: string;
token: string;
};
export type DynSelect_taskId = string;
export async function taskId(api_key?: ApifyApiKey, oauth_token?: Apify) {
if (!api_key?.api_key && !oauth_token?.token) {
return [{ value: '', label: 'Missing Apify API key or OAuth token' }];
}
try {
const client = createClient(api_key, oauth_token);
const data = await client.tasks().list();
const items = data?.items ?? [];
return items.map((task: any) => ({
value: task.id,
label: task.title || task.name || task.id,
}));
} catch (error: any) {
return [
{ value: '', label: `Failed to load tasks: ${error.message || error}` },
];
}
}
function prettifyEvent(e: string) {
return e.replace(/^ACTOR\.RUN\./, '');
}
function generateIdempotencyKey(
id: string,
eventTypes: WebhookEventTypes[],
requestUrl: string
): string {
const sortedEventTypes = [...eventTypes].sort();
const url = new URL(requestUrl);
const pathname = url.pathname;
const hash = createHash('sha256');
hash.update(`${id}:${sortedEventTypes.join(',')}:${pathname}`);
return hash.digest('hex');
}
const createClient = (api_key?: ApifyApiKey, oauth_token?: Apify): ApifyClient => {
const token = oauth_token?.token ?? api_key?.api_key;
if (!token) {
throw new Error('Missing Apify API key or OAuth token');
}
return new ApifyClient({
token: token,
requestInterceptors: [
(request) => {
if (!request.headers) {
request.headers = {};
}
request.headers['x-apify-integration-platform'] = 'windmill';
return request;
},
],
});
};
export async function main(
apifyWebhookConfig: ApifyWebhookConfig,
taskId: DynSelect_taskId,
eventTypes: WebhookEventTypes[],
api_key?: ApifyApiKey,
oauth_token?: Apify,
) {
if (!taskId) {
return { error: 'Task ID is required' };
}
const client = createClient(api_key, oauth_token);
const idempotencyKey = generateIdempotencyKey(
taskId,
eventTypes,
apifyWebhookConfig.url
);
const headersTemplate = JSON.stringify({
Authorization: `Bearer ${apifyWebhookConfig.token}`,
});
// get task to store name for later webhook deletion
const task = await client.task(taskId).get();
const taskName = task && (task.title || task.name);
const webhookEvents = eventTypes
.map((event) => prettifyEvent(event))
.join(', ');
try {
const response = await client.webhooks().create({
requestUrl: apifyWebhookConfig.url,
eventTypes: eventTypes,
condition: {
actorTaskId: taskId,
},
idempotencyKey,
headersTemplate,
shouldInterpolateStrings: true,
description: `${taskName}: ${webhookEvents}`,
title: "Windmill integration"
});
return response;
} catch (e: any) {
return { error: `Failed to create a webhook. Reason: ${e.message}` };
}
}
Submitted by jakub.drobnik222 88 days ago