n8n-nodes-cribops 0.1.18 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -41,7 +41,7 @@ class CribopsTrigger {
|
|
|
41
41
|
loadOptionsMethod: 'getWebhooks',
|
|
42
42
|
},
|
|
43
43
|
default: '',
|
|
44
|
-
description: '
|
|
44
|
+
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
displayName: 'Event Types',
|
|
@@ -109,18 +109,40 @@ class CribopsTrigger {
|
|
|
109
109
|
apiToken: credentials.apiToken,
|
|
110
110
|
});
|
|
111
111
|
try {
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
// Organization is automatically determined from API key
|
|
113
|
+
const webhooks = await cribopsHttp.getWebhooks();
|
|
114
|
+
// Filter for N8N type webhooks that are active and not linked
|
|
115
|
+
const availableWebhooks = webhooks.filter((webhook) => webhook.type === 'N8N' &&
|
|
116
|
+
webhook.status === 'active' &&
|
|
117
|
+
!webhook.linked_workflow_id);
|
|
118
|
+
return availableWebhooks.map((webhook) => ({
|
|
119
|
+
name: webhook.name,
|
|
118
120
|
value: webhook.id,
|
|
119
|
-
description: webhook.description || `
|
|
121
|
+
description: webhook.description || `Type: ${webhook.type}`,
|
|
120
122
|
}));
|
|
121
123
|
}
|
|
122
124
|
catch (error) {
|
|
123
|
-
|
|
125
|
+
let errorMessage = 'Failed to load webhooks';
|
|
126
|
+
if (error instanceof Error) {
|
|
127
|
+
errorMessage += `: ${error.message}`;
|
|
128
|
+
// Check for common issues
|
|
129
|
+
if (error.message.includes('HTTP 401') || error.message.includes('Unauthorized')) {
|
|
130
|
+
errorMessage = 'Authentication failed. Please check your API token in the credentials.';
|
|
131
|
+
}
|
|
132
|
+
else if (error.message.includes('HTTP 404')) {
|
|
133
|
+
errorMessage = 'Webhook endpoint not found. Please check the API base URL in credentials.';
|
|
134
|
+
}
|
|
135
|
+
else if (error.message.includes('HTTP 403')) {
|
|
136
|
+
errorMessage = 'Access forbidden. Your API token may not have permission to access webhooks.';
|
|
137
|
+
}
|
|
138
|
+
else if (error.message.includes('Failed to fetch') || error.message.includes('network')) {
|
|
139
|
+
errorMessage = 'Network error. Please check the API base URL and ensure the Cribops API is accessible.';
|
|
140
|
+
}
|
|
141
|
+
else if (error.message.includes('HTTP 400')) {
|
|
142
|
+
errorMessage = 'Bad request. Please check that the API endpoint is correct and your API token has the necessary permissions.';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage);
|
|
124
146
|
}
|
|
125
147
|
},
|
|
126
148
|
},
|
|
@@ -135,37 +157,37 @@ class CribopsTrigger {
|
|
|
135
157
|
async create() {
|
|
136
158
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
137
159
|
const webhookId = this.getNodeParameter('webhookId');
|
|
138
|
-
const eventTypes = this.getNodeParameter('eventTypes', []);
|
|
139
|
-
const additionalFields = this.getNodeParameter('additionalFields', {});
|
|
140
160
|
const credentials = await this.getCredentials('cribopsApi');
|
|
161
|
+
const workflowId = this.getWorkflow().id;
|
|
162
|
+
const workflowName = this.getWorkflow().name;
|
|
141
163
|
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
142
164
|
baseUrl: credentials.baseUrl,
|
|
143
165
|
apiToken: credentials.apiToken,
|
|
144
166
|
});
|
|
145
167
|
try {
|
|
146
|
-
//
|
|
168
|
+
// Link this workflow to the webhook entity
|
|
147
169
|
const body = {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
workflow_id: workflowId,
|
|
171
|
+
webhook_url: webhookUrl,
|
|
172
|
+
test_webhook_url: webhookUrl.replace('/webhook/', '/webhook-test/'),
|
|
173
|
+
workflow_name: workflowName || 'Unnamed Workflow',
|
|
152
174
|
};
|
|
153
|
-
//
|
|
154
|
-
await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/
|
|
175
|
+
// Link the n8n workflow to the webhook entity
|
|
176
|
+
await cribopsHttp.request('POST', `/api/v1/webhooks/${webhookId}/link`, body);
|
|
155
177
|
// Store webhook data for later use
|
|
156
178
|
const webhookData = this.getWorkflowStaticData('node');
|
|
157
179
|
webhookData.webhookId = webhookId;
|
|
158
|
-
webhookData.
|
|
180
|
+
webhookData.webhookUrl = webhookUrl;
|
|
159
181
|
return true;
|
|
160
182
|
}
|
|
161
183
|
catch (error) {
|
|
162
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to
|
|
184
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to link webhook: ${error instanceof Error ? error.message : String(error)}`);
|
|
163
185
|
}
|
|
164
186
|
},
|
|
165
187
|
async delete() {
|
|
166
188
|
const webhookData = this.getWorkflowStaticData('node');
|
|
167
189
|
const credentials = await this.getCredentials('cribopsApi');
|
|
168
|
-
if (!webhookData.webhookId
|
|
190
|
+
if (!webhookData.webhookId) {
|
|
169
191
|
return true;
|
|
170
192
|
}
|
|
171
193
|
const cribopsHttp = new CribopsHttp_1.CribopsHttp({
|
|
@@ -173,17 +195,15 @@ class CribopsTrigger {
|
|
|
173
195
|
apiToken: credentials.apiToken,
|
|
174
196
|
});
|
|
175
197
|
try {
|
|
176
|
-
//
|
|
177
|
-
await cribopsHttp.request('DELETE', `/api/v1/webhooks/${webhookData.webhookId}/
|
|
178
|
-
target_url: webhookData.targetUrl,
|
|
179
|
-
});
|
|
198
|
+
// Unlink the workflow from the webhook entity
|
|
199
|
+
await cribopsHttp.request('DELETE', `/api/v1/webhooks/${webhookData.webhookId}/link`);
|
|
180
200
|
delete webhookData.webhookId;
|
|
181
|
-
delete webhookData.
|
|
201
|
+
delete webhookData.webhookUrl;
|
|
182
202
|
return true;
|
|
183
203
|
}
|
|
184
204
|
catch (error) {
|
|
185
205
|
// Log error but don't fail
|
|
186
|
-
console.error('Failed to
|
|
206
|
+
console.error('Failed to unlink webhook:', error);
|
|
187
207
|
return true;
|
|
188
208
|
}
|
|
189
209
|
},
|
package/dist/package.json
CHANGED
|
@@ -37,6 +37,18 @@ export interface CribopsQueueMessage {
|
|
|
37
37
|
};
|
|
38
38
|
inserted_at: string;
|
|
39
39
|
}
|
|
40
|
+
export interface CribopsWebhookEntity {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
type: 'N8N' | 'GHL_API' | 'GENERIC';
|
|
45
|
+
status: 'active' | 'inactive';
|
|
46
|
+
linked_workflow_id?: string;
|
|
47
|
+
linked_workflow_name?: string;
|
|
48
|
+
organization_id: string;
|
|
49
|
+
created_at: string;
|
|
50
|
+
updated_at: string;
|
|
51
|
+
}
|
|
40
52
|
export declare class CribopsHttp {
|
|
41
53
|
private config;
|
|
42
54
|
constructor(config: CribopsHttpConfig);
|
|
@@ -57,5 +69,13 @@ export declare class CribopsHttp {
|
|
|
57
69
|
status: string;
|
|
58
70
|
updated_count: number;
|
|
59
71
|
}>;
|
|
72
|
+
getWebhooks(): Promise<CribopsWebhookEntity[]>;
|
|
73
|
+
linkWebhook(webhookId: string, linkData: {
|
|
74
|
+
workflow_id: string;
|
|
75
|
+
webhook_url: string;
|
|
76
|
+
test_webhook_url: string;
|
|
77
|
+
workflow_name: string;
|
|
78
|
+
}): Promise<any>;
|
|
79
|
+
unlinkWebhook(webhookId: string): Promise<any>;
|
|
60
80
|
request<T = any>(method: IHttpRequestMethods, endpoint: string, data?: IDataObject, options?: Partial<IHttpRequestOptions>): Promise<T>;
|
|
61
81
|
}
|
|
@@ -10,7 +10,22 @@ class CribopsHttp {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
async makeRequest(method, endpoint, data, options) {
|
|
13
|
-
|
|
13
|
+
let url = `${this.config.baseUrl}${endpoint}`;
|
|
14
|
+
// Handle query parameters for GET requests
|
|
15
|
+
if (method === 'GET' && data && Object.keys(data).length > 0) {
|
|
16
|
+
const params = new URLSearchParams();
|
|
17
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
18
|
+
if (value !== undefined && value !== null) {
|
|
19
|
+
params.append(key, String(value));
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const queryString = params.toString();
|
|
23
|
+
if (queryString) {
|
|
24
|
+
url += `?${queryString}`;
|
|
25
|
+
}
|
|
26
|
+
// Clear data for GET requests as params are in URL
|
|
27
|
+
data = undefined;
|
|
28
|
+
}
|
|
14
29
|
const requestHeaders = {
|
|
15
30
|
'Authorization': `Bearer ${this.config.apiToken}`,
|
|
16
31
|
'Content-Type': 'application/json',
|
|
@@ -25,11 +40,20 @@ class CribopsHttp {
|
|
|
25
40
|
const response = await fetch(url, {
|
|
26
41
|
method,
|
|
27
42
|
headers: requestHeaders,
|
|
28
|
-
body: data ? JSON.stringify(data) : undefined,
|
|
43
|
+
body: (method !== 'GET' && data) ? JSON.stringify(data) : undefined,
|
|
29
44
|
});
|
|
30
45
|
if (!response.ok) {
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
let errorText = await response.text();
|
|
47
|
+
let errorDetail = '';
|
|
48
|
+
// Try to parse error response as JSON
|
|
49
|
+
try {
|
|
50
|
+
const errorJson = JSON.parse(errorText);
|
|
51
|
+
errorDetail = errorJson.message || errorJson.error || errorText;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
errorDetail = errorText;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`HTTP ${response.status}: ${errorDetail}`);
|
|
33
57
|
}
|
|
34
58
|
const result = await response.json();
|
|
35
59
|
return result;
|
|
@@ -73,8 +97,17 @@ class CribopsHttp {
|
|
|
73
97
|
},
|
|
74
98
|
});
|
|
75
99
|
if (!response.ok) {
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
let errorText = await response.text();
|
|
101
|
+
let errorDetail = '';
|
|
102
|
+
// Try to parse error response as JSON
|
|
103
|
+
try {
|
|
104
|
+
const errorJson = JSON.parse(errorText);
|
|
105
|
+
errorDetail = errorJson.message || errorJson.error || errorText;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
errorDetail = errorText;
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`HTTP ${response.status}: ${errorDetail}`);
|
|
78
111
|
}
|
|
79
112
|
const arrayBuffer = await response.arrayBuffer();
|
|
80
113
|
return Buffer.from(arrayBuffer);
|
|
@@ -144,6 +177,47 @@ class CribopsHttp {
|
|
|
144
177
|
throw new Error(`Failed to mark messages as failed for tenant ${tenantId}: ${error}`);
|
|
145
178
|
}
|
|
146
179
|
}
|
|
180
|
+
// Webhook-specific methods
|
|
181
|
+
async getWebhooks() {
|
|
182
|
+
try {
|
|
183
|
+
// Organization is automatically determined from API key
|
|
184
|
+
const response = await this.makeRequest('GET', '/api/v1/webhooks');
|
|
185
|
+
// Handle different response structures
|
|
186
|
+
// The API might return webhooks directly as array or wrapped in data property
|
|
187
|
+
if (Array.isArray(response)) {
|
|
188
|
+
return response;
|
|
189
|
+
}
|
|
190
|
+
else if (response.data && Array.isArray(response.data)) {
|
|
191
|
+
return response.data;
|
|
192
|
+
}
|
|
193
|
+
else if (response.webhooks && Array.isArray(response.webhooks)) {
|
|
194
|
+
return response.webhooks;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// If we can't find webhooks, return empty array
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
throw new Error(`Failed to fetch webhooks: ${error}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async linkWebhook(webhookId, linkData) {
|
|
206
|
+
try {
|
|
207
|
+
return await this.makeRequest('POST', `/api/v1/webhooks/${webhookId}/link`, linkData);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
throw new Error(`Failed to link webhook: ${error}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async unlinkWebhook(webhookId) {
|
|
214
|
+
try {
|
|
215
|
+
return await this.makeRequest('DELETE', `/api/v1/webhooks/${webhookId}/link`);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
throw new Error(`Failed to unlink webhook: ${error}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
147
221
|
// Generic request method for custom API calls
|
|
148
222
|
async request(method, endpoint, data, options) {
|
|
149
223
|
return this.makeRequest(method, endpoint, data, options);
|