n8n-nodes-deep-ocr 1.3.2 โ 1.4.2
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 +66 -0
- package/dist/nodes/DeepOcr/DeepOcr.node.d.ts.map +1 -1
- package/dist/nodes/DeepOcr/DeepOcr.node.js +20 -22
- package/dist/nodes/DeepOcr/DeepOcr.node.js.map +1 -1
- package/dist/nodes/DeepOcr/deepocr.svg +14 -21
- package/dist/utils/errors.d.ts +25 -16
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +51 -25
- package/dist/utils/errors.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -61,6 +61,48 @@ npm install n8n-nodes-deep-ocr
|
|
|
61
61
|
| `handwriting` | Full transcription with unclear-word markers, confidence rating, language |
|
|
62
62
|
| `generic` | Flexible extraction for any document โ adapts structure to the content |
|
|
63
63
|
|
|
64
|
+
## ๐ค Output Examples
|
|
65
|
+
|
|
66
|
+
### Invoice
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"vendor": { "name": "ACME GmbH", "address": "Musterstr. 1, 10115 Berlin", "vat_id": "DE123456789" },
|
|
70
|
+
"invoice_number": "RE-2024-00842",
|
|
71
|
+
"invoice_date": "2024-03-15",
|
|
72
|
+
"due_date": "2024-04-14",
|
|
73
|
+
"line_items": [
|
|
74
|
+
{ "description": "Consulting Services", "quantity": 8, "unit_price": 150.00, "total": 1200.00 }
|
|
75
|
+
],
|
|
76
|
+
"subtotal": 1200.00,
|
|
77
|
+
"tax_rate": 19,
|
|
78
|
+
"tax_amount": 228.00,
|
|
79
|
+
"total": 1428.00,
|
|
80
|
+
"iban": "DE89 3704 0044 0532 0130 00",
|
|
81
|
+
"filename": "invoice.pdf",
|
|
82
|
+
"document_type": "invoice",
|
|
83
|
+
"metadata": { "pages": 1 }
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Receipt
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"merchant": { "name": "REWE City", "address": "Friedrichstr. 12, 10117 Berlin" },
|
|
91
|
+
"date": "2024-03-22",
|
|
92
|
+
"items": [
|
|
93
|
+
{ "description": "Bioland รpfel 1kg", "quantity": 1, "price": 2.49 },
|
|
94
|
+
{ "description": "Mineralwasser 6x1.5L", "quantity": 1, "price": 3.99 }
|
|
95
|
+
],
|
|
96
|
+
"subtotal": 6.48,
|
|
97
|
+
"tax_breakdown": [{ "rate": 7, "amount": 0.42 }],
|
|
98
|
+
"total": 6.90,
|
|
99
|
+
"payment_method": "EC-Karte",
|
|
100
|
+
"filename": "receipt.jpg",
|
|
101
|
+
"document_type": "receipt",
|
|
102
|
+
"metadata": { "pages": 1 }
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
64
106
|
## ๐ Example Workflow
|
|
65
107
|
|
|
66
108
|
```json
|
|
@@ -94,6 +136,30 @@ npm install n8n-nodes-deep-ocr
|
|
|
94
136
|
| JPG/JPEG | image/jpeg | 10MB |
|
|
95
137
|
| WebP | image/webp | 10MB |
|
|
96
138
|
|
|
139
|
+
## ๐ง Troubleshooting
|
|
140
|
+
|
|
141
|
+
### "Unsupported file type" error
|
|
142
|
+
The node only accepts PDF, PNG, JPG, JPEG, and WebP files. Make sure the upstream node sets the correct MIME type. If you're using **HTTP Request** to download a file, check that the response has a proper `Content-Type` header.
|
|
143
|
+
|
|
144
|
+
### "File size exceeds maximum allowed size"
|
|
145
|
+
The Deep-OCR API accepts files up to **10MB**. For larger PDFs, consider compressing them first or splitting them into individual pages.
|
|
146
|
+
|
|
147
|
+
### "Connection tested successfully" but processing fails
|
|
148
|
+
Your API key is valid, but the key may not have enough quota remaining. Check your usage in the [Deep-OCR Dashboard](https://deep-ocr.com).
|
|
149
|
+
|
|
150
|
+
### Node not appearing in n8n's node picker
|
|
151
|
+
In n8n 2.x, unverified community nodes are hidden from the picker search. Install the node via **Settings โ Community Nodes**, then add it to your workflow by importing the following JSON snippet:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"type": "n8n-nodes-deep-ocr.deepOcr",
|
|
156
|
+
"parameters": { "binaryPropertyName": "data", "documentType": "invoice" }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Binary property not found
|
|
161
|
+
The **Binary Property** field must match the property name set by the previous node. The default is `data` (used by Read Binary File, HTTP Request, etc.). Check the output of the previous node to confirm the property name.
|
|
162
|
+
|
|
97
163
|
## ๐ ๏ธ Development
|
|
98
164
|
|
|
99
165
|
### Prerequisites
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeepOcr.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/DeepOcr/DeepOcr.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAGrB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"DeepOcr.node.d.ts","sourceRoot":"","sources":["../../../src/nodes/DeepOcr/DeepOcr.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAGrB,MAAM,cAAc,CAAC;AA0CtB;;;;;GAKG;AACH,qBAAa,OAAQ,YAAW,SAAS;IACvC,WAAW,EAAE,oBAAoB,CA+F/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CA4GxE"}
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DeepOcr = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const errors_1 = require("../../utils/errors");
|
|
6
|
+
/** Deep-OCR API endpoint */
|
|
7
|
+
const API_ENDPOINT = 'https://api.deep-ocr.com/v1/ocr';
|
|
6
8
|
const ALLOWED_DOCUMENT_TYPES = [
|
|
7
9
|
'auto',
|
|
8
10
|
'bank_statement',
|
|
@@ -51,6 +53,7 @@ class DeepOcr {
|
|
|
51
53
|
default: 'data',
|
|
52
54
|
required: true,
|
|
53
55
|
description: 'Name of the binary property containing the document to process',
|
|
56
|
+
placeholder: "e.g. 'data', 'file', 'document'",
|
|
54
57
|
},
|
|
55
58
|
{
|
|
56
59
|
displayName: 'Document Type',
|
|
@@ -131,13 +134,19 @@ class DeepOcr {
|
|
|
131
134
|
}
|
|
132
135
|
// Get binary data
|
|
133
136
|
const binaryData = this.helpers.assertBinaryData(itemIndex, binaryPropertyName);
|
|
134
|
-
// Validate MIME type โ reject undefined/empty to prevent silent bypass
|
|
137
|
+
// Validate MIME type โ case-insensitive; reject undefined/empty to prevent silent bypass
|
|
135
138
|
if (!(0, errors_1.isValidMimeType)(binaryData.mimeType)) {
|
|
136
139
|
throw (0, errors_1.createFileTypeError)(this.getNode(), binaryData.mimeType ?? 'unknown', itemIndex);
|
|
137
140
|
}
|
|
138
|
-
// Early size check from metadata before loading the full buffer into memory (DoS prevention)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
// Early size check from metadata before loading the full buffer into memory (DoS prevention).
|
|
142
|
+
// Only accepts strict digit strings โ parseInt would partially parse "123abc" โ 123 or "1e9" โ 1,
|
|
143
|
+
// which could let oversized files bypass this guard. Non-matching values fall through to the
|
|
144
|
+
// authoritative buffer-length check below.
|
|
145
|
+
const rawFileSize = binaryData.fileSize;
|
|
146
|
+
const metaSize = typeof rawFileSize === 'string' && /^[0-9]+$/.test(rawFileSize)
|
|
147
|
+
? Number(rawFileSize)
|
|
148
|
+
: 0;
|
|
149
|
+
if (metaSize > 0 && Number.isFinite(metaSize) && !(0, errors_1.isValidFileSize)(metaSize)) {
|
|
141
150
|
throw (0, errors_1.createFileSizeError)(this.getNode(), metaSize, itemIndex);
|
|
142
151
|
}
|
|
143
152
|
// Load buffer
|
|
@@ -146,21 +155,18 @@ class DeepOcr {
|
|
|
146
155
|
if (!(0, errors_1.isValidFileSize)(buffer.length)) {
|
|
147
156
|
throw (0, errors_1.createFileSizeError)(this.getNode(), buffer.length, itemIndex);
|
|
148
157
|
}
|
|
149
|
-
// Sanitize filename to prevent path traversal
|
|
150
|
-
const
|
|
151
|
-
const safeFilename = rawFilename
|
|
152
|
-
.replace(/\.\./g, '')
|
|
153
|
-
.replace(/[/\\]/g, '_')
|
|
154
|
-
.replace(/[<>:"|?*\x00-\x1f]/g, '')
|
|
155
|
-
.substring(0, 255) || 'document';
|
|
158
|
+
// Sanitize filename to prevent path traversal and homograph attacks in multipart headers
|
|
159
|
+
const safeFilename = (0, errors_1.sanitizeFilename)(binaryData.fileName ?? 'document');
|
|
156
160
|
// Make API request โ document_type as query param, file as multipart.
|
|
157
161
|
// When documentType is 'auto', omit the parameter entirely so the API
|
|
158
162
|
// classifies the document automatically.
|
|
159
163
|
const form = new FormData();
|
|
160
164
|
form.append('file', new Blob([buffer], { type: binaryData.mimeType }), safeFilename);
|
|
165
|
+
// n8n's httpRequestWithAuthentication accepts FormData at runtime even though
|
|
166
|
+
// the TypeScript type expects IDataObject โ the double-cast is intentional.
|
|
161
167
|
const rawResponse = await this.helpers.httpRequestWithAuthentication.call(this, 'deepOcrApi', {
|
|
162
168
|
method: 'POST',
|
|
163
|
-
url:
|
|
169
|
+
url: API_ENDPOINT,
|
|
164
170
|
qs: documentType !== 'auto' ? { document_type: documentType } : {},
|
|
165
171
|
body: form,
|
|
166
172
|
});
|
|
@@ -184,10 +190,8 @@ class DeepOcr {
|
|
|
184
190
|
catch (error) {
|
|
185
191
|
if (this.continueOnFail()) {
|
|
186
192
|
const raw = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
187
|
-
// Truncate to prevent verbose API errors from leaking document content into workflow logs
|
|
188
|
-
const errorMessage = raw.length > 200 ? raw.substring(0, 200) + 'โฆ' : raw;
|
|
189
193
|
returnData.push({
|
|
190
|
-
json: { error:
|
|
194
|
+
json: { error: (0, errors_1.truncateErrorMessage)(raw) },
|
|
191
195
|
pairedItem: { item: itemIndex },
|
|
192
196
|
});
|
|
193
197
|
continue;
|
|
@@ -195,13 +199,7 @@ class DeepOcr {
|
|
|
195
199
|
if (error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError) {
|
|
196
200
|
throw error;
|
|
197
201
|
}
|
|
198
|
-
|
|
199
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
200
|
-
};
|
|
201
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), errorObject, {
|
|
202
|
-
message: 'Failed to process document with Deep-OCR API',
|
|
203
|
-
itemIndex,
|
|
204
|
-
});
|
|
202
|
+
throw (0, errors_1.wrapUnknownError)(this.getNode(), error, itemIndex);
|
|
205
203
|
}
|
|
206
204
|
}
|
|
207
205
|
return [returnData];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeepOcr.node.js","sourceRoot":"","sources":["../../../src/nodes/DeepOcr/DeepOcr.node.ts"],"names":[],"mappings":";;;AAQA,+CAAgE;AAChE,+
|
|
1
|
+
{"version":3,"file":"DeepOcr.node.js","sourceRoot":"","sources":["../../../src/nodes/DeepOcr/DeepOcr.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<!--
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
<!--
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
<rect x="14" y="34" width="26" height="3" fill="#4A90D9" rx="1"/>
|
|
16
|
-
<rect x="14" y="41" width="20" height="3" fill="#4A90D9" rx="1"/>
|
|
17
|
-
<!-- OCR scanning effect -->
|
|
18
|
-
<rect x="12" y="18" width="32" height="28" fill="none" stroke="url(#grad1)" stroke-width="2" stroke-dasharray="4,2" rx="2"/>
|
|
19
|
-
<!-- AI/Deep learning symbol -->
|
|
20
|
-
<circle cx="50" cy="46" r="12" fill="url(#grad1)"/>
|
|
21
|
-
<text x="50" y="51" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white" text-anchor="middle">AI</text>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
|
2
|
+
<rect width="32" height="32" rx="8" fill="#1A1A1A"/>
|
|
3
|
+
<!-- Document outline -->
|
|
4
|
+
<rect x="8" y="6" width="14" height="18" rx="2" stroke="#B9FF00" stroke-width="1.5" fill="none"/>
|
|
5
|
+
<!-- Folded corner -->
|
|
6
|
+
<path d="M18 6 L22 10" stroke="#B9FF00" stroke-width="1.5" stroke-linecap="round"/>
|
|
7
|
+
<path d="M18 6 L18 10 L22 10" stroke="#B9FF00" stroke-width="1.5" fill="none" stroke-linejoin="round"/>
|
|
8
|
+
<!-- Scan lines -->
|
|
9
|
+
<line x1="11" y1="13" x2="19" y2="13" stroke="#B9FF00" stroke-width="1.5" stroke-linecap="round"/>
|
|
10
|
+
<line x1="11" y1="16" x2="19" y2="16" stroke="#B9FF00" stroke-width="1.5" stroke-linecap="round"/>
|
|
11
|
+
<line x1="11" y1="19" x2="16" y2="19" stroke="#B9FF00" stroke-width="1.5" stroke-linecap="round"/>
|
|
12
|
+
<!-- Scan line indicator -->
|
|
13
|
+
<line x1="24" y1="12" x2="24" y2="22" stroke="#B9FF00" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
|
14
|
+
<line x1="22" y1="17" x2="26" y2="17" stroke="#B9FF00" stroke-width="2" stroke-linecap="round"/>
|
|
22
15
|
</svg>
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
|
2
|
-
import type { INode
|
|
3
|
-
/**
|
|
4
|
-
* Error codes for Deep-OCR operations
|
|
5
|
-
*/
|
|
6
|
-
export declare enum DeepOcrErrorCode {
|
|
7
|
-
INVALID_FILE_TYPE = "INVALID_FILE_TYPE",
|
|
8
|
-
FILE_TOO_LARGE = "FILE_TOO_LARGE",
|
|
9
|
-
MISSING_BINARY_DATA = "MISSING_BINARY_DATA",
|
|
10
|
-
API_ERROR = "API_ERROR",
|
|
11
|
-
AUTH_ERROR = "AUTH_ERROR"
|
|
12
|
-
}
|
|
2
|
+
import type { INode } from 'n8n-workflow';
|
|
13
3
|
/**
|
|
14
4
|
* Maximum file size in bytes (10MB)
|
|
15
5
|
*/
|
|
16
6
|
export declare const MAX_FILE_SIZE: number;
|
|
7
|
+
/**
|
|
8
|
+
* Maximum safe filename length in characters (enforced on UTF-16 code units, not bytes)
|
|
9
|
+
*/
|
|
10
|
+
export declare const MAX_FILENAME_LENGTH = 255;
|
|
17
11
|
/**
|
|
18
12
|
* Allowed MIME types for document processing
|
|
19
13
|
*/
|
|
@@ -27,15 +21,30 @@ export declare function createFileTypeError(node: INode, mimeType: string, itemI
|
|
|
27
21
|
*/
|
|
28
22
|
export declare function createFileSizeError(node: INode, sizeBytes: number, itemIndex?: number): NodeOperationError;
|
|
29
23
|
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Validates MIME type against allowed types
|
|
24
|
+
* Validates MIME type against allowed types โ case-insensitive to handle
|
|
25
|
+
* non-standard capitalisation from HTTP clients (e.g. image/JPEG).
|
|
26
|
+
* Rejects undefined/empty to prevent silent bypass.
|
|
35
27
|
*/
|
|
36
28
|
export declare function isValidMimeType(mimeType: string | undefined): boolean;
|
|
37
29
|
/**
|
|
38
30
|
* Validates file size against maximum limit
|
|
39
31
|
*/
|
|
40
32
|
export declare function isValidFileSize(sizeBytes: number): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Sanitizes a filename to prevent path traversal and header injection.
|
|
35
|
+
* Applies NFKD Unicode normalization first to neutralize homograph attacks,
|
|
36
|
+
* then strips traversal sequences, path separators, and control characters.
|
|
37
|
+
* Falls back to 'document' if the result is empty.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sanitizeFilename(filename: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Truncates an error message to prevent verbose API errors from leaking
|
|
42
|
+
* document content into workflow logs.
|
|
43
|
+
*/
|
|
44
|
+
export declare function truncateErrorMessage(message: string, maxLength?: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Wraps an unknown caught value into a NodeApiError for n8n.
|
|
47
|
+
* Used for unexpected errors that are neither NodeApiError nor NodeOperationError.
|
|
48
|
+
*/
|
|
49
|
+
export declare function wrapUnknownError(node: INode, error: unknown, itemIndex?: number): NodeApiError;
|
|
41
50
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C;;GAEG;AACH,eAAO,MAAM,aAAa,QAAmB,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,kBAAkB,UAM9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,kBAAkB,CASpB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,KAAK,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,kBAAkB,CAUpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,MAAM,CAK7E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAM9F"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ALLOWED_MIME_TYPES = exports.
|
|
3
|
+
exports.ALLOWED_MIME_TYPES = exports.MAX_FILENAME_LENGTH = exports.MAX_FILE_SIZE = void 0;
|
|
4
4
|
exports.createFileTypeError = createFileTypeError;
|
|
5
5
|
exports.createFileSizeError = createFileSizeError;
|
|
6
|
-
exports.createApiError = createApiError;
|
|
7
6
|
exports.isValidMimeType = isValidMimeType;
|
|
8
7
|
exports.isValidFileSize = isValidFileSize;
|
|
8
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
9
|
+
exports.truncateErrorMessage = truncateErrorMessage;
|
|
10
|
+
exports.wrapUnknownError = wrapUnknownError;
|
|
9
11
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
10
|
-
/**
|
|
11
|
-
* Error codes for Deep-OCR operations
|
|
12
|
-
*/
|
|
13
|
-
var DeepOcrErrorCode;
|
|
14
|
-
(function (DeepOcrErrorCode) {
|
|
15
|
-
DeepOcrErrorCode["INVALID_FILE_TYPE"] = "INVALID_FILE_TYPE";
|
|
16
|
-
DeepOcrErrorCode["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
|
|
17
|
-
DeepOcrErrorCode["MISSING_BINARY_DATA"] = "MISSING_BINARY_DATA";
|
|
18
|
-
DeepOcrErrorCode["API_ERROR"] = "API_ERROR";
|
|
19
|
-
DeepOcrErrorCode["AUTH_ERROR"] = "AUTH_ERROR";
|
|
20
|
-
})(DeepOcrErrorCode || (exports.DeepOcrErrorCode = DeepOcrErrorCode = {}));
|
|
21
12
|
/**
|
|
22
13
|
* Maximum file size in bytes (10MB)
|
|
23
14
|
*/
|
|
24
15
|
exports.MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum safe filename length in characters (enforced on UTF-16 code units, not bytes)
|
|
18
|
+
*/
|
|
19
|
+
exports.MAX_FILENAME_LENGTH = 255;
|
|
25
20
|
/**
|
|
26
21
|
* Allowed MIME types for document processing
|
|
27
22
|
*/
|
|
@@ -52,22 +47,15 @@ function createFileSizeError(node, sizeBytes, itemIndex) {
|
|
|
52
47
|
});
|
|
53
48
|
}
|
|
54
49
|
/**
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return new n8n_workflow_1.NodeApiError(node, error, {
|
|
59
|
-
message: 'Failed to process document with Deep-OCR API',
|
|
60
|
-
itemIndex,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Validates MIME type against allowed types
|
|
50
|
+
* Validates MIME type against allowed types โ case-insensitive to handle
|
|
51
|
+
* non-standard capitalisation from HTTP clients (e.g. image/JPEG).
|
|
52
|
+
* Rejects undefined/empty to prevent silent bypass.
|
|
65
53
|
*/
|
|
66
54
|
function isValidMimeType(mimeType) {
|
|
67
55
|
if (mimeType === undefined || mimeType.trim() === '') {
|
|
68
|
-
return false;
|
|
56
|
+
return false;
|
|
69
57
|
}
|
|
70
|
-
return exports.ALLOWED_MIME_TYPES.includes(mimeType);
|
|
58
|
+
return exports.ALLOWED_MIME_TYPES.includes(mimeType.toLowerCase());
|
|
71
59
|
}
|
|
72
60
|
/**
|
|
73
61
|
* Validates file size against maximum limit
|
|
@@ -75,4 +63,42 @@ function isValidMimeType(mimeType) {
|
|
|
75
63
|
function isValidFileSize(sizeBytes) {
|
|
76
64
|
return sizeBytes <= exports.MAX_FILE_SIZE;
|
|
77
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Sanitizes a filename to prevent path traversal and header injection.
|
|
68
|
+
* Applies NFKD Unicode normalization first to neutralize homograph attacks,
|
|
69
|
+
* then strips traversal sequences, path separators, and control characters.
|
|
70
|
+
* Falls back to 'document' if the result is empty.
|
|
71
|
+
*/
|
|
72
|
+
function sanitizeFilename(filename) {
|
|
73
|
+
return (filename
|
|
74
|
+
.normalize('NFKD')
|
|
75
|
+
.replace(/\.\./g, '')
|
|
76
|
+
.replace(/[/\\]/g, '_')
|
|
77
|
+
.replace(/[<>:"|?*\x00-\x1f]/g, '')
|
|
78
|
+
.substring(0, exports.MAX_FILENAME_LENGTH) || 'document');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Truncates an error message to prevent verbose API errors from leaking
|
|
82
|
+
* document content into workflow logs.
|
|
83
|
+
*/
|
|
84
|
+
function truncateErrorMessage(message, maxLength = 200) {
|
|
85
|
+
if (message.length <= maxLength)
|
|
86
|
+
return message;
|
|
87
|
+
if (maxLength <= 0)
|
|
88
|
+
return '';
|
|
89
|
+
if (maxLength === 1)
|
|
90
|
+
return 'โฆ';
|
|
91
|
+
return message.substring(0, maxLength - 1) + 'โฆ';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Wraps an unknown caught value into a NodeApiError for n8n.
|
|
95
|
+
* Used for unexpected errors that are neither NodeApiError nor NodeOperationError.
|
|
96
|
+
*/
|
|
97
|
+
function wrapUnknownError(node, error, itemIndex) {
|
|
98
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
99
|
+
return new n8n_workflow_1.NodeApiError(node, { message }, {
|
|
100
|
+
message: 'Failed to process document with Deep-OCR API',
|
|
101
|
+
itemIndex,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
78
104
|
//# sourceMappingURL=errors.js.map
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":";;;AA2BA,kDAaC;AAKD,kDAcC;AAOD,0CAKC;AAKD,0CAEC;AAQD,4CASC;AAMD,oDAKC;AAMD,4CAMC;AAtHD,+CAAgE;AAGhE;;GAEG;AACU,QAAA,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C;;GAEG;AACU,QAAA,mBAAmB,GAAG,GAAG,CAAC;AAEvC;;GAEG;AACU,QAAA,kBAAkB,GAAG;IAChC,iBAAiB;IACjB,WAAW;IACX,YAAY;IACZ,WAAW;IACX,YAAY;CACb,CAAC;AAEF;;GAEG;AACH,SAAgB,mBAAmB,CACjC,IAAW,EACX,QAAgB,EAChB,SAAkB;IAElB,OAAO,IAAI,iCAAkB,CAC3B,IAAI,EACJ,0BAA0B,QAAQ,8CAA8C,EAChF;QACE,SAAS;QACT,WAAW,EAAE,2BAA2B,QAAQ,+CAA+C;KAChG,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,mBAAmB,CACjC,IAAW,EACX,SAAiB,EACjB,SAAkB;IAElB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACnD,OAAO,IAAI,iCAAkB,CAC3B,IAAI,EACJ,cAAc,MAAM,0CAA0C,EAC9D;QACE,SAAS;QACT,WAAW,EAAE,wDAAwD;KACtE,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,QAA4B;IAC1D,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,0BAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,SAAiB;IAC/C,OAAO,SAAS,IAAI,qBAAa,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,CACL,QAAQ;SACL,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC;SAClC,SAAS,CAAC,CAAC,EAAE,2BAAmB,CAAC,IAAI,UAAU,CACnD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,OAAe,EAAE,SAAS,GAAG,GAAG;IACnE,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,IAAW,EAAE,KAAc,EAAE,SAAkB;IAC9E,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;IACzE,OAAO,IAAI,2BAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE;QACzC,OAAO,EAAE,8CAA8C;QACvD,SAAS;KACV,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-deep-ocr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "n8n community node for Deep-OCR document processing API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"homepage": "https://github.com/Heey-Global/deep-ocr-n8n",
|
|
16
16
|
"author": {
|
|
17
|
-
"name": "Heey"
|
|
17
|
+
"name": "Heey",
|
|
18
|
+
"email": "hello@deep-ocr.com"
|
|
18
19
|
},
|
|
19
20
|
"repository": {
|
|
20
21
|
"type": "git",
|
|
@@ -39,10 +40,10 @@
|
|
|
39
40
|
]
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"@n8n/eslint-plugin-community-nodes": "^0.10.0",
|
|
43
43
|
"@eslint/eslintrc": "^3.3.5",
|
|
44
|
+
"@n8n/eslint-plugin-community-nodes": "^0.10.0",
|
|
44
45
|
"@types/jest": "^30.0.0",
|
|
45
|
-
"@types/node": "^
|
|
46
|
+
"@types/node": "^24.0.0",
|
|
46
47
|
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
47
48
|
"@typescript-eslint/parser": "^8.57.0",
|
|
48
49
|
"eslint": "^9.39.0",
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"n8n-workflow": "^2.14.0",
|
|
56
57
|
"prettier": "^3.8.0",
|
|
57
58
|
"ts-jest": "^29.4.0",
|
|
58
|
-
"typescript": "^
|
|
59
|
+
"typescript": "^6.0.2"
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"n8n-workflow": "^1.0.0 || ^2.0.0"
|