n8n-nodes-feishu-sheets 0.1.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.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # n8n-nodes-feishu-sheets
2
+
3
+ This is an n8n community node for interacting with Feishu (Lark) Sheets.
4
+
5
+ ## Features
6
+
7
+ - **Spreadsheets**: Create, Copy, Get Info, Update Properties.
8
+ - **Sheets**: Get All, Get Metadata.
9
+ - **Values**: Read, Write, Append.
10
+ - **Custom API**: Call any Feishu API endpoint with authentication handled.
11
+
12
+ ## Credentials
13
+
14
+ You need to create a custom app in the [Feishu Open Platform](https://open.feishu.cn/app) to get:
15
+
16
+ - **App ID**
17
+ - **App Secret**
18
+
19
+ Ensure your app has the following permissions enabled:
20
+ - View/Edit Sheets
21
+ - View/Edit Spreadsheets
22
+
23
+ ## Installation
24
+
25
+ Follow the [n8n community node installation guide](https://docs.n8n.io/integrations/community-nodes/installation/).
26
+
27
+ ## License
28
+
29
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { Icon, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class FeishuApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: Icon;
6
+ documentationUrl: string;
7
+ test: ICredentialTestRequest;
8
+ properties: INodeProperties[];
9
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeishuApi = void 0;
4
+ class FeishuApi {
5
+ name = 'feishuApi';
6
+ displayName = 'Feishu API';
7
+ icon = 'file:feishu.svg';
8
+ documentationUrl = 'https://open.feishu.cn/document/home/index';
9
+ test = {
10
+ request: {
11
+ method: 'POST',
12
+ url: 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/',
13
+ body: {
14
+ app_id: '={{$credentials.appId}}',
15
+ app_secret: '={{$credentials.appSecret}}',
16
+ },
17
+ json: true,
18
+ },
19
+ };
20
+ properties = [
21
+ {
22
+ displayName: 'App ID',
23
+ name: 'appId',
24
+ type: 'string',
25
+ default: '',
26
+ required: true,
27
+ },
28
+ {
29
+ displayName: 'App Secret',
30
+ name: 'appSecret',
31
+ type: 'string',
32
+ typeOptions: {
33
+ password: true,
34
+ },
35
+ default: '',
36
+ required: true,
37
+ },
38
+ ];
39
+ }
40
+ exports.FeishuApi = FeishuApi;
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <path fill="#00C3FF" d="M32 6c14.36 0 26 11.64 26 26S46.36 58 32 58 6 46.36 6 32 17.64 6 32 6z"/>
3
+ <path fill="#fff" d="M18 22h28v6H18zm0 14h18v6H18z"/>
4
+ </svg>
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class FeishuSheets implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,574 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeishuSheets = void 0;
4
+ const GenericFunctions_1 = require("./GenericFunctions");
5
+ function parseJson(value, fieldName) {
6
+ if (!value)
7
+ return {};
8
+ try {
9
+ return JSON.parse(value);
10
+ }
11
+ catch {
12
+ throw new Error(`${fieldName} must be valid JSON`);
13
+ }
14
+ }
15
+ class FeishuSheets {
16
+ description = {
17
+ displayName: 'Feishu Sheets',
18
+ name: 'feishuSheets',
19
+ icon: 'file:feishu.svg',
20
+ group: ['transform'],
21
+ version: 1,
22
+ description: 'Operate Feishu (Lark) spreadsheets',
23
+ defaults: {
24
+ name: 'Feishu Sheets',
25
+ },
26
+ inputs: ['main'],
27
+ outputs: ['main'],
28
+ usableAsTool: true,
29
+ credentials: [
30
+ {
31
+ name: 'feishuApi',
32
+ required: true,
33
+ },
34
+ ],
35
+ properties: [
36
+ {
37
+ displayName: 'Resource',
38
+ name: 'resource',
39
+ type: 'options',
40
+ options: [
41
+ {
42
+ name: 'Spreadsheet',
43
+ value: 'spreadsheet',
44
+ },
45
+ {
46
+ name: 'Sheet',
47
+ value: 'sheet',
48
+ },
49
+ {
50
+ name: 'Values',
51
+ value: 'values',
52
+ },
53
+ {
54
+ name: 'Custom API',
55
+ value: 'custom',
56
+ },
57
+ ],
58
+ default: 'spreadsheet',
59
+ },
60
+ {
61
+ displayName: 'Operation',
62
+ name: 'operation',
63
+ type: 'options',
64
+ displayOptions: {
65
+ show: {
66
+ resource: ['spreadsheet'],
67
+ },
68
+ },
69
+ options: [
70
+ {
71
+ name: 'Create',
72
+ value: 'create',
73
+ },
74
+ {
75
+ name: 'Get Info',
76
+ value: 'getInfo',
77
+ },
78
+ {
79
+ name: 'Copy',
80
+ value: 'copy',
81
+ },
82
+ {
83
+ name: 'Get Sheets',
84
+ value: 'getSheets',
85
+ },
86
+ {
87
+ name: 'Update Properties',
88
+ value: 'updateProperties',
89
+ },
90
+ ],
91
+ default: 'create',
92
+ },
93
+ {
94
+ displayName: 'Title',
95
+ name: 'title',
96
+ type: 'string',
97
+ default: '',
98
+ required: true,
99
+ displayOptions: {
100
+ show: {
101
+ resource: ['spreadsheet'],
102
+ operation: ['create'],
103
+ },
104
+ },
105
+ },
106
+ {
107
+ displayName: 'Folder Token',
108
+ name: 'folderToken',
109
+ type: 'string',
110
+ default: '',
111
+ displayOptions: {
112
+ show: {
113
+ resource: ['spreadsheet'],
114
+ operation: ['create'],
115
+ },
116
+ },
117
+ },
118
+ {
119
+ displayName: 'Spreadsheet Token',
120
+ name: 'spreadsheetToken',
121
+ type: 'string',
122
+ default: '',
123
+ required: true,
124
+ displayOptions: {
125
+ show: {
126
+ resource: ['spreadsheet'],
127
+ operation: ['getInfo', 'getSheets', 'updateProperties'],
128
+ },
129
+ },
130
+ },
131
+ {
132
+ displayName: 'Source File Token',
133
+ name: 'fileToken',
134
+ type: 'string',
135
+ default: '',
136
+ required: true,
137
+ displayOptions: {
138
+ show: {
139
+ resource: ['spreadsheet'],
140
+ operation: ['copy'],
141
+ },
142
+ },
143
+ },
144
+ {
145
+ displayName: 'Destination Folder Token',
146
+ name: 'destinationFolderToken',
147
+ type: 'string',
148
+ default: '',
149
+ displayOptions: {
150
+ show: {
151
+ resource: ['spreadsheet'],
152
+ operation: ['copy'],
153
+ },
154
+ },
155
+ },
156
+ {
157
+ displayName: 'New Title',
158
+ name: 'newTitle',
159
+ type: 'string',
160
+ default: '',
161
+ displayOptions: {
162
+ show: {
163
+ resource: ['spreadsheet'],
164
+ operation: ['copy', 'updateProperties'],
165
+ },
166
+ },
167
+ },
168
+ {
169
+ displayName: 'Operation',
170
+ name: 'operation',
171
+ type: 'options',
172
+ displayOptions: {
173
+ show: {
174
+ resource: ['sheet'],
175
+ },
176
+ },
177
+ options: [
178
+ {
179
+ name: 'Add',
180
+ value: 'add',
181
+ },
182
+ {
183
+ name: 'Copy',
184
+ value: 'copy',
185
+ },
186
+ {
187
+ name: 'Delete',
188
+ value: 'delete',
189
+ },
190
+ {
191
+ name: 'Get',
192
+ value: 'get',
193
+ },
194
+ ],
195
+ default: 'add',
196
+ },
197
+ {
198
+ displayName: 'Spreadsheet Token',
199
+ name: 'spreadsheetToken',
200
+ type: 'string',
201
+ default: '',
202
+ required: true,
203
+ displayOptions: {
204
+ show: {
205
+ resource: ['sheet'],
206
+ },
207
+ },
208
+ },
209
+ {
210
+ displayName: 'Sheet Title',
211
+ name: 'sheetTitle',
212
+ type: 'string',
213
+ default: '',
214
+ displayOptions: {
215
+ show: {
216
+ resource: ['sheet'],
217
+ operation: ['add', 'copy'],
218
+ },
219
+ },
220
+ },
221
+ {
222
+ displayName: 'Source Sheet ID',
223
+ name: 'sheetId',
224
+ type: 'string',
225
+ default: '',
226
+ required: true,
227
+ displayOptions: {
228
+ show: {
229
+ resource: ['sheet'],
230
+ operation: ['copy', 'delete', 'get'],
231
+ },
232
+ },
233
+ },
234
+ {
235
+ displayName: 'Operation',
236
+ name: 'operation',
237
+ type: 'options',
238
+ displayOptions: {
239
+ show: {
240
+ resource: ['values'],
241
+ },
242
+ },
243
+ options: [
244
+ {
245
+ name: 'Read Range',
246
+ value: 'read',
247
+ },
248
+ {
249
+ name: 'Write Range',
250
+ value: 'write',
251
+ },
252
+ {
253
+ name: 'Append',
254
+ value: 'append',
255
+ },
256
+ ],
257
+ default: 'read',
258
+ },
259
+ {
260
+ displayName: 'Spreadsheet Token',
261
+ name: 'spreadsheetToken',
262
+ type: 'string',
263
+ default: '',
264
+ required: true,
265
+ displayOptions: {
266
+ show: {
267
+ resource: ['values'],
268
+ },
269
+ },
270
+ },
271
+ {
272
+ displayName: 'Range',
273
+ name: 'range',
274
+ type: 'string',
275
+ default: '',
276
+ required: true,
277
+ displayOptions: {
278
+ show: {
279
+ resource: ['values'],
280
+ },
281
+ },
282
+ },
283
+ {
284
+ displayName: 'Values (JSON)',
285
+ name: 'valuesJson',
286
+ type: 'string',
287
+ default: '[["Hello", 1], ["World", 2]]',
288
+ required: true,
289
+ displayOptions: {
290
+ show: {
291
+ resource: ['values'],
292
+ operation: ['write', 'append'],
293
+ },
294
+ },
295
+ },
296
+ {
297
+ displayName: 'Insert Data Option',
298
+ name: 'insertDataOption',
299
+ type: 'options',
300
+ default: 'OVERWRITE',
301
+ displayOptions: {
302
+ show: {
303
+ resource: ['values'],
304
+ operation: ['append'],
305
+ },
306
+ },
307
+ options: [
308
+ {
309
+ name: 'Overwrite',
310
+ value: 'OVERWRITE',
311
+ },
312
+ {
313
+ name: 'Insert Rows',
314
+ value: 'INSERT_ROWS',
315
+ },
316
+ ],
317
+ },
318
+ {
319
+ displayName: 'Operation',
320
+ name: 'operation',
321
+ type: 'options',
322
+ displayOptions: {
323
+ show: {
324
+ resource: ['custom'],
325
+ },
326
+ },
327
+ options: [
328
+ {
329
+ name: 'Request',
330
+ value: 'request',
331
+ },
332
+ ],
333
+ default: 'request',
334
+ },
335
+ {
336
+ displayName: 'Method',
337
+ name: 'method',
338
+ type: 'options',
339
+ default: 'GET',
340
+ displayOptions: {
341
+ show: {
342
+ resource: ['custom'],
343
+ operation: ['request'],
344
+ },
345
+ },
346
+ options: [
347
+ {
348
+ name: 'GET',
349
+ value: 'GET',
350
+ },
351
+ {
352
+ name: 'POST',
353
+ value: 'POST',
354
+ },
355
+ {
356
+ name: 'PUT',
357
+ value: 'PUT',
358
+ },
359
+ {
360
+ name: 'DELETE',
361
+ value: 'DELETE',
362
+ },
363
+ {
364
+ name: 'PATCH',
365
+ value: 'PATCH',
366
+ },
367
+ ],
368
+ },
369
+ {
370
+ displayName: 'Endpoint',
371
+ name: 'endpoint',
372
+ type: 'string',
373
+ default: '/open-apis/sheets/v3/spreadsheets',
374
+ required: true,
375
+ displayOptions: {
376
+ show: {
377
+ resource: ['custom'],
378
+ operation: ['request'],
379
+ },
380
+ },
381
+ },
382
+ {
383
+ displayName: 'Query (JSON)',
384
+ name: 'queryJson',
385
+ type: 'string',
386
+ default: '{}',
387
+ displayOptions: {
388
+ show: {
389
+ resource: ['custom'],
390
+ operation: ['request'],
391
+ },
392
+ },
393
+ },
394
+ {
395
+ displayName: 'Headers (JSON)',
396
+ name: 'headersJson',
397
+ type: 'string',
398
+ default: '{}',
399
+ displayOptions: {
400
+ show: {
401
+ resource: ['custom'],
402
+ operation: ['request'],
403
+ },
404
+ },
405
+ },
406
+ {
407
+ displayName: 'Body (JSON)',
408
+ name: 'bodyJson',
409
+ type: 'string',
410
+ default: '{}',
411
+ displayOptions: {
412
+ show: {
413
+ resource: ['custom'],
414
+ operation: ['request'],
415
+ },
416
+ },
417
+ },
418
+ ],
419
+ };
420
+ async execute() {
421
+ const items = this.getInputData();
422
+ const returnData = [];
423
+ for (let i = 0; i < items.length; i++) {
424
+ const resource = this.getNodeParameter('resource', i);
425
+ const operation = this.getNodeParameter('operation', i);
426
+ if (resource === 'spreadsheet') {
427
+ if (operation === 'create') {
428
+ const title = this.getNodeParameter('title', i);
429
+ const folderToken = this.getNodeParameter('folderToken', i, '');
430
+ const body = { title };
431
+ if (folderToken)
432
+ body.folder_token = folderToken;
433
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', '/open-apis/sheets/v3/spreadsheets', {}, body);
434
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
435
+ }
436
+ if (operation === 'getInfo') {
437
+ const spreadsheetToken = this.getNodeParameter('spreadsheetToken', i);
438
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'GET', `/open-apis/sheets/v3/spreadsheets/${encodeURIComponent(spreadsheetToken)}`);
439
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
440
+ }
441
+ if (operation === 'copy') {
442
+ const fileToken = this.getNodeParameter('fileToken', i);
443
+ const destinationFolderToken = this.getNodeParameter('destinationFolderToken', i, '');
444
+ const newTitle = this.getNodeParameter('newTitle', i, '');
445
+ const body = {};
446
+ if (destinationFolderToken)
447
+ body.folder_token = destinationFolderToken;
448
+ if (newTitle)
449
+ body.name = newTitle;
450
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', `/open-apis/drive/v1/files/${encodeURIComponent(fileToken)}/copy`, {}, body);
451
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
452
+ }
453
+ if (operation === 'getSheets') {
454
+ const spreadsheetToken = this.getNodeParameter('spreadsheetToken', i);
455
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'GET', `/open-apis/sheets/v3/spreadsheets/${encodeURIComponent(spreadsheetToken)}/sheets/query`);
456
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
457
+ }
458
+ if (operation === 'updateProperties') {
459
+ const spreadsheetToken = this.getNodeParameter('spreadsheetToken', i);
460
+ const newTitle = this.getNodeParameter('newTitle', i, '');
461
+ const body = {
462
+ properties: {},
463
+ };
464
+ if (newTitle)
465
+ body.properties.title = newTitle;
466
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'PUT', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/properties`, {}, body);
467
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
468
+ }
469
+ }
470
+ if (resource === 'sheet') {
471
+ const spreadsheetToken = this.getNodeParameter('spreadsheetToken', i);
472
+ if (operation === 'add') {
473
+ const sheetTitle = this.getNodeParameter('sheetTitle', i, '');
474
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/sheets_batch_update`, {}, {
475
+ requests: [
476
+ {
477
+ addSheet: {
478
+ properties: {
479
+ title: sheetTitle,
480
+ },
481
+ },
482
+ },
483
+ ],
484
+ });
485
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
486
+ }
487
+ if (operation === 'copy') {
488
+ const sheetId = this.getNodeParameter('sheetId', i);
489
+ const sheetTitle = this.getNodeParameter('sheetTitle', i, '');
490
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/sheets_batch_update`, {}, {
491
+ requests: [
492
+ {
493
+ copySheet: {
494
+ source: {
495
+ sheetId,
496
+ },
497
+ destination: {
498
+ title: sheetTitle,
499
+ },
500
+ },
501
+ },
502
+ ],
503
+ });
504
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
505
+ }
506
+ if (operation === 'delete') {
507
+ const sheetId = this.getNodeParameter('sheetId', i);
508
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/sheets_batch_update`, {}, {
509
+ requests: [
510
+ {
511
+ deleteSheet: {
512
+ sheetId,
513
+ },
514
+ },
515
+ ],
516
+ });
517
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
518
+ }
519
+ if (operation === 'get') {
520
+ const sheetId = this.getNodeParameter('sheetId', i);
521
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'GET', `/open-apis/sheets/v3/spreadsheets/${encodeURIComponent(spreadsheetToken)}/sheets/${encodeURIComponent(sheetId)}`);
522
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
523
+ }
524
+ }
525
+ if (resource === 'values') {
526
+ const spreadsheetToken = this.getNodeParameter('spreadsheetToken', i);
527
+ const range = this.getNodeParameter('range', i);
528
+ if (operation === 'read') {
529
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'GET', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/values/${encodeURIComponent(range)}`);
530
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
531
+ }
532
+ if (operation === 'write') {
533
+ const valuesJson = this.getNodeParameter('valuesJson', i);
534
+ const values = parseJson(valuesJson, 'Values (JSON)');
535
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'PUT', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/values`, {}, {
536
+ valueRange: {
537
+ range,
538
+ values,
539
+ },
540
+ });
541
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
542
+ }
543
+ if (operation === 'append') {
544
+ const valuesJson = this.getNodeParameter('valuesJson', i);
545
+ const values = parseJson(valuesJson, 'Values (JSON)');
546
+ const insertDataOption = this.getNodeParameter('insertDataOption', i);
547
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, 'POST', `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/values_append`, {
548
+ insertDataOption,
549
+ }, {
550
+ valueRange: {
551
+ range,
552
+ values,
553
+ },
554
+ });
555
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
556
+ }
557
+ }
558
+ if (resource === 'custom') {
559
+ const method = this.getNodeParameter('method', i);
560
+ const endpoint = this.getNodeParameter('endpoint', i);
561
+ const queryJson = this.getNodeParameter('queryJson', i, '');
562
+ const headersJson = this.getNodeParameter('headersJson', i, '');
563
+ const bodyJson = this.getNodeParameter('bodyJson', i, '');
564
+ const qs = parseJson(queryJson, 'Query (JSON)');
565
+ const headers = parseJson(headersJson, 'Headers (JSON)');
566
+ const body = bodyJson ? parseJson(bodyJson, 'Body (JSON)') : undefined;
567
+ const response = await GenericFunctions_1.feishuApiRequest.call(this, method, endpoint, qs, body, headers);
568
+ returnData.push(...(0, GenericFunctions_1.wrapAsItems)(response));
569
+ }
570
+ }
571
+ return [returnData];
572
+ }
573
+ }
574
+ exports.FeishuSheets = FeishuSheets;
@@ -0,0 +1,3 @@
1
+ import type { IDataObject, IExecuteFunctions, IHookFunctions, ILoadOptionsFunctions, INodeExecutionData, IHttpRequestMethods } from 'n8n-workflow';
2
+ export declare function feishuApiRequest(this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, qs?: IDataObject, body?: IDataObject | IDataObject[] | string | undefined, headers?: IDataObject): Promise<any>;
3
+ export declare function wrapAsItems(data: unknown): INodeExecutionData[];
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.feishuApiRequest = feishuApiRequest;
4
+ exports.wrapAsItems = wrapAsItems;
5
+ function getNowMs() {
6
+ return Date.now();
7
+ }
8
+ async function getTenantAccessToken() {
9
+ const credentials = await this.getCredentials('feishuApi');
10
+ const appId = credentials.appId;
11
+ const appSecret = credentials.appSecret;
12
+ const cacheKey = `feishuTenantAccessToken:${appId}`;
13
+ const staticData = this.getWorkflowStaticData('global');
14
+ const cached = staticData[cacheKey];
15
+ if (cached && cached.expiresAtMs > getNowMs()) {
16
+ return cached.token;
17
+ }
18
+ const options = {
19
+ method: 'POST',
20
+ url: 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/',
21
+ body: {
22
+ app_id: appId,
23
+ app_secret: appSecret,
24
+ },
25
+ json: true,
26
+ };
27
+ const response = (await this.helpers.httpRequest(options));
28
+ if (!response?.tenant_access_token) {
29
+ throw new Error(`Failed to get tenant_access_token: ${response?.code ?? 'unknown'} ${response?.msg ?? ''}`);
30
+ }
31
+ const expireSeconds = typeof response.expire === 'number' ? response.expire : 3600;
32
+ staticData[cacheKey] = {
33
+ token: response.tenant_access_token,
34
+ expiresAtMs: getNowMs() + Math.max(0, expireSeconds - 60) * 1000,
35
+ };
36
+ return response.tenant_access_token;
37
+ }
38
+ async function feishuApiRequest(method, endpoint, qs = {}, body = undefined, headers = {}) {
39
+ const token = await getTenantAccessToken.call(this);
40
+ const options = {
41
+ method,
42
+ url: `https://open.feishu.cn${endpoint}`,
43
+ qs,
44
+ body: body,
45
+ headers: {
46
+ Authorization: `Bearer ${token}`,
47
+ 'Content-Type': 'application/json; charset=utf-8',
48
+ ...headers,
49
+ },
50
+ json: true,
51
+ };
52
+ return await this.helpers.httpRequest(options);
53
+ }
54
+ function wrapAsItems(data) {
55
+ if (Array.isArray(data)) {
56
+ return data.map((json) => ({ json: json }));
57
+ }
58
+ return [{ json: data }];
59
+ }
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <path fill="#00C3FF" d="M32 6c14.36 0 26 11.64 26 26S46.36 58 32 58 6 46.36 6 32 17.64 6 32 6z"/>
3
+ <path fill="#fff" d="M18 22h28v6H18zm0 14h18v6H18z"/>
4
+ </svg>
5
+
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "n8n-nodes-feishu-sheets",
3
+ "version": "0.1.0",
4
+ "description": "Feishu (Lark) Sheets community node for n8n",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "n8n-community-node-package",
8
+ "n8n",
9
+ "feishu",
10
+ "lark",
11
+ "sheets"
12
+ ],
13
+ "homepage": "",
14
+ "author": "",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": ""
18
+ },
19
+ "main": "dist/index.js",
20
+ "types": "dist/index.d.ts",
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json && node ./scripts/copy-assets.mjs",
26
+ "dev": "n8n-node dev",
27
+ "lint": "eslint .",
28
+ "lint:fix": "eslint . --fix",
29
+ "typecheck": "tsc -p tsconfig.json --noEmit"
30
+ },
31
+ "n8n": {
32
+ "n8nNodesApiVersion": 1,
33
+ "credentials": [
34
+ "dist/credentials/FeishuApi.credentials.js"
35
+ ],
36
+ "nodes": [
37
+ "dist/nodes/FeishuSheets/FeishuSheets.node.js"
38
+ ]
39
+ },
40
+ "devDependencies": {
41
+ "@n8n/node-cli": "^0.19.0",
42
+ "@n8n/eslint-plugin-community-nodes": "^0.7.0",
43
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
44
+ "@typescript-eslint/parser": "^8.55.0",
45
+ "eslint": "^9.39.2",
46
+ "n8n-workflow": "*",
47
+ "typescript": "^5.5.0"
48
+ },
49
+ "peerDependencies": {
50
+ "n8n-workflow": "*"
51
+ },
52
+ "overrides": {
53
+ "change-case": "4.1.2"
54
+ }
55
+ }