n8n-nodes-excel-api 1.0.1 → 1.0.3

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 CHANGED
@@ -3,71 +3,73 @@
3
3
  [![npm version](https://badge.fury.io/js/n8n-nodes-excel-api.svg)](https://badge.fury.io/js/n8n-nodes-excel-api)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- n8n 社群節點,透過 API 存取 Excel 檔案,具備**並行安全保護**。完美適用於多使用者同時透過 n8n 工作流程存取相同 Excel 檔案的場景。
6
+ An n8n community node for accessing Excel files via API with **concurrent safety protection**. Perfect for scenarios where multiple users simultaneously access the same Excel file through n8n workflows.
7
7
 
8
- ## 🎯 為什麼需要這個節點?
8
+ > 📖 **[中文文檔](README_zh-tw.md)** | **[English Documentation](README.md)**
9
9
 
10
- ### 問題所在
11
- 直接在 n8n 中使用 Excel 檔案時:
12
- - ❌ 多個工作流程同時存取同一檔案會導致檔案損毀
13
- - ❌ 並行寫入時會發生資料覆蓋與遺失
14
- - ❌ 缺乏檔案鎖定機制
15
- - ❌ 難以處理多人同時提交的 Webhook 表單
10
+ ## 🎯 Why This Node?
16
11
 
17
- ### 解決方案
18
- 本節點搭配 [Excel API Server](https://github.com/code4Copilot/excel-api-server) 提供:
19
- - **檔案鎖定** - 自動佇列管理並行請求
20
- - **資料完整性** - 無資料遺失或損毀
21
- - **多使用者支援** - 完美適用於多人提交的 HTML 表單
22
- - **類似 Google Sheets 的介面** - n8n 中熟悉的操作方式
23
- - ✅ **批次操作** - 高效的大量更新
12
+ ### The Problem
13
+ When working with Excel files directly in n8n:
14
+ - Multiple workflows accessing the same file cause file corruption
15
+ - Concurrent writes lead to data overwrite and loss
16
+ - No file locking mechanism
17
+ - Difficult to handle simultaneous webhook form submissions
24
18
 
25
- ## 📦 安裝方式
19
+ ### The Solution
20
+ This node works with [Excel API Server](https://github.com/code4Copilot/excel-api-server) to provide:
21
+ - ✅ **File Locking** - Automatically queue concurrent requests
22
+ - ✅ **Data Integrity** - No data loss or corruption
23
+ - ✅ **Multi-User Support** - Perfect for multi-user HTML form submissions
24
+ - ✅ **Google Sheets-like Interface** - Familiar operations in n8n
25
+ - ✅ **Batch Operations** - Efficient bulk updates
26
26
 
27
- ### 方法 1:npm(推薦)
27
+ ## 📦 Installation
28
+
29
+ ### Method 1: npm (Recommended)
28
30
 
29
31
  ```bash
30
32
  npm install n8n-nodes-excel-api
31
33
  ```
32
34
 
33
- ### 方法 2:手動安裝
35
+ ### Method 2: Manual Installation
34
36
 
35
37
  ```bash
36
- # 1. 複製儲存庫
38
+ # 1. Clone repository
37
39
  git clone https://github.com/code4Copilot/n8n-nodes-excel-api.git
38
40
  cd n8n-nodes-excel-api
39
41
 
40
- # 2. 安裝相依套件
42
+ # 2. Install dependencies
41
43
  npm install
42
44
 
43
- # 3. 建置
45
+ # 3. Build
44
46
  npm run build
45
47
 
46
- # 4. 連結到 n8n
48
+ # 4. Link to n8n
47
49
  npm link
48
50
  cd ~/.n8n
49
51
  npm link n8n-nodes-excel-api
50
52
 
51
- # 5. 重新啟動 n8n
53
+ # 5. Restart n8n
52
54
  n8n start
53
55
  ```
54
56
 
55
- ### 方法 3:社群套件(發布後)
57
+ ### Method 3: Community Package (After Publication)
56
58
 
57
- n8n 中:
58
- 1. 前往 **設定****社群節點**
59
- 2. 點擊 **安裝**
60
- 3. 輸入:`n8n-nodes-excel-api`
61
- 4. 點擊 **安裝**
59
+ In n8n:
60
+ 1. Go to **Settings** **Community Nodes**
61
+ 2. Click **Install**
62
+ 3. Enter: `n8n-nodes-excel-api`
63
+ 4. Click **Install**
62
64
 
63
- ## 🚀 前置需求
65
+ ## 🚀 Prerequisites
64
66
 
65
- **必須先執行 Excel API Server!**
67
+ **You must run Excel API Server first!**
66
68
 
67
- 安裝並啟動 [Excel API Server](https://github.com/code4Copilot/excel-api-server)
69
+ Install and start [Excel API Server](https://github.com/code4Copilot/excel-api-server):
68
70
 
69
71
  ```bash
70
- # 使用 Docker 快速啟動
72
+ # Quick start with Docker
71
73
  docker run -d \
72
74
  -p 8000:8000 \
73
75
  -v $(pwd)/data:/app/data \
@@ -75,124 +77,153 @@ docker run -d \
75
77
  yourusername/excel-api-server
76
78
  ```
77
79
 
78
- 詳細資訊請參閱 [Excel API Server 文件](https://github.com/code4Copilot/excel-api-server)
80
+ See [Excel API Server Documentation](https://github.com/code4Copilot/excel-api-server) for details.
79
81
 
80
- ## 🔧 設定
82
+ ## 🔧 Configuration
81
83
 
82
- ### 1. 設定憑證
84
+ ### 1. Set Up Credentials
83
85
 
84
- n8n 中:
85
- 1. 前往 **憑證****新增**
86
- 2. 搜尋「Excel API
87
- 3. 填寫:
88
- - **API URL**:`http://localhost:8000`(您的 API 伺服器位址)
89
- - **API Token**:`your-secret-token`(來自 Excel API Server
90
- 4. 點擊 **儲存**
86
+ In n8n:
87
+ 1. Go to **Credentials** **New**
88
+ 2. Search for "Excel API"
89
+ 3. Fill in:
90
+ - **API URL**: `http://localhost:8000` (Your API server address)
91
+ - **API Token**: `your-secret-token` (From Excel API Server)
92
+ 4. Click **Save**
91
93
 
92
- ### 2. 將節點加入工作流程
94
+ ### 2. Add Node to Workflow
93
95
 
94
- 1. 建立或開啟工作流程
95
- 2. 點擊 **新增節點**
96
- 3. 搜尋「Excel API
97
- 4. 選擇節點
98
- 5. 選擇您的憑證
99
- 6. 設定操作
96
+ 1. Create or open a workflow
97
+ 2. Click **Add Node**
98
+ 3. Search for "Excel API"
99
+ 4. Select the node
100
+ 5. Choose your credential
101
+ 6. Configure operation
100
102
 
101
- ## 📚 操作說明
103
+ ## 📚 Operations
102
104
 
103
- ### 1. Append(附加)
104
- 在工作表末端新增一列資料。
105
+ ### 1. Append
106
+ Add a new row to the end of the sheet.
105
107
 
106
- **兩種模式:**
108
+ **Two Modes:**
107
109
 
108
- #### Object Mode(物件模式)- 推薦
109
- 使用欄位名稱對應,更安全且易於維護。
110
+ #### Object Mode - Recommended
111
+ Map values by column names, safer and easier to maintain.
110
112
 
111
- **範例:**
113
+ **Example:**
112
114
  ```json
113
115
  {
114
- "員工編號": "{{ $json.body.employeeId }}",
115
- "姓名": "{{ $json.body.name }}",
116
- "部門": "{{ $json.body.department }}",
117
- "職位": "{{ $json.body.position }}",
118
- "薪資": "{{ $json.body.salary }}"
116
+ "Employee ID": "{{ $json.body.employeeId }}",
117
+ "Name": "{{ $json.body.name }}",
118
+ "Department": "{{ $json.body.department }}",
119
+ "Position": "{{ $json.body.position }}",
120
+ "Salary": "{{ $json.body.salary }}"
119
121
  }
120
122
  ```
121
123
 
122
- **特色:**
123
- - ✅ 自動讀取 Excel 表頭(第一列)
124
- - ✅ 按照欄位名稱智能對應
125
- - ✅ 忽略未知欄位,並在回應中提示
126
- - ✅ 欄位順序可任意調整
127
- - ✅ 缺少的欄位會自動填入空值
124
+ **Features:**
125
+ - ✅ Automatically read Excel headers (first row)
126
+ - ✅ Intelligently map by column names
127
+ - ✅ Ignore unknown columns with warnings in response
128
+ - ✅ Column order can be arbitrary
129
+ - ✅ Missing columns automatically filled with empty values
128
130
 
129
- #### Array Mode(陣列模式)
130
- 依照精確的欄位順序指定值。
131
+ #### Array Mode
132
+ Specify values in exact column order.
131
133
 
132
- **範例:**
134
+ **Example:**
133
135
  ```json
134
- ["E100", "江小魚", "人資部", "經理", "70000"]
136
+ ["E100", "John Doe", "HR", "Manager", "70000"]
135
137
  ```
136
138
 
137
- **注意:** 值的順序必須與 Excel 欄位順序完全對應。
139
+ **Note:** Value order must exactly match Excel column order.
138
140
 
139
- ### 2. Read(讀取)
140
- Excel 檔案讀取資料。
141
+ ### 2. Read
142
+ Read data from Excel file.
141
143
 
142
- **參數:**
143
- - `file`:檔案名稱(例如:`employees.xlsx`)
144
- - `sheet`:工作表名稱(預設:`Sheet1`)
145
- - `range`:儲存格範圍(例如:`A1:D10`,留空讀取全部資料)
144
+ **Parameters:**
145
+ - `file`: File name (e.g., `employees.xlsx`)
146
+ - `sheet`: Sheet name (default: `Sheet1`)
147
+ - `range`: Cell range (e.g., `A1:D10`, leave empty to read all)
146
148
 
147
- **輸出:**
148
- - 若偵測到表頭,自動將第一列轉換為欄位名稱
149
- - 回傳物件陣列,以表頭作為鍵值
150
- - 若無表頭則回傳原始資料陣列
149
+ **Output:**
150
+ - Auto-convert first row to column names if headers detected
151
+ - Return array of objects with headers as keys
152
+ - Return raw data array if no headers
151
153
 
152
- ### 3. Update(更新)
153
- 更新現有列的資料。
154
+ ### 3. Update
155
+ Update existing row data.
154
156
 
155
- **識別方式:**
157
+ **Identify Methods:**
156
158
 
157
- #### 依列號(Row Number
158
- 直接指定要更新的列號(從 2 開始,第 1 列為表頭)。
159
+ #### By Row Number
160
+ Directly specify row number to update (starts from 2, row 1 is header).
159
161
 
160
- **範例:**
162
+ **Example:**
161
163
  ```json
162
164
  {
163
165
  "operation": "update",
164
166
  "identifyBy": "rowNumber",
165
167
  "rowNumber": 5,
166
168
  "valuesToSet": {
167
- "狀態": "已完成",
168
- "更新日期": "2025-12-21"
169
+ "Status": "Completed",
170
+ "Update Date": "2025-12-21"
169
171
  }
170
172
  }
171
173
  ```
172
174
 
173
- #### 依查找(Lookup
174
- 透過查找特定欄位的值來找到要更新的列。
175
+ #### By Lookup
176
+ Find rows to update by looking up specific column values.
177
+
178
+ **Process Modes:**
179
+
180
+ ##### All Matching Records - Default
181
+ Update all matching rows, suitable for batch update scenarios.
175
182
 
176
- **範例:**
183
+ **Example: Update all IT department employees**
177
184
  ```json
178
185
  {
179
186
  "operation": "update",
180
187
  "identifyBy": "lookup",
181
- "lookupColumn": "員工編號",
188
+ "lookupColumn": "Department",
189
+ "lookupValue": "IT",
190
+ "processMode": "all",
191
+ "valuesToSet": {
192
+ "Status": "Reviewed",
193
+ "Review Date": "2026-01-06"
194
+ }
195
+ }
196
+ ```
197
+
198
+ ##### First Match Only
199
+ Update only the first matching record, suitable for unique identifier lookups.
200
+
201
+ **Example: Update specific employee data**
202
+ ```json
203
+ {
204
+ "operation": "update",
205
+ "identifyBy": "lookup",
206
+ "lookupColumn": "Employee ID",
182
207
  "lookupValue": "E100",
208
+ "processMode": "first",
183
209
  "valuesToSet": {
184
- "薪資": "80000",
185
- "職位": "資深經理"
210
+ "Salary": "80000",
211
+ "Position": "Senior Manager"
186
212
  }
187
213
  }
188
214
  ```
189
215
 
190
- ### 4. Delete(刪除)
191
- 從工作表中刪除一列。
216
+ **💡 Usage Tips:**
217
+ - When looking up by unique identifiers (Employee ID, Email), use `processMode: "first"` for better performance
218
+ - Use `processMode: "all"` when batch updating multiple records
219
+ - Default is `"all"` to ensure no matching records are missed
192
220
 
193
- **識別方式:**
221
+ ### 4. Delete
222
+ Delete a row from the sheet.
194
223
 
195
- #### 依列號
224
+ **Identify Methods:**
225
+
226
+ #### By Row Number
196
227
  ```json
197
228
  {
198
229
  "operation": "delete",
@@ -201,31 +232,59 @@ docker run -d \
201
232
  }
202
233
  ```
203
234
 
204
- #### 依查找
235
+ #### By Lookup
236
+ Find rows to delete by looking up specific column values.
237
+
238
+ **Process Modes:**
239
+
240
+ ##### All Matching Records - Default
241
+ Delete all matching rows.
242
+
243
+ **Example: Delete all terminated employees**
205
244
  ```json
206
245
  {
207
246
  "operation": "delete",
208
247
  "identifyBy": "lookup",
209
- "lookupColumn": "員工編號",
210
- "lookupValue": "E100"
248
+ "lookupColumn": "Status",
249
+ "lookupValue": "Terminated",
250
+ "processMode": "all"
211
251
  }
212
252
  ```
213
253
 
214
- ### 5. Batch(批次)
215
- 一次執行多個操作(更有效率)。
254
+ ##### First Match Only
255
+ Delete only the first matching record.
216
256
 
217
- **範例:**
257
+ **Example: Delete specific employee**
258
+ ```json
259
+ {
260
+ "operation": "delete",
261
+ "identifyBy": "lookup",
262
+ "lookupColumn": "Employee ID",
263
+ "lookupValue": "E100",
264
+ "processMode": "first"
265
+ }
266
+ ```
267
+
268
+ **⚠️ Important:**
269
+ - Delete operations cannot be undone, use with caution
270
+ - When looking up by unique identifiers, use `processMode: "first"`
271
+ - Verify lookup conditions carefully when batch deleting to avoid accidental data loss
272
+
273
+ ### 5. Batch
274
+ Execute multiple operations at once (more efficient).
275
+
276
+ **Example:**
218
277
  ```json
219
278
  {
220
279
  "operations": [
221
280
  {
222
281
  "type": "append",
223
- "values": ["E010", "Alice", "行銷部", "專員", "65000"]
282
+ "values": ["E010", "Alice", "Marketing", "Specialist", "65000"]
224
283
  },
225
284
  {
226
285
  "type": "update",
227
286
  "row": 5,
228
- "values": ["E005", "Updated Name", "IT", "經理", "90000"]
287
+ "values": ["E005", "Updated Name", "IT", "Manager", "90000"]
229
288
  },
230
289
  {
231
290
  "type": "delete",
@@ -235,45 +294,43 @@ docker run -d \
235
294
  }
236
295
  ```
237
296
 
238
- ## 🎨 使用範例
297
+ ## 🎨 Usage Examples
239
298
 
240
- ## 🎨 使用範例
299
+ ### Example 1: Webhook Form to Excel
241
300
 
242
- ### 範例 1:Webhook 表單寫入 Excel
243
-
244
- 完美適用於多人同時提交表單的場景!
301
+ Perfect for scenarios with multiple simultaneous form submissions!
245
302
 
246
303
  ```
247
304
  ┌──────────────────┐
248
- │ Webhook │ 接收表單提交
305
+ │ Webhook │ Receive form submission
249
306
  │ POST /submit │
250
307
  └────────┬─────────┘
251
308
 
252
309
 
253
310
  ┌──────────────────┐
254
- │ Excel API │ 操作:Append(物件模式)
255
- │ │ 檔案:registrations.xlsx
256
- │ │ 值:{
257
- │ │ "姓名": "{{ $json.body.name }}",
311
+ │ Excel API │ Operation: Append (Object Mode)
312
+ │ │ File: registrations.xlsx
313
+ │ │ Values: {
314
+ │ │ "Name": "{{ $json.body.name }}",
258
315
  │ │ "Email": "{{ $json.body.email }}",
259
- │ │ "電話": "{{ $json.body.phone }}",
260
- │ │ "提交時間": "{{ $now }}"
316
+ │ │ "Phone": "{{ $json.body.phone }}",
317
+ │ │ "Submit Time": "{{ $now }}"
261
318
  │ │ }
262
319
  └────────┬─────────┘
263
320
 
264
321
 
265
322
  ┌──────────────────┐
266
- │ Respond Webhook │ 回傳成功訊息
323
+ │ Respond Webhook │ Return success message
267
324
  └──────────────────┘
268
325
  ```
269
326
 
270
- **HTML 表單:**
327
+ **HTML Form:**
271
328
  ```html
272
329
  <form id="registrationForm">
273
- <input type="text" name="name" placeholder="姓名" required>
330
+ <input type="text" name="name" placeholder="Name" required>
274
331
  <input type="email" name="email" placeholder="Email" required>
275
- <input type="tel" name="phone" placeholder="電話" required>
276
- <button type="submit">提交</button>
332
+ <input type="tel" name="phone" placeholder="Phone" required>
333
+ <button type="submit">Submit</button>
277
334
  </form>
278
335
 
279
336
  <script>
@@ -285,78 +342,134 @@ document.getElementById('registrationForm').addEventListener('submit', async (e)
285
342
  headers: {'Content-Type': 'application/json'},
286
343
  body: JSON.stringify(Object.fromEntries(formData))
287
344
  });
288
- alert('提交成功!');
345
+ alert('Submitted successfully!');
289
346
  });
290
347
  </script>
291
348
  ```
292
349
 
293
- ### 範例 2:每日報表產生
350
+ ### Example 2: Daily Report Generation
294
351
 
295
352
  ```
296
353
  ┌──────────────────┐
297
- │ Schedule │ 每天早上 9:00
354
+ │ Schedule │ Every day at 9:00 AM
298
355
  │ 0 9 * * * │
299
356
  └────────┬─────────┘
300
357
 
301
358
 
302
359
  ┌──────────────────┐
303
- │ Excel API │ 操作:Read
304
- │ (讀取) │ 檔案:sales.xlsx
360
+ │ Excel API │ Operation: Read
361
+ │ (Read) │ File: sales.xlsx
305
362
  └────────┬─────────┘
306
363
 
307
364
 
308
365
  ┌──────────────────┐
309
- │ Filter │ 篩選今日記錄
366
+ │ Filter │ Filter today's records
310
367
  └────────┬─────────┘
311
368
 
312
369
 
313
370
  ┌──────────────────┐
314
- │ Send Email │ 發送每日報表
371
+ │ Send Email │ Send daily report
315
372
  └──────────────────┘
316
373
  ```
317
374
 
318
- ### 範例 3:批次更新
375
+ ### Example 3: Batch Updates
319
376
 
320
377
  ```
321
378
  ┌──────────────────┐
322
- │ Code │ 準備操作陣列
379
+ │ Code │ Prepare operations array
323
380
  │ │ operations = [...]
324
381
  └────────┬─────────┘
325
382
 
326
383
 
327
384
  ┌──────────────────┐
328
- │ Excel API │ 操作:Batch
329
- │ (批次) 檔案:data.xlsx
330
- │ │ 操作:{{ $json.operations }}
385
+ │ Excel API │ Operation: Batch
386
+ │ (Batch) File: data.xlsx
387
+ │ │ Operations: {{ $json.operations }}
331
388
  └──────────────────┘
332
389
  ```
333
390
 
334
- ### 範例 4:透過員工編號更新薪資
391
+ ### Example 4: Update Salary by Employee ID
335
392
 
336
393
  ```
337
394
  ┌──────────────────┐
338
- │ Webhook │ 接收更新請求
395
+ │ Webhook │ Receive update request
339
396
  │ POST /update │ { "employeeId": "E100", "salary": 85000 }
340
397
  └────────┬─────────┘
341
398
 
342
399
 
343
400
  ┌──────────────────┐
344
- │ Excel API │ 操作:Update
345
- │ │ 識別方式:Lookup
346
- │ │ 查找欄位:員工編號
347
- │ │ 查找值:{{ $json.body.employeeId }}
348
- │ │ 設定值:{ "薪資": "{{ $json.body.salary }}" }
401
+ │ Excel API │ Operation: Update
402
+ │ │ Identify By: Lookup
403
+ │ │ Lookup Column: Employee ID
404
+ │ │ Lookup Value: {{ $json.body.employeeId }}
405
+ │ │ Process Mode: First Match Only
406
+ │ │ Values To Set: { "Salary": "{{ $json.body.salary }}" }
407
+ └────────┬─────────┘
408
+
409
+
410
+ ┌──────────────────┐
411
+ │ Respond Webhook │ Return update result
412
+ └──────────────────┘
413
+ ```
414
+
415
+ ### Example 5: Batch Department Status Update
416
+
417
+ **Use Case:** Review all employees in a department at once
418
+
419
+ ```
420
+ ┌──────────────────┐
421
+ │ Webhook │ Receive batch review request
422
+ │ POST /approve │ { "department": "IT", "status": "Reviewed" }
423
+ └────────┬─────────┘
424
+
425
+
426
+ ┌──────────────────┐
427
+ │ Excel API │ Operation: Update
428
+ │ │ Identify By: Lookup
429
+ │ │ Lookup Column: Department
430
+ │ │ Lookup Value: {{ $json.body.department }}
431
+ │ │ Process Mode: All Matching Records
432
+ │ │ Values To Set: {
433
+ │ │ "Status": "{{ $json.body.status }}",
434
+ │ │ "Review Date": "{{ $now.format('YYYY-MM-DD') }}",
435
+ │ │ "Reviewer": "{{ $json.body.reviewer }}"
436
+ │ │ }
349
437
  └────────┬─────────┘
350
438
 
351
439
 
352
440
  ┌──────────────────┐
353
- │ Respond Webhook │ 回傳更新結果
441
+ │ Respond Webhook │ Return: Updated N records
354
442
  └──────────────────┘
355
443
  ```
356
444
 
357
- ## 🧪 並行測試
445
+ ### Example 6: Clean Up Expired Data
358
446
 
359
- 測試 10 個同時提交:
447
+ **Use Case:** Periodically delete employee records terminated over a year ago
448
+
449
+ ```
450
+ ┌──────────────────┐
451
+ │ Schedule │ Execute on 1st of month
452
+ │ 0 0 1 * * │
453
+ └────────┬─────────┘
454
+
455
+
456
+ ┌──────────────────┐
457
+ │ Excel API │ Operation: Delete
458
+ │ │ Identify By: Lookup
459
+ │ │ Lookup Column: Status
460
+ │ │ Lookup Value: Terminated
461
+ │ │ Process Mode: All Matching Records
462
+ └────────┬─────────┘
463
+
464
+
465
+ ┌──────────────────┐
466
+ │ Send Email │ Notify admin: Cleaned N records
467
+ └──────────────────┘
468
+ ```
469
+
470
+ ## 🧪 Concurrent Testing
471
+
472
+ Test 10 simultaneous submissions:
360
473
 
361
474
  ```javascript
362
475
  // concurrent_test.js
@@ -367,156 +480,184 @@ for (let i = 0; i < 10; i++) {
367
480
  method: 'POST',
368
481
  headers: {'Content-Type': 'application/json'},
369
482
  body: JSON.stringify({
370
- 員工編號: `E${String(i).padStart(3, '0')}`,
371
- 姓名: `測試使用者 ${i}`,
372
- 時間戳記: new Date().toISOString()
483
+ EmployeeID: `E${String(i).padStart(3, '0')}`,
484
+ Name: `Test User ${i}`,
485
+ Timestamp: new Date().toISOString()
373
486
  })
374
487
  })
375
488
  );
376
489
  }
377
490
 
378
491
  await Promise.all(promises);
379
- console.log('所有請求完成!');
492
+ console.log('All requests completed!');
380
493
  ```
381
494
 
382
- **結果:** 所有 10 筆記錄都會安全地寫入 Excel,不會有資料遺失或損毀!
495
+ **Result:** All 10 records will be safely written to Excel without data loss or corruption!
383
496
 
384
- ## ⚠️ 常見問題
497
+ ## ⚠️ Troubleshooting
385
498
 
386
- ### 問題 1:節點未顯示在 n8n
499
+ ### Issue 1: Node Not Showing in n8n
387
500
 
388
- **解決方法:**
501
+ **Solution:**
389
502
  ```bash
390
- # 重新啟動 n8n
503
+ # Restart n8n
391
504
  pkill -f n8n
392
505
  n8n start
393
506
 
394
- # 或使用 pm2
507
+ # Or with pm2
395
508
  pm2 restart n8n
396
509
  ```
397
510
 
398
- ### 問題 2API 連線失敗
511
+ ### Issue 2: API Connection Failed
399
512
 
400
- **解決方法:**
401
- - 檢查 Excel API Server 是否正在執行:`curl http://localhost:8000/`
402
- - 驗證憑證中的 API URL 是否正確
403
- - 檢查 API Token 是否正確
404
- - 檢查防火牆設定
513
+ **Solution:**
514
+ - Check if Excel API Server is running: `curl http://localhost:8000/`
515
+ - Verify API URL in credentials is correct
516
+ - Check API Token is correct
517
+ - Check firewall settings
405
518
 
406
- ### 問題 3:「找不到參數」錯誤
519
+ ### Issue 3: "Parameter Not Found" Error
407
520
 
408
- **原因:** 參數名稱設定錯誤
521
+ **Cause:** Incorrect parameter name configuration
409
522
 
410
- **解決方法:**
411
- - 確認選擇了正確的 Append ModeObject Array
412
- - Object Mode:使用 `appendValuesObject` 參數
413
- - Array Mode:使用 `appendValuesArray` 參數
414
- - 檢查 JSON 格式是否正確
523
+ **Solution:**
524
+ - Confirm correct Append Mode is selected (Object or Array)
525
+ - Object Mode: Use `appendValuesObject` parameter
526
+ - Array Mode: Use `appendValuesArray` parameter
527
+ - Check JSON format is correct
415
528
 
416
- ### 問題 4:「檔案鎖定」錯誤
529
+ ### Issue 4: "File Lock" Error
417
530
 
418
- **原因:** 並行請求過多或 API 伺服器問題
531
+ **Cause:** Too many concurrent requests or API server issues
419
532
 
420
- **解決方法:**
421
- - 稍等片刻後重試
422
- - 檢查 API 伺服器狀態
423
- - 必要時重新啟動 Excel API Server
533
+ **Solution:**
534
+ - Wait a moment and retry
535
+ - Check API server status
536
+ - Restart Excel API Server if necessary
424
537
 
425
- ## 🔐 安全性
538
+ ## 🔐 Security
426
539
 
427
- ### 最佳實踐
540
+ ### Best Practices
428
541
 
429
- 1. **使用強式 API Token**
542
+ 1. **Use Strong API Token**
430
543
  ```bash
431
- # 產生安全的 token
544
+ # Generate secure token
432
545
  openssl rand -hex 32
433
546
  ```
434
547
 
435
- 2. **在正式環境使用 HTTPS**
436
- - 設定反向代理(Nginx
437
- - 使用 SSL 憑證
548
+ 2. **Use HTTPS in Production**
549
+ - Set up reverse proxy (Nginx)
550
+ - Use SSL certificate
438
551
 
439
- 3. **限制存取**
440
- - 僅允許信任的網路存取 API URL
441
- - 遠端存取時使用 VPN
552
+ 3. **Restrict Access**
553
+ - Allow only trusted networks to access API URL
554
+ - Use VPN for remote access
442
555
 
443
- 4. **定期備份**
444
- - 設定 Excel 檔案自動備份
445
- - 將備份儲存在安全位置
556
+ 4. **Regular Backups**
557
+ - Set up automatic backups of Excel files
558
+ - Store backups in secure location
446
559
 
447
- ## 📊 效能優化建議
560
+ ## 📊 Performance Optimization Tips
448
561
 
449
- ### 1. 使用批次操作
562
+ ### 1. Use Batch Operations
450
563
  ```javascript
451
- // ❌ 不好:多次單一操作
564
+ // ❌ Bad: Multiple single operations
452
565
  for (item of items) {
453
566
  await appendRow(item);
454
567
  }
455
568
 
456
- // ✅ 好:一次批次操作
569
+ // ✅ Good: One batch operation
457
570
  await batchOperations(items.map(item => ({
458
571
  type: "append",
459
572
  values: item.values
460
573
  })));
461
574
  ```
462
575
 
463
- ### 2. 讀取時指定範圍
576
+ ### 2. Specify Range When Reading
464
577
  ```javascript
465
- // ❌ 不好:讀取整個檔案
578
+ // ❌ Bad: Read entire file
466
579
  range: ""
467
580
 
468
- // ✅ 好:只讀取需要的範圍
581
+ // ✅ Good: Only read needed range
469
582
  range: "A1:D100"
470
583
  ```
471
584
 
472
- ### 3. 使用高效的工作流程
473
- - 在一個工作流程中組合相關操作
474
- - 減少 API 呼叫次數
475
- - 適當使用快取
585
+ ### 3. Use Efficient Workflows
586
+ - Combine related operations in one workflow
587
+ - Reduce number of API calls
588
+ - Use caching appropriately
589
+
590
+ ## 🆕 Latest Features
591
+
592
+ ### 🎉 Automatic Type Conversion (v1.0.3)
593
+ - ✅ **Smart Type Detection**: Automatically convert strings to appropriate data types
594
+ - ✅ **Number Conversion**: `"123"` → `123`, `"45.67"` → `45.67`
595
+ - ✅ **Boolean Conversion**: `"true"` → `true`, `"false"` → `false`
596
+ - ✅ **Null Conversion**: `"null"` or empty string → `null`
597
+ - ✅ **Date Conversion**: ISO format date strings auto-convert (`"2024-01-15"`)
598
+ - ✅ **Preserve Typed Values**: Numbers, booleans, etc. remain unchanged
599
+ - ✅ **All Operations**: Supported in both Append and Update operations
600
+
601
+ **Example:**
602
+ ```json
603
+ {
604
+ "EmployeeID": "E001", // Remains string
605
+ "Age": "30", // Auto-converts to number 30
606
+ "Salary": "50000.50", // Auto-converts to 50000.50
607
+ "IsActive": "true", // Auto-converts to boolean true
608
+ "TerminationDate": "null", // Auto-converts to null
609
+ "HireDate": "2020-01-15" // Auto-converts to date format
610
+ }
611
+ ```
476
612
 
477
- ## 🆕 最新功能
613
+ ### Object Mode
614
+ - ✅ Uses `/api/excel/append_object` API
615
+ - ✅ Automatically reads Excel headers (first row)
616
+ - ✅ Intelligently maps by column names
617
+ - ✅ Ignores unknown columns with warnings in response
618
+ - ✅ No need to remember column order
478
619
 
479
- ### Object Mode(物件模式)
480
- - ✅ 使用 `/api/excel/append_object` API
481
- - ✅ 自動讀取 Excel 表頭(第一列)
482
- - ✅ 按照欄位名稱智能對應
483
- - ✅ 忽略未知欄位,並在回應中提示
484
- - ✅ 不需要記住欄位順序
620
+ ### Advanced Update and Delete
621
+ - ✅ Support operations by row number
622
+ - ✅ Support operations by column value lookup
623
+ - ✅ Can update specific columns without affecting others
624
+ - ✅ Batch processing support with process modes
485
625
 
486
- ### 進階更新與刪除
487
- - ✅ 支援依列號直接操作
488
- - ✅ 支援依查找欄位值來操作
489
- - ✅ 可更新特定欄位而不影響其他欄位
626
+ ### Lookup Column Selection
627
+ - ✅ Dynamic dropdown selection of Excel headers
628
+ - ✅ Support for Chinese and special character column names
629
+ - ✅ Automatic URL encoding for special characters
630
+ - ✅ Enhanced user experience with visual column selection
490
631
 
491
- ## 🤝 貢獻
632
+ ## 🤝 Contributing
492
633
 
493
- 歡迎貢獻!
634
+ Contributions are welcome!
494
635
 
495
- 1. Fork 此儲存庫
496
- 2. 建立您的功能分支:`git checkout -b feature/AmazingFeature`
497
- 3. 提交您的變更:`git commit -m 'Add some AmazingFeature'`
498
- 4. 推送到分支:`git push origin feature/AmazingFeature`
499
- 5. 開啟 Pull Request
636
+ 1. Fork this repository
637
+ 2. Create your feature branch: `git checkout -b feature/AmazingFeature`
638
+ 3. Commit your changes: `git commit -m 'Add some AmazingFeature'`
639
+ 4. Push to the branch: `git push origin feature/AmazingFeature`
640
+ 5. Open a Pull Request
500
641
 
501
- ## 📄 授權
642
+ ## 📄 License
502
643
 
503
- MIT 授權 - 詳見 [LICENSE](LICENSE) 檔案
644
+ MIT License - see [LICENSE](LICENSE) file
504
645
 
505
- ## 🔗 相關專案
646
+ ## 🔗 Related Projects
506
647
 
507
- - [Excel API Server](https://github.com/code4Copilot/excel-api-server) - 後端 API 伺服器(必要)
508
- - [n8n](https://github.com/n8n-io/n8n) - 工作流程自動化工具
648
+ - [Excel API Server](https://github.com/code4Copilot/excel-api-server) - Backend API server (Required)
649
+ - [n8n](https://github.com/n8n-io/n8n) - Workflow automation tool
509
650
 
510
- ## 📧 支援
651
+ ## 📧 Support
511
652
 
512
- - GitHub Issues[回報問題](https://github.com/code4Copilot/n8n-nodes-excel-api/issues)
513
- - Email:your.email@example.com
514
- - n8n 社群:[n8n 論壇](https://community.n8n.io)
653
+ - GitHub Issues: [Report Issues](https://github.com/code4Copilot/n8n-nodes-excel-api/issues)
654
+ - Email: hueyan.chen@gmail.com
655
+ - n8n Community: [n8n Forum](https://community.n8n.io)
515
656
 
516
- ## ⭐ Star 歷史
657
+ ## ⭐ Star History
517
658
 
518
- 如果這個專案對您有幫助,請給它一個 ⭐!
659
+ If this project helps you, please give it a ⭐!
519
660
 
520
661
  ---
521
662
 
522
- **用 ❤️ n8n 社群打造**
663
+ **Built with ❤️ for the n8n community**
@@ -5,6 +5,7 @@ export declare class ExcelApi implements INodeType {
5
5
  loadOptions: {
6
6
  getExcelFiles(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
7
  getExcelSheets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
+ getColumnNames(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
9
  };
9
10
  };
10
11
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
@@ -2,6 +2,73 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExcelApi = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ /**
6
+ * 自動轉換欄位值的型態
7
+ * 支援:數字、布林值、日期、null
8
+ */
9
+ function convertValueType(value) {
10
+ // 如果已經是 null、undefined,直接返回
11
+ if (value === null || value === undefined) {
12
+ return null;
13
+ }
14
+ // 如果不是字串,保持原樣
15
+ if (typeof value !== 'string') {
16
+ return value;
17
+ }
18
+ // 處理空字串
19
+ if (value.trim() === '') {
20
+ return null;
21
+ }
22
+ // 處理 "null" 字串
23
+ if (value.toLowerCase() === 'null') {
24
+ return null;
25
+ }
26
+ // 處理布林值
27
+ if (value.toLowerCase() === 'true') {
28
+ return true;
29
+ }
30
+ if (value.toLowerCase() === 'false') {
31
+ return false;
32
+ }
33
+ // 處理數字(整數和浮點數)
34
+ // 使用正則表達式確保是有效的數字格式
35
+ if (/^-?\d+(\.\d+)?$/.test(value.trim())) {
36
+ const num = Number(value);
37
+ if (!isNaN(num)) {
38
+ return num;
39
+ }
40
+ }
41
+ // 處理 ISO 日期格式
42
+ // 格式如:2024-01-15 或 2024-01-15T10:30:00 或 2024-01-15T10:30:00.000Z
43
+ if (/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3}Z?)?)?$/.test(value.trim())) {
44
+ const date = new Date(value);
45
+ if (!isNaN(date.getTime())) {
46
+ // 返回 ISO 字串格式,Excel API Server 會處理
47
+ return date.toISOString();
48
+ }
49
+ }
50
+ // 其他情況保持原字串
51
+ return value;
52
+ }
53
+ /**
54
+ * 遞迴轉換物件或陣列中的所有值
55
+ */
56
+ function convertObjectValues(obj) {
57
+ if (obj === null || obj === undefined) {
58
+ return obj;
59
+ }
60
+ if (Array.isArray(obj)) {
61
+ return obj.map(item => convertValueType(item));
62
+ }
63
+ if (typeof obj === 'object') {
64
+ const converted = {};
65
+ for (const [key, value] of Object.entries(obj)) {
66
+ converted[key] = convertValueType(value);
67
+ }
68
+ return converted;
69
+ }
70
+ return convertValueType(obj);
71
+ }
5
72
  class ExcelApi {
6
73
  constructor() {
7
74
  this.description = {
@@ -150,11 +217,15 @@ class ExcelApi {
150
217
  description: 'Row number to update/delete (1-based, row 1 is header)',
151
218
  hint: '⚠️ Row 1 is protected (header row). Data rows start from row 2.',
152
219
  },
153
- // Lookup Column (for lookup method)
220
+ // ✨ NEW: Lookup Column - 改為下拉選單
154
221
  {
155
222
  displayName: 'Lookup Column',
156
223
  name: 'lookupColumn',
157
- type: 'string',
224
+ type: 'options',
225
+ typeOptions: {
226
+ loadOptionsMethod: 'getColumnNames',
227
+ loadOptionsDependsOn: ['fileName', 'sheetName'],
228
+ },
158
229
  displayOptions: {
159
230
  show: {
160
231
  operation: ['update', 'delete'],
@@ -163,9 +234,8 @@ class ExcelApi {
163
234
  },
164
235
  required: true,
165
236
  default: '',
166
- placeholder: 'e.g., 員工編號, Email, ID',
167
- description: 'Column name to search in (must match header exactly)',
168
- hint: 'The first row is treated as headers',
237
+ description: 'Column name to search in (automatically loaded from Excel headers)',
238
+ hint: '💡 The list shows all column names from the first row of your Excel file',
169
239
  },
170
240
  // Lookup Value (for lookup method)
171
241
  {
@@ -184,6 +254,33 @@ class ExcelApi {
184
254
  description: 'Value to search for in the lookup column',
185
255
  hint: 'Can use expressions like {{ $json.id }}',
186
256
  },
257
+ // Process Mode (for lookup method)
258
+ {
259
+ displayName: 'Process Mode',
260
+ name: 'processMode',
261
+ type: 'options',
262
+ displayOptions: {
263
+ show: {
264
+ operation: ['update', 'delete'],
265
+ identifyBy: ['lookup']
266
+ }
267
+ },
268
+ options: [
269
+ {
270
+ name: 'All Matching Records',
271
+ value: 'all',
272
+ description: 'Process all records that match the lookup condition'
273
+ },
274
+ {
275
+ name: 'First Match Only',
276
+ value: 'first',
277
+ description: 'Process only the first matching record'
278
+ },
279
+ ],
280
+ default: 'all',
281
+ description: 'Choose whether to process all matching records or just the first one',
282
+ hint: '💡 "All Matching Records" will update/delete every row where the lookup column matches the lookup value',
283
+ },
187
284
  // Update operation: Values to Set
188
285
  {
189
286
  displayName: 'Values to Set',
@@ -274,6 +371,38 @@ class ExcelApi {
274
371
  return [];
275
372
  }
276
373
  },
374
+ // ✨ NEW: 獲取欄位名稱(表頭)
375
+ async getColumnNames() {
376
+ const fileName = this.getNodeParameter('fileName');
377
+ const sheetName = this.getNodeParameter('sheetName');
378
+ if (!fileName || !sheetName) {
379
+ return [];
380
+ }
381
+ const credentials = await this.getCredentials('excelApiAuth');
382
+ const apiUrl = credentials.url;
383
+ const apiToken = credentials.token;
384
+ try {
385
+ const response = await this.helpers.request({
386
+ method: 'GET',
387
+ url: `${apiUrl}/api/excel/headers?file=${encodeURIComponent(fileName)}&sheet=${encodeURIComponent(sheetName)}`,
388
+ headers: {
389
+ 'Authorization': `Bearer ${apiToken}`,
390
+ },
391
+ json: true,
392
+ });
393
+ if (response.success && response.headers) {
394
+ return response.headers.map((header) => ({
395
+ name: header,
396
+ value: header,
397
+ }));
398
+ }
399
+ return [];
400
+ }
401
+ catch (error) {
402
+ // 如果獲取失敗,返回空列表(用戶可以手動輸入)
403
+ return [];
404
+ }
405
+ },
277
406
  },
278
407
  };
279
408
  }
@@ -312,10 +441,11 @@ class ExcelApi {
312
441
  else {
313
442
  appendValues = appendValuesRaw;
314
443
  }
444
+ // 自動轉換值的型態
445
+ const convertedValues = convertObjectValues(appendValues);
315
446
  // Use different API endpoint based on append mode
316
447
  if (appendMode === 'object') {
317
448
  // Object Mode: Use append_object API
318
- // This API automatically reads headers and maps values by column names
319
449
  responseData = await this.helpers.request({
320
450
  method: 'POST',
321
451
  url: `${apiUrl}/api/excel/append_object`,
@@ -326,7 +456,7 @@ class ExcelApi {
326
456
  body: {
327
457
  file: fileName,
328
458
  sheet: sheetName,
329
- values: appendValues,
459
+ values: convertedValues,
330
460
  },
331
461
  json: true,
332
462
  });
@@ -343,7 +473,7 @@ class ExcelApi {
343
473
  body: {
344
474
  file: fileName,
345
475
  sheet: sheetName,
346
- values: appendValues,
476
+ values: convertedValues,
347
477
  },
348
478
  json: true,
349
479
  });
@@ -402,11 +532,13 @@ class ExcelApi {
402
532
  else {
403
533
  valuesToSet = valuesToSetRaw;
404
534
  }
535
+ // 自動轉換值的型態
536
+ const convertedValuesToSet = convertObjectValues(valuesToSet);
405
537
  // Build request body
406
538
  const requestBody = {
407
539
  file: fileName,
408
540
  sheet: sheetName,
409
- values_to_set: valuesToSet,
541
+ values_to_set: convertedValuesToSet,
410
542
  };
411
543
  if (identifyBy === 'rowNumber') {
412
544
  const rowNumber = this.getNodeParameter('rowNumber', i);
@@ -415,8 +547,10 @@ class ExcelApi {
415
547
  else if (identifyBy === 'lookup') {
416
548
  const lookupColumn = this.getNodeParameter('lookupColumn', i);
417
549
  const lookupValue = this.getNodeParameter('lookupValue', i);
550
+ const processMode = this.getNodeParameter('processMode', i);
418
551
  requestBody.lookup_column = lookupColumn;
419
552
  requestBody.lookup_value = lookupValue;
553
+ requestBody.process_all = (processMode === 'all');
420
554
  }
421
555
  responseData = await this.helpers.request({
422
556
  method: 'PUT',
@@ -428,6 +562,12 @@ class ExcelApi {
428
562
  body: requestBody,
429
563
  json: true,
430
564
  });
565
+ // Check if any rows were affected
566
+ if (responseData.success && responseData.updated_count === 0) {
567
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), identifyBy === 'lookup'
568
+ ? `No matching rows found. Lookup column: "${requestBody.lookup_column}", Lookup value: "${requestBody.lookup_value}"`
569
+ : `Row ${requestBody.row} not found or is protected (header row cannot be updated)`);
570
+ }
431
571
  }
432
572
  else if (operation === 'delete') {
433
573
  // Get identification method
@@ -444,8 +584,10 @@ class ExcelApi {
444
584
  else if (identifyBy === 'lookup') {
445
585
  const lookupColumn = this.getNodeParameter('lookupColumn', i);
446
586
  const lookupValue = this.getNodeParameter('lookupValue', i);
587
+ const processMode = this.getNodeParameter('processMode', i);
447
588
  requestBody.lookup_column = lookupColumn;
448
589
  requestBody.lookup_value = lookupValue;
590
+ requestBody.process_all = (processMode === 'all');
449
591
  }
450
592
  responseData = await this.helpers.request({
451
593
  method: 'DELETE',
@@ -457,6 +599,12 @@ class ExcelApi {
457
599
  body: requestBody,
458
600
  json: true,
459
601
  });
602
+ // Check if any rows were affected
603
+ if (responseData.success && responseData.deleted_count === 0) {
604
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), identifyBy === 'lookup'
605
+ ? `No matching rows found. Lookup column: "${requestBody.lookup_column}", Lookup value: "${requestBody.lookup_value}"`
606
+ : `Row ${requestBody.row} not found or is protected (header row cannot be deleted)`);
607
+ }
460
608
  }
461
609
  else if (operation === 'batch') {
462
610
  const batchOperationsRaw = this.getNodeParameter('batchOperations', i);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-excel-api",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "n8n node for accessing Excel files via API with concurrent safety",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",