import { ActorListSortBy, ApifyClient } from 'apify-client@^2.19.0';
import { createHash } from 'node:crypto';
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;
};
type ActorSource = 'RECENTLY_USED_ACTORS' | 'APIFY_STORE_ACTORS';
export type DynSelect_actorId = string;
export async function actorId(actorSource: ActorSource, api_key?: ApifyApiKey, oauth_token?: Apify) {
if (!api_key?.api_key && !oauth_token?.token) {
return [{ value: '', label: 'Missing Apify API key or OAuth token' }];
}
const client = createClient(api_key, oauth_token);
const mapActorToSelectOption = (actor: any) => {
const optionName = actor.title
? `${actor.title} (${actor.username}/${actor.name})`
: `${actor.username}/${actor.name}`;
return {
label: optionName,
value: actor.id,
};
};
try {
if (actorSource === 'RECENTLY_USED_ACTORS') {
const recentActors = await client.actors().list({
limit: 100,
offset: 0,
sortBy: ActorListSortBy.LAST_RUN_STARTED_AT,
desc: true,
});
return recentActors.items.map(mapActorToSelectOption);
}
const storeActors = await client.store().list({
limit: 1000,
offset: 0,
});
return storeActors.items.map(mapActorToSelectOption);
} catch (error: any) {
return [
{ value: '', label: `Failed to load actors: ${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(
actorSource: ActorSource,
apifyWebhookConfig: ApifyWebhookConfig,
actorId: DynSelect_actorId,
eventTypes: WebhookEventTypes[],
api_key?: ApifyApiKey,
oauth_token?: Apify,
) {
if (!actorId) {
return { error: 'Actor ID is required' };
}
const client = createClient(api_key, oauth_token);
const idempotencyKey = generateIdempotencyKey(
actorId,
eventTypes,
apifyWebhookConfig.url
);
const headersTemplate = JSON.stringify({
Authorization: `Bearer ${apifyWebhookConfig.token}`,
});
// get actor to store name for later webhook deletion
const actor = await client.actor(actorId).get();
const actorName = actor && (actor.title || actor.name);
const webhookEvents = eventTypes
.map((event) => prettifyEvent(event))
.join(', ');
try {
const response = await client.webhooks().create({
requestUrl: apifyWebhookConfig.url,
eventTypes: eventTypes,
condition: {
actorId: actorId,
},
idempotencyKey,
headersTemplate,
shouldInterpolateStrings: true,
description: `${actorName}: ${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