@ironfang/n8n-nodes-ironfang 0.4.1 → 0.4.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 +9 -8
- package/dist/credentials/IronfangApi.credentials.js +2 -2
- package/dist/credentials/IronfangS3.credentials.js +1 -1
- package/dist/credentials/IronfangWebhookApi.credentials.js +1 -1
- package/dist/lib/renderwolf.d.ts +1 -1
- package/dist/lib/renderwolf.js +1 -1
- package/dist/nodes/Ironfang/execute.js +1 -1
- package/dist/nodes/Ironfang/parameters.js +1 -1
- package/dist/nodes/Ironfang/transport.js +10 -4
- package/dist/nodes/Ironfang/webhook.js +6 -4
- package/docs/operations.md +149 -149
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Use Ironfang Render, Finance, Audit and Rig, and Ironfang's public developer
|
|
|
6
6
|
tools, in n8n. The Ironfang action node has **162 operations**, grouped by
|
|
7
7
|
product. Ironfang Trigger receives signed Audit, Finance and Render events.
|
|
8
8
|
|
|
9
|
-
**Release: 0.4.
|
|
9
|
+
**Release: 0.4.3.** Credits, cache status and webhook signatures are read from the `X-Ironfang-*` and `Ironfang-*` headers; update before the API stops sending the launch-era ones. 0.4.2 moved the API to `api.ironfang.com` (credentials saved with the old `api.ironfang.uk` address keep working unchanged), and 0.4.1 made the package installable on n8n releases
|
|
10
10
|
before 2.33 that use PostgreSQL; see [Installation](#installation-and-credentials).
|
|
11
11
|
It includes 0.4.0, in which the products are named Ironfang Render, Finance and Audit
|
|
12
12
|
(formerly Renderwolf, Financewolf and Auditwolf), and the node follows: requests
|
|
@@ -27,7 +27,7 @@ node's newest version as an integer and these packages' newest node version was
|
|
|
27
27
|
1.1 or 1.2. Install 0.4.1 or later, or upgrade n8n. n8n Cloud installation requires n8n's manual package verification;
|
|
28
28
|
npm publishing and automated checks do not establish that approval.
|
|
29
29
|
|
|
30
|
-
Create an `if_live_` platform API key at [portal.ironfang.
|
|
30
|
+
Create an `if_live_` platform API key at [portal.ironfang.com](https://portal.ironfang.com)
|
|
31
31
|
with the scopes your workflow needs, then select an Ironfang API credential.
|
|
32
32
|
Existing `rw_live_` Render keys and both origin-only and `/renderwolf` base
|
|
33
33
|
URLs remain supported. Other product bases and custom gateway prefixes work too.
|
|
@@ -54,10 +54,10 @@ The [operation reference](docs/operations.md) lists each action's endpoint,
|
|
|
54
54
|
permission, query fields and request example. Complex API bodies use a JSON
|
|
55
55
|
editor so nested fields remain available. Replace the example values before
|
|
56
56
|
running a write operation. Request definitions are available in the product
|
|
57
|
-
specifications: [Render](https://api.ironfang.
|
|
58
|
-
[Finance](https://api.ironfang.
|
|
59
|
-
[Audit](https://api.ironfang.
|
|
60
|
-
[Rig](https://api.ironfang.
|
|
57
|
+
specifications: [Render](https://api.ironfang.com/render/openapi.yaml),
|
|
58
|
+
[Finance](https://api.ironfang.com/finance/openapi.yaml),
|
|
59
|
+
[Audit](https://api.ironfang.com/audit/openapi.yaml),
|
|
60
|
+
[Rig](https://api.ironfang.com/rig/openapi.yaml).
|
|
61
61
|
|
|
62
62
|
Screenshots support PNG/JPEG/WebP and phone/tablet device presets. Advanced
|
|
63
63
|
Render Options accepts custom `css`, a page `script`, ordered `actions` (click,
|
|
@@ -164,8 +164,9 @@ The trigger verifies HMAC-SHA256 over the timestamp and exact body bytes using
|
|
|
164
164
|
constant-time comparison, accepts timestamps within five minutes, and rejects
|
|
165
165
|
invalid signatures before starting a workflow. Render and Finance secrets are
|
|
166
166
|
decoded from hex; Audit secrets are used as returned. Deliveries are signed in
|
|
167
|
-
the `
|
|
168
|
-
|
|
167
|
+
the `Ironfang-Signature` and `Ironfang-Timestamp` headers; the trigger still
|
|
168
|
+
reads the `Renderwolf-`, `Financewolf-` or `Auditwolf-` headers the products
|
|
169
|
+
launched with when those are all a delivery carries. The signed event ID, rather
|
|
169
170
|
than the unsigned header ID, is used for duplicate detection.
|
|
170
171
|
|
|
171
172
|
Duplicate state covers seven days/up to 10,000 IDs in n8n workflow static data.
|
|
@@ -5,7 +5,7 @@ class IronfangApi {
|
|
|
5
5
|
name = 'ironfangApi';
|
|
6
6
|
icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
|
|
7
7
|
displayName = 'Ironfang API';
|
|
8
|
-
documentationUrl = 'https://ironfang.
|
|
8
|
+
documentationUrl = 'https://ironfang.com/render/docs';
|
|
9
9
|
properties = [
|
|
10
10
|
{
|
|
11
11
|
displayName: 'API Key',
|
|
@@ -20,7 +20,7 @@ class IronfangApi {
|
|
|
20
20
|
displayName: 'Base URL',
|
|
21
21
|
name: 'baseUrl',
|
|
22
22
|
type: 'string',
|
|
23
|
-
default: 'https://api.ironfang.
|
|
23
|
+
default: 'https://api.ironfang.com/render',
|
|
24
24
|
description: 'API origin or product base URL. Origin-only and /renderwolf values saved by earlier versions remain valid; the node selects the requested product path.',
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -5,7 +5,7 @@ class IronfangS3 {
|
|
|
5
5
|
name = 'ironfangS3';
|
|
6
6
|
displayName = 'Ironfang S3 Destination';
|
|
7
7
|
icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
|
|
8
|
-
documentationUrl = 'https://ironfang.
|
|
8
|
+
documentationUrl = 'https://ironfang.com/renderwolf/docs';
|
|
9
9
|
properties = [
|
|
10
10
|
{ displayName: 'Access Key', name: 'accessKey', type: 'string', default: '', required: true },
|
|
11
11
|
{ displayName: 'Secret Key', name: 'secretKey', type: 'string', typeOptions: { password: true }, default: '', required: true },
|
|
@@ -5,7 +5,7 @@ class IronfangWebhookApi {
|
|
|
5
5
|
name = 'ironfangWebhookApi';
|
|
6
6
|
displayName = 'Ironfang Webhook API';
|
|
7
7
|
icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
|
|
8
|
-
documentationUrl = 'https://ironfang.
|
|
8
|
+
documentationUrl = 'https://ironfang.com/audit/docs';
|
|
9
9
|
properties = [
|
|
10
10
|
{ displayName: 'Product', name: 'product', type: 'options', default: 'auditwolf', options: [{ name: 'Audit', value: 'auditwolf' }, { name: 'Finance', value: 'financewolf' }, { name: 'Render', value: 'renderwolf' }] },
|
|
11
11
|
{ displayName: 'Signing Secret', name: 'signingSecret', type: 'string', typeOptions: { password: true }, default: '', required: true, description: 'The one-time secret returned when creating the webhook endpoint or destination. This is not an API key.' },
|
package/dist/lib/renderwolf.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { IExecuteFunctions, INodeExecutionData, INodeTypeDescription } from
|
|
|
2
2
|
import { credentialTest } from '../nodes/Ironfang/credentialTest';
|
|
3
3
|
/**
|
|
4
4
|
* Ironfang node. One node per vendor: pick a product with Resource, then an
|
|
5
|
-
* operation within it. Ironfang Render (https://ironfang.
|
|
5
|
+
* operation within it. Ironfang Render (https://ironfang.com/render/docs) was the
|
|
6
6
|
* first product, and its saved resource value keeps the name it launched under; later ones are added as further resources rather than as
|
|
7
7
|
* separate nodes.
|
|
8
8
|
*
|
package/dist/lib/renderwolf.js
CHANGED
|
@@ -6,7 +6,7 @@ const transport_1 = require("../nodes/Ironfang/transport");
|
|
|
6
6
|
const credentialTest_1 = require("../nodes/Ironfang/credentialTest");
|
|
7
7
|
/**
|
|
8
8
|
* Ironfang node. One node per vendor: pick a product with Resource, then an
|
|
9
|
-
* operation within it. Ironfang Render (https://ironfang.
|
|
9
|
+
* operation within it. Ironfang Render (https://ironfang.com/render/docs) was the
|
|
10
10
|
* first product, and its saved resource value keeps the name it launched under; later ones are added as further resources rather than as
|
|
11
11
|
* separate nodes.
|
|
12
12
|
*
|
|
@@ -43,7 +43,7 @@ async function runOperation(context, item, op) {
|
|
|
43
43
|
const publicMode = op.product === 'tools' || (op.product === 'financewolf' && parameter(context, 'authentication', item, 'apiKey') === 'public');
|
|
44
44
|
if (publicMode && !op.public)
|
|
45
45
|
throw new Error('This operation requires an API key; public mode cannot access saved results');
|
|
46
|
-
const base = publicMode ? parameter(context, 'publicBaseUrl', item, 'https://api.ironfang.
|
|
46
|
+
const base = publicMode ? parameter(context, 'publicBaseUrl', item, 'https://api.ironfang.com') : (await context.getCredentials('ironfangApi')).baseUrl;
|
|
47
47
|
const path = op.path.replace(/\{(\w+)\}/g, (_, name) => (0, transport_1.identifier)(context, parameter(context, name, item), item));
|
|
48
48
|
const url = (0, transport_1.productBase)(base, op.product) + path;
|
|
49
49
|
const qs = { ...parameter(context, 'query', item, {}) };
|
|
@@ -20,7 +20,7 @@ function queryField(field) {
|
|
|
20
20
|
}
|
|
21
21
|
exports.productParameters = [
|
|
22
22
|
{ displayName: 'Authentication', name: 'authentication', type: 'options', noDataExpression: true, default: 'apiKey', displayOptions: { show: { resource: ['financewolf'] } }, options: [{ name: 'API Key', value: 'apiKey' }, { name: 'Public (No Saved Results)', value: 'public' }], description: 'Public validation, generation, ruleset reads and report verification only. An invalid key never falls back to public access.' },
|
|
23
|
-
{ displayName: 'Public API Base URL', name: 'publicBaseUrl', type: 'string', default: 'https://api.ironfang.
|
|
23
|
+
{ displayName: 'Public API Base URL', name: 'publicBaseUrl', type: 'string', default: 'https://api.ironfang.com', displayOptions: { show: { resource: ['tools', 'financewolf'] } }, description: 'Used by public requests only; authenticated requests use the credential base URL' },
|
|
24
24
|
{ displayName: 'Public tools share per-address limits. Screenshot is limited to 20/hour/address and a shared daily ceiling. Render Screenshot is the account-based automation service.', name: 'toolsNotice', type: 'notice', default: '', displayOptions: { show: { resource: ['tools'] } } },
|
|
25
25
|
...catalog_1.operations.flatMap((op) => {
|
|
26
26
|
const fields = [{ displayName: `Permission: ${op.scope}. ${op.description}.`, name: 'operationNotice', type: 'notice', default: '', displayOptions: show(op) }];
|
|
@@ -13,10 +13,16 @@ exports.productBase = productBase;
|
|
|
13
13
|
exports.renderwolfBase = renderwolfBase;
|
|
14
14
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
15
15
|
function apiBase(value) {
|
|
16
|
-
const url = new URL(String(value || 'https://api.ironfang.
|
|
16
|
+
const url = new URL(String(value || 'https://api.ironfang.com/render'));
|
|
17
17
|
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
|
|
18
18
|
throw new Error('Base URL must be an HTTP(S) URL without credentials, a query or fragment');
|
|
19
19
|
}
|
|
20
|
+
// The API moved from ironfang.uk to ironfang.com on 28 Sep 2026. The old
|
|
21
|
+
// names redirect, but a redirect to another domain drops the API key, so
|
|
22
|
+
// credentials saved with an ironfang.uk address are sent to .com directly.
|
|
23
|
+
if (url.hostname === 'ironfang.uk' || url.hostname.endsWith('.ironfang.uk')) {
|
|
24
|
+
url.hostname = url.hostname.slice(0, -'uk'.length) + 'com';
|
|
25
|
+
}
|
|
20
26
|
return url.toString().replace(/\/+$/, '');
|
|
21
27
|
}
|
|
22
28
|
function identifier(context, value, itemIndex) {
|
|
@@ -30,9 +36,9 @@ function responseMetadata(headers, statusCode) {
|
|
|
30
36
|
const result = { statusCode };
|
|
31
37
|
if (headers['x-ironfang-request-id'])
|
|
32
38
|
result.requestId = headers['x-ironfang-request-id'];
|
|
33
|
-
if (headers['x-
|
|
34
|
-
result.cacheStatus = headers['x-
|
|
35
|
-
const credits = headers['x-
|
|
39
|
+
if (headers['x-ironfang-cache'])
|
|
40
|
+
result.cacheStatus = headers['x-ironfang-cache'];
|
|
41
|
+
const credits = headers['x-ironfang-credits'];
|
|
36
42
|
if (credits !== undefined && credits !== '' && Number.isFinite(Number(credits)))
|
|
37
43
|
result.creditsCharged = Number(credits);
|
|
38
44
|
return result;
|
|
@@ -4,8 +4,10 @@ exports.signingKey = signingKey;
|
|
|
4
4
|
exports.verifyEvent = verifyEvent;
|
|
5
5
|
exports.webhookCredentialTest = webhookCredentialTest;
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// Every product signs with Ironfang-Signature and Ironfang-Timestamp. The saved
|
|
8
|
+
// product values keep the launch names (renderwolf and so on), and deliveries
|
|
9
|
+
// from before the Ironfang-* headers carried only <Launch name>-Signature, which
|
|
10
|
+
// is still read when the new header is absent. Render and Finance issue hex secrets.
|
|
9
11
|
function signingKey(product, secret) {
|
|
10
12
|
if (['renderwolf', 'financewolf'].includes(product) && /^[a-f0-9]{64}$/i.test(secret))
|
|
11
13
|
return Buffer.from(secret, 'hex');
|
|
@@ -16,8 +18,8 @@ function signingKey(product, secret) {
|
|
|
16
18
|
function verifyEvent(raw, headers, product, secret, now) {
|
|
17
19
|
if (!raw.length || raw.length > 65_536)
|
|
18
20
|
throw new Error('Webhook payload size is invalid');
|
|
19
|
-
const timestamp = headers[`${product}-timestamp`];
|
|
20
|
-
const signature = headers[`${product}-signature`];
|
|
21
|
+
const timestamp = headers['ironfang-timestamp'] ?? headers[`${product}-timestamp`];
|
|
22
|
+
const signature = headers['ironfang-signature'] ?? headers[`${product}-signature`];
|
|
21
23
|
if (typeof timestamp !== 'string' || !/^\d{10,11}$/.test(timestamp) || Math.abs(now / 1000 - Number(timestamp)) > 300)
|
|
22
24
|
throw new Error('Webhook timestamp is invalid or expired');
|
|
23
25
|
if (typeof signature !== 'string' || !/^v1=[a-f0-9]{64}$/.test(signature))
|