node-red-contrib-tdn-eventsub 0.2.3

Node-RED nodes for an access-control REST Server API: access groups, cardholders and items lists to global context, plus continuous item status subscriptions.

npm install node-red-contrib-tdn-eventsub

node-red-contrib-tdn-eventsub

Node-RED nodes for an access-control REST Server API.

Node Endpoint (under the base path) Mode
get cardholders /cardholders, /cardholders/{id} request/reply
get access groups /access_groups, /access_groups/{id}/cardholders request/reply
get items /items, /items/{id} request/reply
get personal data fields /personal_data_fields, /personal_data_fields/{id} request/reply
get event groups /events/groups, /events/groups/{id} (event types) request/reply
item status POST /items/updates, then long-poll continuous
event updates /events/updates, long-poll continuous

Every node has a trigger input and three outputs:

  1. Response: the full server response in msg.
  2. Status: one message per successful server response, for wiring to a shared log or dashboard.
  3. Error: every non-success, whether an HTTP error, a network failure, a certificate problem or bad input. Errors never appear on the status output.

No runtime dependencies. Node 16+, Node-RED 3+.

Install

cd ~/.node-red
npm install node-red-contrib-tdn-eventsub@latest

Restart Node-RED. Nodes appear under TDN REST. Import the example via Import → Examples → node-red-contrib-tdn-eventsub.

Server address

Every request goes to <protocol>://<host>:<port><basePath><endpoint>, for example:

http://localhost:80/api/cardholders
https://192.168.0.20:8904/api/access_groups
https://testserver:8911/api/items

The address is read from global context at every request, key rest_server (configurable in the server config node):

global.rest_server = {
  host: "192.168.0.20",   // hostname or IP (a full URL like "https://testserver:8911/api" also works)
  port: 8904,
  basePath: "/api",
  protocol: "https"       // or "http"
}

Any field that's missing falls back to the server config node. A plain string value is treated as the host or URL. You can change the value with a change node to switch servers without redeploying. The protocol is decided in this order: protocol, then the scheme in host, then the config node's Plain HTTP checkbox, then https.

Server config

Field Notes
Global key Default rest_server. Leave blank to ignore global context.
Host / IP, Port, Base path Fallbacks when global context doesn't set them. Defaults: port 8904, base path /api.
API key Optional. Stored as a Node-RED credential and treated as opaque. When it's set, it is always used. When it's blank, each node reads a credentials object.
Send key as Authorization header (default) or API key header.
Auth scheme Authorization header only. Blank (the default) sends Authorization: Basic base64(":" + key), which server v9.0+ accepts. For older servers, enter the API-key scheme token from your server's REST API docs; the key is then sent as Authorization: <scheme> <key>.
Header name API key header only. Sent as <header name>: <key>; default X-API-Key.
Plain HTTP (insecure) Uses unencrypted http:// when nothing else sets the protocol. Certificate settings are ignored and the key travels in clear text, so use it for bench testing only.
Cert fingerprint Optional. The SHA-256 (64 hex) or SHA-1 (40 hex) fingerprint of the server certificate; colons are optional. When set, a self-signed certificate is accepted only if it matches. The check runs before any request is written, so the key is never sent to an impostor.
Verify server certificate Normal CA check when no fingerprint is set.
Force links onto this host:port Paging and subscription links (next.href) are built from the server's own machine name. With this option on, they keep their path and query but use your host and port. With it off, a link that would downgrade https to http is refused.
Client cert / key / PFX Only needed if the REST Client item on the server has a thumbprint (pinned client certificate).

Get the server fingerprint with:

openssl s_client -connect rest-server:8904 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

Credentials object

If the server config has no API key stored in Node-RED, every node takes the key from its Credentials property. This is a typedInput pointing at msg, flow, global or env; the default is msg.credentials. The value is either a bare key string or:

{ apiKey: "XXXX-...", authType: "authorization" | "header", authScheme: "", headerName: "X-API-Key" }
  • Missing fields fall back to the server config.
  • A stored key always takes precedence.
  • A credentials property on the msg is deleted before the msg is passed on.
  • Subscriptions read the credentials once per trigger.

List nodes

Input

msg Request
no id, or an inject timestamp or boolean list: GET <endpoint> with top, sort and fields; every page is followed and merged
an id: msg.id, a string or number msg.payload, or msg.payload.id details: GET <endpoint>/{id}. For access groups this is the group's members instead: GET /access_groups/{id}/cardholders
msg.action set takes precedence over the defaults above
msg.action Request Stored in global context
list GET <endpoint> (all pages) <key> (array) and <key>_updated
get GET <endpoint>/{id} <key>_byId[id]
members (access groups) GET /access_groups/{id}/cardholders (all pages) <key>_members[id]
groups (cardholders) GET /cardholders/{id}?fields=accessGroups <key>_groups[id]
create POST <endpoint>, body msg.payload none (msg.location = new resource)
update PATCH <endpoint>/{id}, body msg.payload none
delete DELETE <endpoint>/{id} (only with an explicit action) removes <key>_byId[id]

