@fachkraftfreund/n8n-nodes-supabase 1.2.0

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.
@@ -0,0 +1,15 @@
1
+ <svg width="109" height="113" viewBox="0 0 109 113" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint0_linear)"/>
3
+ <path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint1_linear)" fill-opacity="0.2"/>
4
+ <path d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z" fill="#3ECF8E"/>
5
+ <defs>
6
+ <linearGradient id="paint0_linear" x1="53.9738" y1="54.974" x2="94.1635" y2="71.8295" gradientUnits="userSpaceOnUse">
7
+ <stop stop-color="#249361"/>
8
+ <stop offset="1" stop-color="#3ECF8E"/>
9
+ </linearGradient>
10
+ <linearGradient id="paint1_linear" x1="36.1558" y1="30.578" x2="54.4844" y2="65.0806" gradientUnits="userSpaceOnUse">
11
+ <stop/>
12
+ <stop offset="1" stop-opacity="0"/>
13
+ </linearGradient>
14
+ </defs>
15
+ </svg>
@@ -0,0 +1,4 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
3
+ import { DatabaseOperation } from '../../types';
4
+ export declare function executeDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemIndex: number): Promise<INodeExecutionData[]>;
@@ -0,0 +1,388 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeDatabaseOperation = void 0;
4
+ const supabaseClient_1 = require("../../utils/supabaseClient");
5
+ async function executeDatabaseOperation(supabase, operation, itemIndex) {
6
+ const returnData = [];
7
+ try {
8
+ switch (operation) {
9
+ case 'create':
10
+ returnData.push(...await handleCreate.call(this, supabase, itemIndex));
11
+ break;
12
+ case 'read':
13
+ returnData.push(...await handleRead.call(this, supabase, itemIndex));
14
+ break;
15
+ case 'update':
16
+ returnData.push(...await handleUpdate.call(this, supabase, itemIndex));
17
+ break;
18
+ case 'delete':
19
+ returnData.push(...await handleDelete.call(this, supabase, itemIndex));
20
+ break;
21
+ case 'upsert':
22
+ returnData.push(...await handleUpsert.call(this, supabase, itemIndex));
23
+ break;
24
+ case 'createTable':
25
+ returnData.push(...await handleCreateTable.call(this, supabase, itemIndex));
26
+ break;
27
+ case 'dropTable':
28
+ returnData.push(...await handleDropTable.call(this, supabase, itemIndex));
29
+ break;
30
+ case 'addColumn':
31
+ returnData.push(...await handleAddColumn.call(this, supabase, itemIndex));
32
+ break;
33
+ case 'dropColumn':
34
+ returnData.push(...await handleDropColumn.call(this, supabase, itemIndex));
35
+ break;
36
+ case 'createIndex':
37
+ returnData.push(...await handleCreateIndex.call(this, supabase, itemIndex));
38
+ break;
39
+ case 'dropIndex':
40
+ returnData.push(...await handleDropIndex.call(this, supabase, itemIndex));
41
+ break;
42
+ case 'customQuery':
43
+ returnData.push(...await handleCustomQuery.call(this, supabase, itemIndex));
44
+ break;
45
+ default:
46
+ throw new Error(`Unknown database operation: ${operation}`);
47
+ }
48
+ }
49
+ catch (error) {
50
+ throw new Error(`Database operation failed: ${(0, supabaseClient_1.formatSupabaseError)(error)}`);
51
+ }
52
+ return returnData;
53
+ }
54
+ exports.executeDatabaseOperation = executeDatabaseOperation;
55
+ async function handleCreate(supabase, itemIndex) {
56
+ const table = this.getNodeParameter('table', itemIndex);
57
+ const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
58
+ (0, supabaseClient_1.validateTableName)(table);
59
+ let dataToInsert;
60
+ if (uiMode === 'advanced') {
61
+ const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
62
+ try {
63
+ dataToInsert = JSON.parse(jsonData);
64
+ }
65
+ catch {
66
+ throw new Error('Invalid JSON data provided');
67
+ }
68
+ }
69
+ else {
70
+ const columns = this.getNodeParameter('columns.column', itemIndex, []);
71
+ dataToInsert = {};
72
+ for (const column of columns) {
73
+ if (column.name && column.value !== undefined) {
74
+ dataToInsert[column.name] = column.value;
75
+ }
76
+ }
77
+ }
78
+ const { data, error } = await supabase
79
+ .from(table)
80
+ .insert(dataToInsert)
81
+ .select();
82
+ if (error) {
83
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
84
+ }
85
+ return [{ json: { data, operation: 'create', table } }];
86
+ }
87
+ async function handleRead(supabase, itemIndex) {
88
+ const table = this.getNodeParameter('table', itemIndex);
89
+ const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
90
+ (0, supabaseClient_1.validateTableName)(table);
91
+ let query = supabase.from(table).select('*');
92
+ const returnFields = this.getNodeParameter('returnFields', itemIndex, '*');
93
+ if (returnFields && returnFields !== '*') {
94
+ query = supabase.from(table).select(returnFields);
95
+ }
96
+ if (uiMode === 'simple') {
97
+ const filters = this.getNodeParameter('filters.filter', itemIndex, []);
98
+ for (const filter of filters) {
99
+ const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
100
+ query = query.filter(filter.column, operator, filter.value);
101
+ }
102
+ }
103
+ else {
104
+ const advancedFilters = this.getNodeParameter('advancedFilters', itemIndex, '');
105
+ if (advancedFilters) {
106
+ try {
107
+ const filters = JSON.parse(advancedFilters);
108
+ for (const [column, condition] of Object.entries(filters)) {
109
+ if (typeof condition === 'object' && condition !== null) {
110
+ const [operator, value] = Object.entries(condition)[0];
111
+ query = query.filter(column, (0, supabaseClient_1.convertFilterOperator)(operator), value);
112
+ }
113
+ else {
114
+ query = query.eq(column, condition);
115
+ }
116
+ }
117
+ }
118
+ catch {
119
+ throw new Error('Invalid advanced filters JSON');
120
+ }
121
+ }
122
+ }
123
+ const sort = this.getNodeParameter('sort.sortField', itemIndex, []);
124
+ for (const sortField of sort) {
125
+ query = query.order(sortField.column, { ascending: sortField.ascending });
126
+ }
127
+ const limit = this.getNodeParameter('limit', itemIndex, undefined);
128
+ const offset = this.getNodeParameter('offset', itemIndex, undefined);
129
+ if (limit !== undefined) {
130
+ query = query.limit(limit);
131
+ }
132
+ if (offset !== undefined) {
133
+ query = query.range(offset, offset + (limit || 1000) - 1);
134
+ }
135
+ const { data, error, count } = await query;
136
+ if (error) {
137
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
138
+ }
139
+ const returnData = [];
140
+ if (Array.isArray(data)) {
141
+ for (const row of data) {
142
+ returnData.push({ json: row });
143
+ }
144
+ }
145
+ if (returnData.length === 0) {
146
+ returnData.push({
147
+ json: {
148
+ data: [],
149
+ count: count || 0,
150
+ operation: 'read',
151
+ table,
152
+ message: 'No records found',
153
+ },
154
+ });
155
+ }
156
+ return returnData;
157
+ }
158
+ async function handleUpdate(supabase, itemIndex) {
159
+ const table = this.getNodeParameter('table', itemIndex);
160
+ const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
161
+ (0, supabaseClient_1.validateTableName)(table);
162
+ let dataToUpdate;
163
+ if (uiMode === 'advanced') {
164
+ const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
165
+ try {
166
+ dataToUpdate = JSON.parse(jsonData);
167
+ }
168
+ catch {
169
+ throw new Error('Invalid JSON data provided');
170
+ }
171
+ }
172
+ else {
173
+ const columns = this.getNodeParameter('columns.column', itemIndex, []);
174
+ dataToUpdate = {};
175
+ for (const column of columns) {
176
+ if (column.name && column.value !== undefined) {
177
+ dataToUpdate[column.name] = column.value;
178
+ }
179
+ }
180
+ }
181
+ let query = supabase.from(table).update(dataToUpdate);
182
+ const filters = this.getNodeParameter('filters.filter', itemIndex, []);
183
+ for (const filter of filters) {
184
+ const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
185
+ query = query.filter(filter.column, operator, filter.value);
186
+ }
187
+ const { data, error } = await query.select();
188
+ if (error) {
189
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
190
+ }
191
+ return [{ json: { data, operation: 'update', table, updated: (data === null || data === void 0 ? void 0 : data.length) || 0 } }];
192
+ }
193
+ async function handleDelete(supabase, itemIndex) {
194
+ const table = this.getNodeParameter('table', itemIndex);
195
+ (0, supabaseClient_1.validateTableName)(table);
196
+ let query = supabase.from(table).delete();
197
+ const filters = this.getNodeParameter('filters.filter', itemIndex, []);
198
+ if (filters.length === 0) {
199
+ throw new Error('At least one filter is required for delete operations to prevent accidental data loss');
200
+ }
201
+ for (const filter of filters) {
202
+ const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
203
+ query = query.filter(filter.column, operator, filter.value);
204
+ }
205
+ const { data, error } = await query.select();
206
+ if (error) {
207
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
208
+ }
209
+ return [{ json: { data, operation: 'delete', table, deleted: (data === null || data === void 0 ? void 0 : data.length) || 0 } }];
210
+ }
211
+ async function handleUpsert(supabase, itemIndex) {
212
+ const table = this.getNodeParameter('table', itemIndex);
213
+ const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
214
+ const onConflict = this.getNodeParameter('onConflict', itemIndex, '');
215
+ (0, supabaseClient_1.validateTableName)(table);
216
+ let dataToUpsert;
217
+ if (uiMode === 'advanced') {
218
+ const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
219
+ try {
220
+ dataToUpsert = JSON.parse(jsonData);
221
+ }
222
+ catch {
223
+ throw new Error('Invalid JSON data provided');
224
+ }
225
+ }
226
+ else {
227
+ const columns = this.getNodeParameter('columns.column', itemIndex, []);
228
+ dataToUpsert = {};
229
+ for (const column of columns) {
230
+ if (column.name && column.value !== undefined) {
231
+ dataToUpsert[column.name] = column.value;
232
+ }
233
+ }
234
+ }
235
+ const options = {};
236
+ if (onConflict) {
237
+ options.onConflict = onConflict;
238
+ }
239
+ const { data, error } = await supabase
240
+ .from(table)
241
+ .upsert(dataToUpsert, options)
242
+ .select();
243
+ if (error) {
244
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
245
+ }
246
+ return [{ json: { data, operation: 'upsert', table } }];
247
+ }
248
+ async function handleCreateTable(supabase, itemIndex) {
249
+ const tableName = this.getNodeParameter('tableName', itemIndex);
250
+ const columns = this.getNodeParameter('columnDefinitions.column', itemIndex, []);
251
+ (0, supabaseClient_1.validateTableName)(tableName);
252
+ if (columns.length === 0) {
253
+ throw new Error('At least one column definition is required');
254
+ }
255
+ let sql = `CREATE TABLE "${tableName}" (`;
256
+ const columnDefs = [];
257
+ for (const column of columns) {
258
+ (0, supabaseClient_1.validateColumnName)(column.name);
259
+ let columnDef = `"${column.name}" ${column.type}`;
260
+ if (!column.nullable) {
261
+ columnDef += ' NOT NULL';
262
+ }
263
+ if (column.defaultValue) {
264
+ columnDef += ` DEFAULT ${column.defaultValue}`;
265
+ }
266
+ if (column.primaryKey) {
267
+ columnDef += ' PRIMARY KEY';
268
+ }
269
+ if (column.unique && !column.primaryKey) {
270
+ columnDef += ' UNIQUE';
271
+ }
272
+ columnDefs.push(columnDef);
273
+ }
274
+ sql += columnDefs.join(', ') + ')';
275
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
276
+ if (error) {
277
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
278
+ }
279
+ return [{ json: { operation: 'createTable', tableName, sql, success: true } }];
280
+ }
281
+ async function handleDropTable(supabase, itemIndex) {
282
+ const tableName = this.getNodeParameter('tableName', itemIndex);
283
+ const cascade = this.getNodeParameter('cascade', itemIndex, false);
284
+ (0, supabaseClient_1.validateTableName)(tableName);
285
+ const sql = `DROP TABLE "${tableName}"${cascade ? ' CASCADE' : ''}`;
286
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
287
+ if (error) {
288
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
289
+ }
290
+ return [{ json: { operation: 'dropTable', tableName, sql, success: true } }];
291
+ }
292
+ async function handleAddColumn(supabase, itemIndex) {
293
+ const tableName = this.getNodeParameter('tableName', itemIndex);
294
+ const columnDefinition = this.getNodeParameter('columnDefinition', itemIndex);
295
+ (0, supabaseClient_1.validateTableName)(tableName);
296
+ (0, supabaseClient_1.validateColumnName)(columnDefinition.name);
297
+ let sql = `ALTER TABLE "${tableName}" ADD COLUMN "${columnDefinition.name}" ${columnDefinition.type}`;
298
+ if (!columnDefinition.nullable) {
299
+ sql += ' NOT NULL';
300
+ }
301
+ if (columnDefinition.defaultValue) {
302
+ sql += ` DEFAULT ${columnDefinition.defaultValue}`;
303
+ }
304
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
305
+ if (error) {
306
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
307
+ }
308
+ return [{ json: { operation: 'addColumn', tableName, columnName: columnDefinition.name, sql, success: true } }];
309
+ }
310
+ async function handleDropColumn(supabase, itemIndex) {
311
+ const tableName = this.getNodeParameter('tableName', itemIndex);
312
+ const columnName = this.getNodeParameter('columnName', itemIndex);
313
+ const cascade = this.getNodeParameter('cascade', itemIndex, false);
314
+ (0, supabaseClient_1.validateTableName)(tableName);
315
+ (0, supabaseClient_1.validateColumnName)(columnName);
316
+ const sql = `ALTER TABLE "${tableName}" DROP COLUMN "${columnName}"${cascade ? ' CASCADE' : ''}`;
317
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
318
+ if (error) {
319
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
320
+ }
321
+ return [{ json: { operation: 'dropColumn', tableName, columnName, sql, success: true } }];
322
+ }
323
+ async function handleCreateIndex(supabase, itemIndex) {
324
+ const tableName = this.getNodeParameter('tableName', itemIndex);
325
+ const indexDefinition = this.getNodeParameter('indexDefinition', itemIndex);
326
+ (0, supabaseClient_1.validateTableName)(tableName);
327
+ if (!indexDefinition.name || indexDefinition.columns.length === 0) {
328
+ throw new Error('Index name and columns are required');
329
+ }
330
+ const uniqueKeyword = indexDefinition.unique ? 'UNIQUE ' : '';
331
+ const method = indexDefinition.method ? ` USING ${indexDefinition.method}` : '';
332
+ const columnList = indexDefinition.columns.map(col => `"${col}"`).join(', ');
333
+ const sql = `CREATE ${uniqueKeyword}INDEX "${indexDefinition.name}" ON "${tableName}"${method} (${columnList})`;
334
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
335
+ if (error) {
336
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
337
+ }
338
+ return [{ json: { operation: 'createIndex', tableName, indexName: indexDefinition.name, sql, success: true } }];
339
+ }
340
+ async function handleDropIndex(supabase, itemIndex) {
341
+ const indexName = this.getNodeParameter('indexName', itemIndex);
342
+ const cascade = this.getNodeParameter('cascade', itemIndex, false);
343
+ if (!indexName) {
344
+ throw new Error('Index name is required');
345
+ }
346
+ const sql = `DROP INDEX "${indexName}"${cascade ? ' CASCADE' : ''}`;
347
+ const { data, error } = await supabase.rpc('exec_sql', { sql });
348
+ if (error) {
349
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
350
+ }
351
+ return [{ json: { operation: 'dropIndex', indexName, sql, success: true } }];
352
+ }
353
+ async function handleCustomQuery(supabase, itemIndex) {
354
+ const customSql = this.getNodeParameter('customSql', itemIndex);
355
+ if (!(customSql === null || customSql === void 0 ? void 0 : customSql.trim())) {
356
+ throw new Error('SQL query is required');
357
+ }
358
+ if (customSql.trim().toLowerCase().startsWith('select')) {
359
+ const { data, error } = await supabase.rpc('exec_sql_select', { sql: customSql });
360
+ if (error) {
361
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
362
+ }
363
+ const returnData = [];
364
+ if (Array.isArray(data)) {
365
+ for (const row of data) {
366
+ returnData.push({ json: row });
367
+ }
368
+ }
369
+ if (returnData.length === 0) {
370
+ returnData.push({
371
+ json: {
372
+ data: [],
373
+ operation: 'customQuery',
374
+ sql: customSql,
375
+ message: 'Query executed successfully, no results returned',
376
+ },
377
+ });
378
+ }
379
+ return returnData;
380
+ }
381
+ else {
382
+ const { data, error } = await supabase.rpc('exec_sql', { sql: customSql });
383
+ if (error) {
384
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
385
+ }
386
+ return [{ json: { operation: 'customQuery', sql: customSql, result: data, success: true } }];
387
+ }
388
+ }
@@ -0,0 +1,4 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
3
+ import { StorageOperation } from '../../types';
4
+ export declare function executeStorageOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: StorageOperation, itemIndex: number): Promise<INodeExecutionData[]>;