n8n-nodes-teable-tuandk 1.0.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/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/credentials/TeableApi.credentials.d.ts +10 -0
- package/dist/credentials/TeableApi.credentials.d.ts.map +1 -0
- package/dist/credentials/TeableApi.credentials.js +45 -0
- package/dist/credentials/TeableApi.credentials.js.map +1 -0
- package/dist/nodes/Teable/GenericFunctions.d.ts +34 -0
- package/dist/nodes/Teable/GenericFunctions.d.ts.map +1 -0
- package/dist/nodes/Teable/GenericFunctions.js +178 -0
- package/dist/nodes/Teable/GenericFunctions.js.map +1 -0
- package/dist/nodes/Teable/RecordDescription.d.ts +4 -0
- package/dist/nodes/Teable/RecordDescription.d.ts.map +1 -0
- package/dist/nodes/Teable/RecordDescription.js +370 -0
- package/dist/nodes/Teable/RecordDescription.js.map +1 -0
- package/dist/nodes/Teable/SpaceDescription.d.ts +4 -0
- package/dist/nodes/Teable/SpaceDescription.d.ts.map +1 -0
- package/dist/nodes/Teable/SpaceDescription.js +30 -0
- package/dist/nodes/Teable/SpaceDescription.js.map +1 -0
- package/dist/nodes/Teable/TableDescription.d.ts +4 -0
- package/dist/nodes/Teable/TableDescription.d.ts.map +1 -0
- package/dist/nodes/Teable/TableDescription.js +53 -0
- package/dist/nodes/Teable/TableDescription.js.map +1 -0
- package/dist/nodes/Teable/Teable.node.d.ts +16 -0
- package/dist/nodes/Teable/Teable.node.d.ts.map +1 -0
- package/dist/nodes/Teable/Teable.node.js +412 -0
- package/dist/nodes/Teable/Teable.node.js.map +1 -0
- package/dist/nodes/Teable/TeableTrigger.node.d.ts +14 -0
- package/dist/nodes/Teable/TeableTrigger.node.d.ts.map +1 -0
- package/dist/nodes/Teable/TeableTrigger.node.js +286 -0
- package/dist/nodes/Teable/TeableTrigger.node.js.map +1 -0
- package/dist/nodes/Teable/teable.png +0 -0
- package/index.js +2 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kimotho (phoenixconsultants46@gmail.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# n8n-nodes-teable
|
|
2
|
+
|
|
3
|
+
An [n8n](https://n8n.io/) community node for [Teable](https://teable.ai) — the fast, real-time, no-code database built on Postgres.
|
|
4
|
+
|
|
5
|
+
Replace clunky HTTP nodes with a proper Teable integration: dynamic dropdowns, full CRUD, bulk operations, upsert, and auto-pagination — all in one node.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/n8n-nodes-teable-io)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
In your n8n instance go to **Settings → Community Nodes → Install** and enter:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
n8n-nodes-teable-io
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install via npm (self-hosted n8n):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install n8n-nodes-teable-io
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Credentials
|
|
29
|
+
|
|
30
|
+
Create a **Teable API** credential with:
|
|
31
|
+
|
|
32
|
+
| Field | Description |
|
|
33
|
+
|---|---|
|
|
34
|
+
| **API Token** | Your personal access token from Teable account settings |
|
|
35
|
+
| **Base URL** | `https://app.teable.ai` (default) or your self-hosted URL |
|
|
36
|
+
|
|
37
|
+
After saving, click **Test** — n8n verifies the token works regardless of which permission scope it was issued with (space, base, table, or record level).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Nodes
|
|
42
|
+
|
|
43
|
+
### Teable (action node)
|
|
44
|
+
|
|
45
|
+
#### Record
|
|
46
|
+
|
|
47
|
+
| Operation | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| **Get All** | List records with optional filter, sort, view, field selection, and auto-pagination |
|
|
50
|
+
| **Get** | Fetch a single record by its ID |
|
|
51
|
+
| **Create** | Create a new record with a field-value UI |
|
|
52
|
+
| **Create Many** | Bulk-create from a JSON array (auto-batched at 1 000 per request) |
|
|
53
|
+
| **Update** | Patch fields on a single record |
|
|
54
|
+
| **Update Many** | Bulk-update from a JSON array |
|
|
55
|
+
| **Delete** | Delete a record by ID |
|
|
56
|
+
| **Upsert** | Create or update based on a unique field match |
|
|
57
|
+
| **Search** | Full-text or field-scoped search |
|
|
58
|
+
|
|
59
|
+
#### Table
|
|
60
|
+
|
|
61
|
+
| Operation | Description |
|
|
62
|
+
|---|---|
|
|
63
|
+
| **Get All** | List all tables in a base |
|
|
64
|
+
| **Get Schema** | Return all fields with their types and settings |
|
|
65
|
+
| **Get Views** | List all views for a table |
|
|
66
|
+
|
|
67
|
+
#### Space / Base
|
|
68
|
+
|
|
69
|
+
| Operation | Description |
|
|
70
|
+
|---|---|
|
|
71
|
+
| **List Spaces** | All spaces accessible to your token |
|
|
72
|
+
| **List Bases** | All bases within a space |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Teable Trigger (polling trigger)
|
|
77
|
+
|
|
78
|
+
Polls for new or updated records on a schedule you set in the workflow settings.
|
|
79
|
+
|
|
80
|
+
| Event | Description |
|
|
81
|
+
|---|---|
|
|
82
|
+
| **New Record** | Fires when a record is added |
|
|
83
|
+
| **Record Updated** | Fires when an existing record is modified (excludes newly created records) |
|
|
84
|
+
| **New or Updated Record** | Fires on any change |
|
|
85
|
+
|
|
86
|
+
Each trigger output includes:
|
|
87
|
+
- `event` — `"created"` or `"updated"`
|
|
88
|
+
- `current` — current field values (flattened for easy expression access)
|
|
89
|
+
- `previous` — previous field snapshot (null on first run)
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Usage Tips
|
|
94
|
+
|
|
95
|
+
**Finding IDs:** Run *Space → List Spaces* then *Space → List Bases* to get your IDs, or copy them from the Teable URL.
|
|
96
|
+
|
|
97
|
+
**Return All:** Toggle on "Return All" in *Get All* to automatically paginate through every record (uses skip/take internally, max 1 000 per page).
|
|
98
|
+
|
|
99
|
+
**Field Key Type:** Switch between *Field Name* (human-readable) and *Field ID* (`fldXXX`) per operation.
|
|
100
|
+
|
|
101
|
+
**Upsert:** Pick the match field from the dropdown — the node searches for a record with that value and updates it, or creates a new one if not found.
|
|
102
|
+
|
|
103
|
+
**Bulk operations:** Pass a JSON array to *Create Many* / *Update Many*. Each update object needs `id` and `fields`:
|
|
104
|
+
```json
|
|
105
|
+
[
|
|
106
|
+
{ "id": "recABC123", "fields": { "Status": "Done" } },
|
|
107
|
+
{ "id": "recXYZ456", "fields": { "Status": "In Progress" } }
|
|
108
|
+
]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Filter JSON:** The *Get All* filter follows Teable's filter schema:
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"conjunction": "and",
|
|
115
|
+
"filterSet": [
|
|
116
|
+
{ "fieldId": "fldXXX", "operator": "=", "value": "Active" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Known Limitations
|
|
124
|
+
|
|
125
|
+
| Limitation | Detail |
|
|
126
|
+
|---|---|
|
|
127
|
+
| **n8n version** | `usableAsTool` (AI Agent support) requires n8n ≥ 1.22.0. Workflow node works on any supported n8n version. |
|
|
128
|
+
| **Large table sorting** | Ordering by `createdTime` or `lastModifiedTime` on very large tables can cause 504 timeouts. The trigger falls back to unordered fetching automatically. The main node does not — avoid sorting by system fields on tables with millions of rows. |
|
|
129
|
+
| **Trigger poll volume** | The trigger fetches up to the configured limit per poll cycle. If your table receives bursts larger than that limit between polls, some records may be missed. Raise the limit or shorten the poll interval to compensate. |
|
|
130
|
+
| **Static crawl only** | JS-rendered pages are not relevant here, but the Teable API is REST-only — there is no real-time webhook trigger yet. Polling introduces latency equal to your poll interval. |
|
|
131
|
+
| **Return All cap** | All `Return All` operations are hard-capped at 100,000 records to prevent out-of-memory crashes in n8n. |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Roadmap
|
|
136
|
+
|
|
137
|
+
- **Webhook trigger** — real-time record events via Teable webhooks (no polling)
|
|
138
|
+
- **Additional record operations** — bulk delete, record history, and more
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Security
|
|
143
|
+
|
|
144
|
+
`0.2.5` introduced a security hardening pass:
|
|
145
|
+
|
|
146
|
+
- SSRF protection on the `baseUrl` credential field
|
|
147
|
+
- Path injection prevention on all resource ID parameters
|
|
148
|
+
- Improved credential test endpoint (works with any token scope)
|
|
149
|
+
- Error message hardening (no raw user input in logs)
|
|
150
|
+
|
|
151
|
+
See [CHANGELOG.md](CHANGELOG.md) for full details.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Development
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Install dependencies
|
|
159
|
+
npm install
|
|
160
|
+
|
|
161
|
+
# Build (compiles TypeScript to dist/)
|
|
162
|
+
npm run build
|
|
163
|
+
|
|
164
|
+
# Link to local n8n for testing
|
|
165
|
+
npm link
|
|
166
|
+
cd ~/.n8n && mkdir -p nodes && cd nodes
|
|
167
|
+
npm link n8n-nodes-teable-io
|
|
168
|
+
|
|
169
|
+
# Start n8n — the Teable node appears in the node picker
|
|
170
|
+
npx n8n start
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Author
|
|
176
|
+
|
|
177
|
+
Built and maintained by **ZACHARIA Kimotho (Imperol)**
|
|
178
|
+
|
|
179
|
+
[](https://www.linkedin.com/in/zacharia-kimotho/)
|
|
180
|
+
[](https://github.com/Imperol3)
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
[MIT](LICENSE) © ZACHARIA Kimotho (Imperol)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class TeableApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=TeableApi.credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeableApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/TeableApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,MAAM,cAAc,CAAC;AAEtB,qBAAa,SAAU,YAAW,eAAe;IAChD,IAAI,SAAe;IACnB,WAAW,SAAgB;IAC3B,gBAAgB,SAAgD;IAEhE,UAAU,EAAE,eAAe,EAAE,CAoB3B;IAEF,YAAY,EAAE,oBAAoB,CAOhC;IAEF,IAAI,EAAE,sBAAsB,CAK1B;CACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TeableApi = void 0;
|
|
4
|
+
class TeableApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'teableApi';
|
|
7
|
+
this.displayName = 'Teable API';
|
|
8
|
+
this.documentationUrl = 'https://help.teable.ai/en/api-doc/overview';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Token',
|
|
12
|
+
name: 'apiToken',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
default: '',
|
|
16
|
+
required: true,
|
|
17
|
+
placeholder: 'teable_accv...',
|
|
18
|
+
description: 'Your Teable personal access token. Generate one in your Teable account settings. Enter the token only — do NOT include the "Bearer " prefix.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
displayName: 'Base URL',
|
|
22
|
+
name: 'baseUrl',
|
|
23
|
+
type: 'string',
|
|
24
|
+
default: 'https://app.teable.ai',
|
|
25
|
+
description: 'Leave as default for Teable Cloud. Override with your instance URL for self-hosted Teable (e.g. https://teable.mycompany.com).',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
this.authenticate = {
|
|
29
|
+
type: 'generic',
|
|
30
|
+
properties: {
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: '=Bearer {{$credentials.apiToken}}',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
this.test = {
|
|
37
|
+
request: {
|
|
38
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
39
|
+
url: '/api/auth/user',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.TeableApi = TeableApi;
|
|
45
|
+
//# sourceMappingURL=TeableApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeableApi.credentials.js","sourceRoot":"","sources":["../../credentials/TeableApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,SAAS;IAAtB;QACC,SAAI,GAAG,WAAW,CAAC;QACnB,gBAAW,GAAG,YAAY,CAAC;QAC3B,qBAAgB,GAAG,4CAA4C,CAAC;QAEhE,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,gBAAgB;gBAC7B,WAAW,EACV,8IAA8I;aAC/I;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,uBAAuB;gBAChC,WAAW,EACV,gIAAgI;aACjI;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,aAAa,EAAE,mCAAmC;iBAClD;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,gBAAgB;aACrB;SACD,CAAC;IACH,CAAC;CAAA;AA1CD,8BA0CC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IAllExecuteFunctions, IDataObject, IHttpRequestMethods } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Validate that a base URL is a safe, absolute HTTPS/HTTP URL pointing to a
|
|
4
|
+
* public host. Throws if the URL would allow SSRF against internal services.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateBaseUrl(raw: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Validate that an API path segment (tableId, recordId, etc.) contains only
|
|
9
|
+
* safe characters. Prevents path traversal via user-supplied IDs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validatePathSegment(value: string, label: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Make an authenticated request to the Teable API.
|
|
14
|
+
* Authentication is handled by n8n via the credential's authenticate definition.
|
|
15
|
+
* Retries up to 3 times on 429 (rate limit) and 5xx (server error).
|
|
16
|
+
* Note: no delay between retries — setTimeout and timers/promises are both
|
|
17
|
+
* blocked in n8n Cloud's sandbox environment.
|
|
18
|
+
*/
|
|
19
|
+
export declare function teableApiRequest(this: IAllExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* Fetch ALL records by automatically paginating with skip/take.
|
|
22
|
+
* Teable's max per page is 1000. Capped at MAX_RECORDS_RETURN_ALL.
|
|
23
|
+
*/
|
|
24
|
+
export declare function teableApiRequestAllItems(this: IAllExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject): Promise<any[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Parse a JSON string or pass through an object.
|
|
27
|
+
* Used for filter/orderBy parameters that users can supply as raw JSON.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseJsonParameter(value: string | IDataObject): IDataObject;
|
|
30
|
+
/**
|
|
31
|
+
* Build a fields object from an n8n "fixedCollection" additionalFields value.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildFieldsObject(fieldsUi: IDataObject, fieldsKey?: string): IDataObject;
|
|
34
|
+
//# sourceMappingURL=GenericFunctions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenericFunctions.d.ts","sourceRoot":"","sources":["../../../nodes/Teable/GenericFunctions.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EAIpB,WAAW,EAEX,mBAAmB,EAEnB,MAAM,cAAc,CAAC;AAKtB;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcnD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxE;AAWD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,oBAAoB,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,WAAgB,EACtB,EAAE,GAAE,WAAgB,GAClB,OAAO,CAAC,GAAG,CAAC,CAgDd;AAKD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC7C,IAAI,EAAE,oBAAoB,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,WAAgB,EACtB,EAAE,GAAE,WAAgB,GAClB,OAAO,CAAC,GAAG,EAAE,CAAC,CA0BhB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAU3E;AAKD;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,WAAW,EACrB,SAAS,SAAgB,GACvB,WAAW,CAoBb"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateBaseUrl = validateBaseUrl;
|
|
4
|
+
exports.validatePathSegment = validatePathSegment;
|
|
5
|
+
exports.teableApiRequest = teableApiRequest;
|
|
6
|
+
exports.teableApiRequestAllItems = teableApiRequestAllItems;
|
|
7
|
+
exports.parseJsonParameter = parseJsonParameter;
|
|
8
|
+
exports.buildFieldsObject = buildFieldsObject;
|
|
9
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
10
|
+
// Private/loopback CIDRs that must not be reachable via SSRF.
|
|
11
|
+
const BLOCKED_HOSTNAMES = /^(localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\.|::1|0\.0\.0\.0)/i;
|
|
12
|
+
/**
|
|
13
|
+
* Validate that a base URL is a safe, absolute HTTPS/HTTP URL pointing to a
|
|
14
|
+
* public host. Throws if the URL would allow SSRF against internal services.
|
|
15
|
+
*/
|
|
16
|
+
function validateBaseUrl(raw) {
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = new URL(raw);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new Error('Invalid Base URL. Must be a full URL (e.g. https://app.teable.ai).');
|
|
23
|
+
}
|
|
24
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
25
|
+
throw new Error(`Invalid Base URL protocol "${parsed.protocol}". Only https:// is allowed.`);
|
|
26
|
+
}
|
|
27
|
+
if (BLOCKED_HOSTNAMES.test(parsed.hostname)) {
|
|
28
|
+
throw new Error(`Base URL hostname "${parsed.hostname}" is not allowed. Must be a public host.`);
|
|
29
|
+
}
|
|
30
|
+
return parsed.origin; // strip any path, credentials, or fragments
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate that an API path segment (tableId, recordId, etc.) contains only
|
|
34
|
+
* safe characters. Prevents path traversal via user-supplied IDs.
|
|
35
|
+
*/
|
|
36
|
+
function validatePathSegment(value, label) {
|
|
37
|
+
if (!/^[\w-]+$/.test(value)) {
|
|
38
|
+
throw new Error(`Invalid ${label}: "${value}". Must contain only letters, digits, underscores, or hyphens.`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
/** Extract a numeric HTTP status from whatever shape the error arrives in. */
|
|
43
|
+
function extractStatus(error) {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
const raw = (_c = (_a = error === null || error === void 0 ? void 0 : error.statusCode) !== null && _a !== void 0 ? _a : (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.statusCode) !== null && _c !== void 0 ? _c : (_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status;
|
|
46
|
+
if (raw !== undefined)
|
|
47
|
+
return Number(raw);
|
|
48
|
+
// NodeApiError stores status in httpCode as a string
|
|
49
|
+
if ((error === null || error === void 0 ? void 0 : error.httpCode) !== undefined)
|
|
50
|
+
return Number(error.httpCode);
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Make an authenticated request to the Teable API.
|
|
55
|
+
* Authentication is handled by n8n via the credential's authenticate definition.
|
|
56
|
+
* Retries up to 3 times on 429 (rate limit) and 5xx (server error).
|
|
57
|
+
* Note: no delay between retries — setTimeout and timers/promises are both
|
|
58
|
+
* blocked in n8n Cloud's sandbox environment.
|
|
59
|
+
*/
|
|
60
|
+
async function teableApiRequest(method, endpoint, body = {}, qs = {}) {
|
|
61
|
+
var _a, _b, _c, _d, _e, _f;
|
|
62
|
+
const credentials = await this.getCredentials('teableApi');
|
|
63
|
+
const baseUrl = validateBaseUrl((_a = credentials.baseUrl) !== null && _a !== void 0 ? _a : 'https://app.teable.ai');
|
|
64
|
+
const options = {
|
|
65
|
+
method,
|
|
66
|
+
url: `${baseUrl}/api${endpoint}`,
|
|
67
|
+
headers: {
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
Accept: 'application/json',
|
|
70
|
+
},
|
|
71
|
+
qs,
|
|
72
|
+
body,
|
|
73
|
+
json: true,
|
|
74
|
+
};
|
|
75
|
+
// Remove empty body for GET/DELETE
|
|
76
|
+
if (method === 'GET' || method === 'DELETE') {
|
|
77
|
+
delete options.body;
|
|
78
|
+
}
|
|
79
|
+
const MAX_ATTEMPTS = 3;
|
|
80
|
+
let lastError;
|
|
81
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
// .call(this, ...) passes the execution context so httpRequestWithAuthentication
|
|
84
|
+
// can resolve this.getNode() / this.getCredentials() internally.
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
return await this.helpers.httpRequestWithAuthentication.call(this, 'teableApi', options);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
lastError = error;
|
|
90
|
+
const status = extractStatus(error);
|
|
91
|
+
const retryable = status !== undefined && (status === 429 || status >= 500);
|
|
92
|
+
if (!retryable || attempt === MAX_ATTEMPTS)
|
|
93
|
+
break;
|
|
94
|
+
// No delay between retries — timers are not available in n8n Cloud sandbox
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// All attempts exhausted — surface a clean error
|
|
98
|
+
const status = (_b = extractStatus(lastError)) !== null && _b !== void 0 ? _b : '';
|
|
99
|
+
const rawBody = (_d = (_c = lastError === null || lastError === void 0 ? void 0 : lastError.response) === null || _c === void 0 ? void 0 : _c.body) !== null && _d !== void 0 ? _d : (_e = lastError === null || lastError === void 0 ? void 0 : lastError.response) === null || _e === void 0 ? void 0 : _e.data;
|
|
100
|
+
const bodyMsg = typeof rawBody === 'string'
|
|
101
|
+
? rawBody.slice(0, 300)
|
|
102
|
+
: (_f = rawBody === null || rawBody === void 0 ? void 0 : rawBody.message) !== null && _f !== void 0 ? _f : (rawBody ? JSON.stringify(rawBody).slice(0, 300) : '');
|
|
103
|
+
const message = bodyMsg || (lastError === null || lastError === void 0 ? void 0 : lastError.message) || `Request failed${status ? ` (HTTP ${status})` : ''}`;
|
|
104
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), lastError, { message: String(message) });
|
|
105
|
+
}
|
|
106
|
+
// Hard cap on Return All to prevent OOM on very large tables.
|
|
107
|
+
const MAX_RECORDS_RETURN_ALL = 100000;
|
|
108
|
+
/**
|
|
109
|
+
* Fetch ALL records by automatically paginating with skip/take.
|
|
110
|
+
* Teable's max per page is 1000. Capped at MAX_RECORDS_RETURN_ALL.
|
|
111
|
+
*/
|
|
112
|
+
async function teableApiRequestAllItems(method, endpoint, body = {}, qs = {}) {
|
|
113
|
+
var _a, _b, _c;
|
|
114
|
+
const PAGE_SIZE = 1000;
|
|
115
|
+
const results = [];
|
|
116
|
+
let skip = 0;
|
|
117
|
+
let hasMore = true;
|
|
118
|
+
while (hasMore) {
|
|
119
|
+
const response = await teableApiRequest.call(this, method, endpoint, body, {
|
|
120
|
+
...qs,
|
|
121
|
+
take: PAGE_SIZE,
|
|
122
|
+
skip,
|
|
123
|
+
});
|
|
124
|
+
const records = (_b = (_a = response === null || response === void 0 ? void 0 : response.records) !== null && _a !== void 0 ? _a : response) !== null && _b !== void 0 ? _b : [];
|
|
125
|
+
results.push(...records);
|
|
126
|
+
if (results.length >= MAX_RECORDS_RETURN_ALL)
|
|
127
|
+
break;
|
|
128
|
+
// Teable returns a `total` count — stop when we have everything
|
|
129
|
+
const total = (_c = response === null || response === void 0 ? void 0 : response.total) !== null && _c !== void 0 ? _c : records.length;
|
|
130
|
+
skip += PAGE_SIZE;
|
|
131
|
+
hasMore = skip < total;
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Parse a JSON string or pass through an object.
|
|
137
|
+
* Used for filter/orderBy parameters that users can supply as raw JSON.
|
|
138
|
+
*/
|
|
139
|
+
function parseJsonParameter(value) {
|
|
140
|
+
if (typeof value === 'string') {
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(value);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Do not echo the raw value — it may contain sensitive content.
|
|
146
|
+
throw new Error('Invalid JSON parameter: could not parse the provided string. Check for syntax errors.');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
// Keys that must never be used as field names — assigning to these pollutes the prototype chain.
|
|
152
|
+
const PROTOTYPE_POISON_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
153
|
+
/**
|
|
154
|
+
* Build a fields object from an n8n "fixedCollection" additionalFields value.
|
|
155
|
+
*/
|
|
156
|
+
function buildFieldsObject(fieldsUi, fieldsKey = 'fieldValues') {
|
|
157
|
+
const items = fieldsUi[fieldsKey];
|
|
158
|
+
if (!(items === null || items === void 0 ? void 0 : items.length))
|
|
159
|
+
return {};
|
|
160
|
+
const fields = Object.create(null);
|
|
161
|
+
for (const { fieldName, fieldValue } of items) {
|
|
162
|
+
if (PROTOTYPE_POISON_KEYS.has(fieldName)) {
|
|
163
|
+
throw new Error(`Field name "${fieldName}" is reserved and cannot be used.`);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(fieldValue);
|
|
167
|
+
// Giữ string gốc cho numbers để tránh "123" → 123 và mất độ
|
|
168
|
+
// chính xác với số nguyên lớn vượt Number.MAX_SAFE_INTEGER.
|
|
169
|
+
// boolean/null/object/array vẫn dùng parsed value (Checkbox cần boolean thực).
|
|
170
|
+
fields[fieldName] = typeof parsed === 'number' ? fieldValue.trim() : parsed;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
fields[fieldName] = fieldValue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return fields;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=GenericFunctions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../../nodes/Teable/GenericFunctions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecordDescription.d.ts","sourceRoot":"","sources":["../../../nodes/Teable/RecordDescription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AA6D/C,eAAO,MAAM,gBAAgB,EAAE,eAAe,EAoB7C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAAe,EAySzC,CAAC"}
|