Default keys are rest_cardholders, rest_accessGroups, rest_items, rest_pdfs and rest_eventGroups. Any context store can be picked. Id responses go into maps, so they never overwrite the full list.

Per-message overrides: msg.query (an object or "a=1&b=2"), msg.fields, msg.top, msg.maxPages and msg.contextKey.

Requests are queued and run one at a time, up to the Queue limit (default 100).

Output 1

msg.payload    // list/members: { results: [...all pages], count, pageCount }; details/writes: the server's body
msg.pages      // raw page bodies (toggle)
msg.action, msg.id, msg.statusCode, msg.location, msg.request, msg.contextKey

Item status node

  1. POST /items/updates with {"itemIds":[...]} returns the current status of every item. It is sent on output 1 with phase:"subscribe".
  2. GET next.href is the update-wait: a long poll that the server answers on a change, or after about 50 s.
  3. When updates arrive they're output with phase:"update", and the next update-wait goes out immediately. Empty update-waits only update the node badge (or use the Also output empty responses option).

Input is "508", "508,526", an array, {itemIds:[...]}, or item objects with id. msg.itemIds and then msg.id take precedence. A new trigger replaces the running subscription. payload:"stop", action:"stop" or msg.stop=true stops it.

There's an optional global map of item id → latest status. The delay between waits is capped at 20 s, so each request stays inside the server's 30 s window.

This needs REST Server v8.30+ and the RESTStatus licence.

Event updates node

GET /events/updates?<filter> long-polls for new events. Each response's updates.href (or next.href) is followed immediately, indefinitely.

  • Filter: set it in the node (group=23&type=20001&source=508) or in msg.query at start. Use get event groups to find group and type ids.
  • Output 1: msg.payload (the full response), msg.events, msg.phase, msg.seq and msg.query.
  • Global context (optional): keeps the last N events.
  • Start on deploy: an option that takes credentials from flow, global or env.
  • Stopping: same as item status.

Failures and back-off

Failure List nodes Item status / event updates
401 wrong API key error, no retry; the rest of the queue is dropped (one EDROPPED error) so a bad key can't trigger the server's failed-login alarms error, polling stops
Certificate / TLS (pin mismatch, CA, client cert, not TLS) error, no retry; queue dropped error, polling stops
403 licence or privilege error, no retry error, polling stops
404 error, no retry on the start endpoint: error, polling stops. On a followed link (expired subscription): error, then an immediate re-subscribe
other 4xx error, no retry error, polling stops
5xx, 408, 429, timeout, network GET/DELETE retried Retries times (default 3) at delay × 2ⁿ (5 s, 10 s, 20 s…, capped); each attempt is an error with attempt and retryInMs. POST/PATCH are never retried, to avoid duplicates error, back-off 5 s → 60 s, re-subscribe, indefinitely

A stopped subscription restarts on the next trigger or on redeploy. Error payloads carry category (fatal, transient or expired) and, for subscriptions, stopped: true when polling has ended.

Status output

Status messages are { topic: "status", payload: { state, text, node, name, type, time, ... } }:

  • lists: response (one per successful HTTP response, with statusCode, method, url and page), then done;
  • item status: subscribed and update;
  • event updates: update;
  • subscriptions: stopped on a stop input.

Empty long-poll returns (about every 50 s) show on the node badge only.

Test

npm test

This spins up a mock REST Server (self-signed HTTPS, plain HTTP, and a TLS echo endpoint) and a minimal Node-RED harness. It covers:

  • addressing and the global-context override;
  • every list node and action, paging, the queue, back-off, and the hard-stop rules;
  • the credentials object and both auth header modes;
  • certificate pinning;
  • the item-status and events long-poll loops, including expiry, stop/replace and recovery.

After changing a list-node definition, regenerate the editor HTML with node tools/gen-lists-html.js.

Node Info

Version: 0.2.3
Updated 3 hours ago
License: MIT
Rating: not yet rated

Categories

Actions

Rate:

Downloads

0 in the last week

Nodes

  • tdn-rest-server
  • tdn-rest-item-status
  • tdn-rest-events

Keywords

  • node-red
  • access control
  • rest
  • rest api
  • tdn

Maintainers