@processlink/node-red-contrib-processlink 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Process Link
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,126 @@
1
+ # @processlink/node-red-contrib-processlink
2
+
3
+ Node-RED nodes for Process Link platform integration.
4
+
5
+ ## Available Nodes
6
+
7
+ | Node | Category | Description |
8
+ |------|----------|-------------|
9
+ | **files upload** | Process Link | Upload files to Process Link Files API |
10
+
11
+ *More nodes coming soon: mail, downtime, notes*
12
+
13
+ ## Installation
14
+
15
+ ### Via Node-RED Palette Manager (Recommended)
16
+
17
+ 1. Open Node-RED
18
+ 2. Go to **Menu → Manage palette → Install**
19
+ 3. Search for `@processlink/node-red-contrib-processlink`
20
+ 4. Click **Install**
21
+
22
+ ### Via npm
23
+
24
+ ```bash
25
+ cd ~/.node-red
26
+ npm install @processlink/node-red-contrib-processlink
27
+ ```
28
+
29
+ Then restart Node-RED.
30
+
31
+ ## Quick Start
32
+
33
+ 1. **Get your credentials** from your Process Link app:
34
+ - Go to **Settings → API Keys**
35
+ - Click **Generate API Key**
36
+ - Copy the **Site ID** and **API Key**
37
+
38
+ 2. **Add a node** to your flow:
39
+ - Find nodes in the palette under "Process Link"
40
+ - Drag one into your flow
41
+
42
+ 3. **Configure credentials**:
43
+ - Double-click the node
44
+ - Click the pencil icon next to "Config"
45
+ - Enter your **Site ID** and **API Key**
46
+ - Click **Add**, then **Done**
47
+
48
+ ## Node Reference
49
+
50
+ ### Files Upload
51
+
52
+ Uploads files to the Process Link Files API.
53
+
54
+ #### Inputs
55
+
56
+ | Property | Type | Description |
57
+ |----------|------|-------------|
58
+ | `msg.payload` | Buffer \| string | The file content to upload |
59
+ | `msg.filename` | string | (Optional) Filename to use |
60
+
61
+ #### Outputs
62
+
63
+ | Property | Type | Description |
64
+ |----------|------|-------------|
65
+ | `msg.payload` | object | API response with `ok`, `file_id`, `created_at` |
66
+ | `msg.file_id` | string | The UUID of the uploaded file |
67
+ | `msg.statusCode` | number | HTTP status code (201 on success) |
68
+
69
+ #### Status Indicators
70
+
71
+ - Yellow: Uploading in progress
72
+ - Green: Upload successful
73
+ - Red: Error occurred
74
+
75
+ ## Example Flow
76
+
77
+ Upload a file from disk:
78
+
79
+ ```
80
+ [File In] → [files upload] → [Debug]
81
+ ```
82
+
83
+ 1. Configure a **File In** node to read your file
84
+ 2. Connect it to the **files upload** node
85
+ 3. Add a **Debug** node to see the result
86
+ 4. Configure the upload node with your Site ID and API Key
87
+
88
+ ### Dynamic Filename
89
+
90
+ Set the filename dynamically in a Function node:
91
+
92
+ ```javascript
93
+ msg.filename = "report-" + new Date().toISOString().split('T')[0] + ".csv";
94
+ return msg;
95
+ ```
96
+
97
+ ## Error Handling
98
+
99
+ | Status Code | Meaning | Solution |
100
+ |-------------|---------|----------|
101
+ | 201 | Success | File uploaded successfully |
102
+ | 400 | Bad Request | Check that payload is a valid file buffer |
103
+ | 401 | Unauthorized | Check your API key is correct |
104
+ | 403 | Forbidden | Enable API access in site settings |
105
+ | 404 | Not Found | Check your Site ID is correct |
106
+ | 429 | Rate Limited | Slow down - max 30 uploads/minute per site |
107
+ | 507 | Storage Full | Contact your administrator to increase storage |
108
+
109
+ ## Rate Limits
110
+
111
+ The API allows **30 uploads per minute** per site. If you exceed this limit, you'll receive a 429 status code. The node will still output the message so you can implement retry logic in your flow.
112
+
113
+ ## Security
114
+
115
+ - API keys are stored encrypted by Node-RED
116
+ - All communication uses HTTPS
117
+ - Keys are never logged or exposed in flow exports
118
+
119
+ ## Support
120
+
121
+ - **Issues**: [GitHub Issues](https://github.com/process-link/node-red-contrib-processlink/issues)
122
+ - **Email**: support@processlink.com.au
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,140 @@
1
+ [
2
+ {
3
+ "id": "pl-example-flow",
4
+ "type": "tab",
5
+ "label": "Process Link File Upload",
6
+ "disabled": false,
7
+ "info": "Example flow demonstrating how to upload files to Process Link Files API"
8
+ },
9
+ {
10
+ "id": "pl-inject",
11
+ "type": "inject",
12
+ "z": "pl-example-flow",
13
+ "name": "Trigger Upload",
14
+ "props": [],
15
+ "repeat": "",
16
+ "crontab": "",
17
+ "once": false,
18
+ "onceDelay": 0.1,
19
+ "topic": "",
20
+ "x": 130,
21
+ "y": 100,
22
+ "wires": [
23
+ [
24
+ "pl-file-in"
25
+ ]
26
+ ]
27
+ },
28
+ {
29
+ "id": "pl-file-in",
30
+ "type": "file in",
31
+ "z": "pl-example-flow",
32
+ "name": "Read File",
33
+ "filename": "/path/to/your/file.pdf",
34
+ "filenameType": "str",
35
+ "format": "",
36
+ "chunk": false,
37
+ "sendError": false,
38
+ "encoding": "none",
39
+ "allProps": true,
40
+ "x": 310,
41
+ "y": 100,
42
+ "wires": [
43
+ [
44
+ "pl-upload"
45
+ ]
46
+ ]
47
+ },
48
+ {
49
+ "id": "pl-upload",
50
+ "type": "processlink-files-upload",
51
+ "z": "pl-example-flow",
52
+ "name": "Upload to Process Link",
53
+ "server": "",
54
+ "filename": "",
55
+ "timeout": "30000",
56
+ "apiUrl": "https://files.processlink.com.au/api/upload",
57
+ "x": 530,
58
+ "y": 100,
59
+ "wires": [
60
+ [
61
+ "pl-switch"
62
+ ]
63
+ ]
64
+ },
65
+ {
66
+ "id": "pl-switch",
67
+ "type": "switch",
68
+ "z": "pl-example-flow",
69
+ "name": "Check Status",
70
+ "property": "statusCode",
71
+ "propertyType": "msg",
72
+ "rules": [
73
+ {
74
+ "t": "eq",
75
+ "v": "201",
76
+ "vt": "num"
77
+ },
78
+ {
79
+ "t": "else"
80
+ }
81
+ ],
82
+ "checkall": "true",
83
+ "repair": false,
84
+ "outputs": 2,
85
+ "x": 730,
86
+ "y": 100,
87
+ "wires": [
88
+ [
89
+ "pl-debug-success"
90
+ ],
91
+ [
92
+ "pl-debug-error"
93
+ ]
94
+ ]
95
+ },
96
+ {
97
+ "id": "pl-debug-success",
98
+ "type": "debug",
99
+ "z": "pl-example-flow",
100
+ "name": "Upload Success",
101
+ "active": true,
102
+ "tosidebar": true,
103
+ "console": false,
104
+ "tostatus": true,
105
+ "complete": "file_id",
106
+ "targetType": "msg",
107
+ "statusVal": "file_id",
108
+ "statusType": "msg",
109
+ "x": 940,
110
+ "y": 80,
111
+ "wires": []
112
+ },
113
+ {
114
+ "id": "pl-debug-error",
115
+ "type": "debug",
116
+ "z": "pl-example-flow",
117
+ "name": "Upload Error",
118
+ "active": true,
119
+ "tosidebar": true,
120
+ "console": false,
121
+ "tostatus": true,
122
+ "complete": "payload",
123
+ "targetType": "msg",
124
+ "statusVal": "payload.error",
125
+ "statusType": "msg",
126
+ "x": 930,
127
+ "y": 120,
128
+ "wires": []
129
+ },
130
+ {
131
+ "id": "pl-comment",
132
+ "type": "comment",
133
+ "z": "pl-example-flow",
134
+ "name": "Instructions: Configure the 'Upload to Process Link' node with your Site ID and API Key",
135
+ "info": "1. Double-click the orange 'Upload to Process Link' node\n2. Click the pencil icon next to 'Config'\n3. Enter your Site ID and API Key from the Process Link Files app\n4. Update the 'Read File' node with the path to your file\n5. Click the inject button to upload",
136
+ "x": 330,
137
+ "y": 40,
138
+ "wires": []
139
+ }
140
+ ]
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
2
+ <g fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
3
+ <polyline points="8,14 14,6 20,14"/>
4
+ <polyline points="20,14 26,6 32,14"/>
5
+ <polyline points="32,17 38,23 32,29"/>
6
+ <polyline points="32,26 26,34 20,26"/>
7
+ <polyline points="20,26 14,34 8,26"/>
8
+ <polyline points="8,29 2,23 8,17"/>
9
+ </g>
10
+ </svg>
@@ -0,0 +1,65 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("processlink-config", {
3
+ category: "config",
4
+ defaults: {
5
+ name: { value: "" },
6
+ siteId: { value: "", required: true },
7
+ },
8
+ credentials: {
9
+ apiKey: { type: "password" },
10
+ },
11
+ label: function () {
12
+ return this.name || "Process Link Config";
13
+ },
14
+ });
15
+ </script>
16
+
17
+ <script type="text/html" data-template-name="processlink-config">
18
+ <div class="form-row">
19
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
20
+ <input type="text" id="node-config-input-name" placeholder="My Site Config" />
21
+ </div>
22
+ <div class="form-row">
23
+ <label for="node-config-input-siteId"><i class="fa fa-building"></i> Site ID</label>
24
+ <input
25
+ type="text"
26
+ id="node-config-input-siteId"
27
+ placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
28
+ />
29
+ </div>
30
+ <div class="form-row">
31
+ <label for="node-config-input-apiKey"><i class="fa fa-key"></i> API Key</label>
32
+ <input type="password" id="node-config-input-apiKey" placeholder="Your API key" />
33
+ </div>
34
+ <div class="form-tips">
35
+ <p>
36
+ Get your Site ID and API Key from
37
+ <a href="https://processlink.com.au" target="_blank">Process Link</a>.
38
+ </p>
39
+ <p>
40
+ Go to <strong>Settings → API Keys</strong> in your app to generate a new key.
41
+ </p>
42
+ </div>
43
+ </script>
44
+
45
+ <script type="text/html" data-help-name="processlink-config">
46
+ <p>Configuration node for Process Link API authentication.</p>
47
+
48
+ <h3>Properties</h3>
49
+ <dl class="message-properties">
50
+ <dt>Name <span class="property-type">string</span></dt>
51
+ <dd>Optional friendly name for this configuration.</dd>
52
+ <dt>Site ID <span class="property-type">string</span></dt>
53
+ <dd>The UUID of your site from Process Link. Found in your site settings.</dd>
54
+ <dt>API Key <span class="property-type">string</span></dt>
55
+ <dd>
56
+ The API key for authentication. Generate one from Settings → API Keys in your Process Link app.
57
+ </dd>
58
+ </dl>
59
+
60
+ <h3>Details</h3>
61
+ <p>
62
+ This configuration is shared across all Process Link nodes in your flow. The API key is stored
63
+ encrypted by Node-RED.
64
+ </p>
65
+ </script>
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Process Link Configuration Node
3
+ * Shared credentials for all Process Link nodes
4
+ */
5
+
6
+ module.exports = function (RED) {
7
+ function ProcessLinkConfigNode(config) {
8
+ RED.nodes.createNode(this, config);
9
+ this.name = config.name;
10
+ this.siteId = config.siteId;
11
+ // API key is stored in this.credentials.apiKey (encrypted by Node-RED)
12
+ }
13
+
14
+ RED.nodes.registerType("processlink-config", ProcessLinkConfigNode, {
15
+ credentials: {
16
+ apiKey: { type: "password" },
17
+ },
18
+ });
19
+ };
@@ -0,0 +1,113 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("processlink-files-upload", {
3
+ category: "Process Link",
4
+ color: "#f97316",
5
+ defaults: {
6
+ name: { value: "" },
7
+ server: { value: "", type: "processlink-config", required: true },
8
+ filename: { value: "" },
9
+ timeout: { value: "30000" },
10
+ apiUrl: { value: "https://files.processlink.com.au/api/upload" },
11
+ },
12
+ inputs: 1,
13
+ outputs: 1,
14
+ icon: "processlink.svg",
15
+ paletteLabel: "files upload",
16
+ label: function () {
17
+ return this.name || "files upload";
18
+ },
19
+ labelStyle: function () {
20
+ return this.name ? "node_label_italic" : "";
21
+ },
22
+ inputLabels: "file buffer",
23
+ outputLabels: "response",
24
+ });
25
+ </script>
26
+
27
+ <script type="text/html" data-template-name="processlink-files-upload">
28
+ <div class="form-row">
29
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
30
+ <input type="text" id="node-input-name" placeholder="Name" />
31
+ </div>
32
+ <div class="form-row">
33
+ <label for="node-input-server"><i class="fa fa-server"></i> Config</label>
34
+ <input type="text" id="node-input-server" />
35
+ </div>
36
+ <div class="form-row">
37
+ <label for="node-input-filename"><i class="fa fa-file"></i> Filename</label>
38
+ <input type="text" id="node-input-filename" placeholder="msg.filename (or specify default)" />
39
+ </div>
40
+ <div class="form-row">
41
+ <label for="node-input-timeout"><i class="fa fa-clock-o"></i> Timeout</label>
42
+ <input type="text" id="node-input-timeout" placeholder="30000" />
43
+ <span style="margin-left: 5px; color: #888">ms</span>
44
+ </div>
45
+ <div class="form-row" style="display: none">
46
+ <label for="node-input-apiUrl"><i class="fa fa-link"></i> API URL</label>
47
+ <input type="text" id="node-input-apiUrl" />
48
+ </div>
49
+ </script>
50
+
51
+ <script type="text/html" data-help-name="processlink-files-upload">
52
+ <p>Uploads files to the Process Link Files API.</p>
53
+
54
+ <h3>Inputs</h3>
55
+ <dl class="message-properties">
56
+ <dt>payload <span class="property-type">buffer | string</span></dt>
57
+ <dd>The file content to upload.</dd>
58
+ <dt class="optional">filename <span class="property-type">string</span></dt>
59
+ <dd>The filename to use. Defaults to the configured filename or "file.bin".</dd>
60
+ </dl>
61
+
62
+ <h3>Outputs</h3>
63
+ <dl class="message-properties">
64
+ <dt>payload <span class="property-type">object</span></dt>
65
+ <dd>The API response containing <code>ok</code>, <code>file_id</code>, and <code>created_at</code>.</dd>
66
+ <dt>file_id <span class="property-type">string</span></dt>
67
+ <dd>The UUID of the uploaded file (convenience property).</dd>
68
+ <dt>statusCode <span class="property-type">number</span></dt>
69
+ <dd>The HTTP status code (201 on success).</dd>
70
+ </dl>
71
+
72
+ <h3>Details</h3>
73
+ <p>
74
+ This node uploads files to your Process Link site. The file content should be passed in
75
+ <code>msg.payload</code> as a Buffer (from a file-in node) or as a string.
76
+ </p>
77
+ <p>
78
+ The filename can be set in <code>msg.filename</code> or configured in the node properties. If
79
+ the filename contains a path, only the basename will be used.
80
+ </p>
81
+
82
+ <h3>Status Codes</h3>
83
+ <ul>
84
+ <li><strong>201</strong> - Upload successful</li>
85
+ <li><strong>400</strong> - Bad request (missing file, invalid site ID)</li>
86
+ <li><strong>401</strong> - Invalid API key</li>
87
+ <li><strong>403</strong> - API access not enabled for this site</li>
88
+ <li><strong>404</strong> - Site not found</li>
89
+ <li><strong>429</strong> - Rate limit exceeded (max 30 uploads/minute)</li>
90
+ <li><strong>507</strong> - Storage limit exceeded</li>
91
+ </ul>
92
+
93
+ <h3>Example Flow</h3>
94
+ <pre>
95
+ [File In] → [Process Link Upload] → [Debug]
96
+
97
+ The File In node reads a file and outputs msg.payload (Buffer)
98
+ and msg.filename. Connect directly to this node to upload.</pre>
99
+
100
+ <h3>References</h3>
101
+ <ul>
102
+ <li>
103
+ <a href="https://files.processlink.com.au" target="_blank">Process Link Files</a> - Manage
104
+ your files and API keys
105
+ </li>
106
+ <li>
107
+ <a href="https://github.com/process-link/node-red-contrib-processlink" target="_blank"
108
+ >GitHub</a
109
+ >
110
+ - Source code and issues
111
+ </li>
112
+ </ul>
113
+ </script>
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Process Link Files Upload Node
3
+ * Uploads files to Process Link Files API
4
+ */
5
+
6
+ module.exports = function (RED) {
7
+ const https = require("https");
8
+ const http = require("http");
9
+
10
+ function ProcessLinkFilesUploadNode(config) {
11
+ RED.nodes.createNode(this, config);
12
+ const node = this;
13
+
14
+ // Get the config node
15
+ this.server = RED.nodes.getNode(config.server);
16
+
17
+ node.on("input", function (msg, send, done) {
18
+ // For Node-RED 0.x compatibility
19
+ send = send || function () { node.send.apply(node, arguments); };
20
+ done = done || function (err) { if (err) node.error(err, msg); };
21
+
22
+ // Validate config
23
+ if (!node.server) {
24
+ node.status({ fill: "red", shape: "ring", text: "no config" });
25
+ done(new Error("No Process Link configuration selected"));
26
+ return;
27
+ }
28
+
29
+ const siteId = node.server.siteId;
30
+ const apiKey = node.server.credentials?.apiKey;
31
+
32
+ if (!siteId) {
33
+ node.status({ fill: "red", shape: "ring", text: "no site ID" });
34
+ done(new Error("Site ID not configured"));
35
+ return;
36
+ }
37
+
38
+ if (!apiKey) {
39
+ node.status({ fill: "red", shape: "ring", text: "no API key" });
40
+ done(new Error("API Key not configured"));
41
+ return;
42
+ }
43
+
44
+ // Validate payload
45
+ let fileBuffer;
46
+ if (Buffer.isBuffer(msg.payload)) {
47
+ fileBuffer = msg.payload;
48
+ } else if (typeof msg.payload === "string") {
49
+ fileBuffer = Buffer.from(msg.payload);
50
+ } else {
51
+ node.status({ fill: "red", shape: "ring", text: "invalid payload" });
52
+ done(new Error("msg.payload must be a Buffer or string"));
53
+ return;
54
+ }
55
+
56
+ // Get filename
57
+ const filename = msg.filename || config.filename || "file.bin";
58
+ const basename = filename.split(/[\\/]/).pop();
59
+
60
+ // Build multipart form data
61
+ const boundary = "----NodeREDProcessLink" + Date.now() + Math.random().toString(36).substring(2);
62
+
63
+ const header = Buffer.from(
64
+ `--${boundary}\r\n` +
65
+ `Content-Disposition: form-data; name="file"; filename="${basename}"\r\n` +
66
+ `Content-Type: application/octet-stream\r\n\r\n`
67
+ );
68
+ const footer = Buffer.from(`\r\n--${boundary}--\r\n`);
69
+ const body = Buffer.concat([header, fileBuffer, footer]);
70
+
71
+ // Parse URL
72
+ const apiUrl = config.apiUrl || "https://files.processlink.com.au/api/upload";
73
+ const url = new URL(apiUrl);
74
+ const isHttps = url.protocol === "https:";
75
+
76
+ const options = {
77
+ hostname: url.hostname,
78
+ port: url.port || (isHttps ? 443 : 80),
79
+ path: url.pathname,
80
+ method: "POST",
81
+ headers: {
82
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
83
+ "Content-Length": body.length,
84
+ "x-site-id": siteId,
85
+ "x-api-key": apiKey,
86
+ },
87
+ };
88
+
89
+ node.status({ fill: "yellow", shape: "dot", text: "uploading..." });
90
+
91
+ const transport = isHttps ? https : http;
92
+ const req = transport.request(options, (res) => {
93
+ let responseData = "";
94
+
95
+ res.on("data", (chunk) => {
96
+ responseData += chunk;
97
+ });
98
+
99
+ res.on("end", () => {
100
+ let parsedResponse;
101
+ try {
102
+ parsedResponse = JSON.parse(responseData);
103
+ } catch (e) {
104
+ parsedResponse = { raw: responseData };
105
+ }
106
+
107
+ msg.payload = parsedResponse;
108
+ msg.statusCode = res.statusCode;
109
+ msg.headers = res.headers;
110
+
111
+ if (res.statusCode === 201 && parsedResponse.ok) {
112
+ // Success
113
+ msg.file_id = parsedResponse.file_id;
114
+ node.status({
115
+ fill: "green",
116
+ shape: "dot",
117
+ text: `uploaded: ${parsedResponse.file_id?.substring(0, 8)}...`,
118
+ });
119
+ send(msg);
120
+ done();
121
+
122
+ // Clear status after 5 seconds
123
+ setTimeout(() => node.status({}), 5000);
124
+ } else {
125
+ // API error
126
+ const errorMsg = parsedResponse.error || parsedResponse.message || `HTTP ${res.statusCode}`;
127
+ node.status({ fill: "red", shape: "dot", text: errorMsg });
128
+
129
+ // Still send the message so users can handle errors in their flow
130
+ send(msg);
131
+ done();
132
+
133
+ // Clear status after 10 seconds
134
+ setTimeout(() => node.status({}), 10000);
135
+ }
136
+ });
137
+ });
138
+
139
+ req.on("error", (err) => {
140
+ node.status({ fill: "red", shape: "ring", text: "request failed" });
141
+ msg.payload = { error: err.message };
142
+ msg.statusCode = 0;
143
+ send(msg);
144
+ done(err);
145
+
146
+ setTimeout(() => node.status({}), 10000);
147
+ });
148
+
149
+ // Set timeout
150
+ const timeout = parseInt(config.timeout) || 30000;
151
+ req.setTimeout(timeout, () => {
152
+ req.destroy();
153
+ node.status({ fill: "red", shape: "ring", text: "timeout" });
154
+ msg.payload = { error: "Request timed out" };
155
+ msg.statusCode = 0;
156
+ send(msg);
157
+ done(new Error("Request timed out"));
158
+
159
+ setTimeout(() => node.status({}), 10000);
160
+ });
161
+
162
+ req.write(body);
163
+ req.end();
164
+ });
165
+
166
+ node.on("close", function () {
167
+ node.status({});
168
+ });
169
+ }
170
+
171
+ RED.nodes.registerType("processlink-files-upload", ProcessLinkFilesUploadNode);
172
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@processlink/node-red-contrib-processlink",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "1.0.0",
7
+ "description": "Node-RED nodes for Process Link platform integration",
8
+ "keywords": [
9
+ "node-red",
10
+ "processlink",
11
+ "process-link",
12
+ "files",
13
+ "upload",
14
+ "api",
15
+ "industrial",
16
+ "iot",
17
+ "automation"
18
+ ],
19
+ "node-red": {
20
+ "version": ">=2.0.0",
21
+ "nodes": {
22
+ "processlink-config": "nodes/config/processlink-config.js",
23
+ "processlink-files-upload": "nodes/files/processlink-files-upload.js"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "test": "echo \"No tests yet\" && exit 0"
28
+ },
29
+ "author": "Process Link <support@processlink.com.au>",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/process-link/node-red-contrib-processlink.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/process-link/node-red-contrib-processlink/issues"
37
+ },
38
+ "homepage": "https://github.com/process-link/node-red-contrib-processlink#readme",
39
+ "engines": {
40
+ "node": ">=14.0.0"
41
+ }
42
+ }