@processlink/node-red-contrib-processlink 1.2.0 → 1.2.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.
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
"Bash(git add:*)",
|
|
5
5
|
"Bash(git commit -m \"$\\(cat <<''EOF''\nAdd folder/area support to upload API\n\n- Add areaId and folderId params to /api/upload endpoint\n- Add API key auth to /api/sites/[siteId]/folders endpoint\n- Add new /api/sites/[siteId]/areas endpoint with API key auth\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
|
6
6
|
"Bash(git commit -m \"$\\(cat <<''EOF''\nAdd location selector for file uploads \\(v1.2.0\\)\n\n- Add Location dropdown showing Area > Folder hierarchy\n- Fetch areas and folders from Files API with API key auth\n- Send areaId and folderId in upload requests\n- Update README documentation\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
|
7
|
-
"Bash(npm publish:*)"
|
|
7
|
+
"Bash(npm publish:*)",
|
|
8
|
+
"WebFetch(domain:files.processlink.com.au)",
|
|
9
|
+
"Bash(git commit:*)",
|
|
10
|
+
"Bash(npm view:*)"
|
|
8
11
|
]
|
|
9
12
|
}
|
|
10
13
|
}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# CLAUDE.md - AI Assistant Guidelines
|
|
2
|
+
|
|
3
|
+
## Critical: This is a Published NPM Package
|
|
4
|
+
|
|
5
|
+
This package (`@processlink/node-red-contrib-processlink`) is **published to NPM** and used by real customers in production Node-RED environments. Changes here can break many people's workflows.
|
|
6
|
+
|
|
7
|
+
### Before Making Any Changes
|
|
8
|
+
|
|
9
|
+
1. **Understand the impact** - Changes affect all users who update the package
|
|
10
|
+
2. **Backwards compatibility** - Existing node configurations must continue to work
|
|
11
|
+
3. **Test thoroughly** - Test in a real Node-RED instance before publishing
|
|
12
|
+
4. **Version carefully** - Follow semver (patch for fixes, minor for features, major for breaking changes)
|
|
13
|
+
|
|
14
|
+
### Publishing Checklist
|
|
15
|
+
|
|
16
|
+
Before running `npm publish`:
|
|
17
|
+
- [ ] Test all nodes in Node-RED editor (deploy, configure, run)
|
|
18
|
+
- [ ] Verify existing flows still work after update
|
|
19
|
+
- [ ] Check browser console for JavaScript errors
|
|
20
|
+
- [ ] Test error handling paths
|
|
21
|
+
- [ ] Update version in package.json appropriately
|
|
22
|
+
- [ ] Document changes in commit message
|
|
23
|
+
|
|
24
|
+
## Architecture Notes
|
|
25
|
+
|
|
26
|
+
### Node-RED Security Model
|
|
27
|
+
|
|
28
|
+
**Important**: Credentials (API keys, passwords) are stored server-side only and are NEVER accessible from the browser/editor HTML code.
|
|
29
|
+
|
|
30
|
+
- `node.credentials.apiKey` - Only accessible in `.js` files (server-side)
|
|
31
|
+
- `configNode.credentials` - Always `undefined` in `.html` files (client-side)
|
|
32
|
+
|
|
33
|
+
To access credentials from the editor, create an admin HTTP endpoint:
|
|
34
|
+
```javascript
|
|
35
|
+
// In the .js file
|
|
36
|
+
RED.httpAdmin.get("/your-endpoint/:id", function(req, res) {
|
|
37
|
+
const node = RED.nodes.getNode(req.params.id);
|
|
38
|
+
const apiKey = node.credentials?.apiKey; // Access credentials here
|
|
39
|
+
// Make API calls server-side, return results to client
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### File Structure
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
nodes/
|
|
47
|
+
config/
|
|
48
|
+
processlink-config.js # Shared credentials config node
|
|
49
|
+
processlink-config.html
|
|
50
|
+
files/
|
|
51
|
+
processlink-files-upload.js # File upload node
|
|
52
|
+
processlink-files-upload.html
|
|
53
|
+
system/
|
|
54
|
+
processlink-system-info.js # System info node
|
|
55
|
+
processlink-system-info.html
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Node-RED Conventions
|
|
59
|
+
|
|
60
|
+
- Dual outputs: First output for success, second for errors
|
|
61
|
+
- Status indicators: green=success, yellow=processing, red=error
|
|
62
|
+
- Config nodes: Store shared credentials, referenced by other nodes
|
|
63
|
+
- Always use `send` and `done` callbacks for proper async handling
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
module.exports = function (RED) {
|
|
7
|
+
const https = require("https");
|
|
8
|
+
|
|
7
9
|
function ProcessLinkConfigNode(config) {
|
|
8
10
|
RED.nodes.createNode(this, config);
|
|
9
11
|
this.name = config.name;
|
|
@@ -16,4 +18,95 @@ module.exports = function (RED) {
|
|
|
16
18
|
apiKey: { type: "password" },
|
|
17
19
|
},
|
|
18
20
|
});
|
|
21
|
+
|
|
22
|
+
// Admin endpoint to fetch locations (areas and folders) for a config node
|
|
23
|
+
RED.httpAdmin.get("/processlink/locations/:id", function (req, res) {
|
|
24
|
+
const configNode = RED.nodes.getNode(req.params.id);
|
|
25
|
+
if (!configNode) {
|
|
26
|
+
return res.status(404).json({ error: "Config node not found" });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const siteId = configNode.siteId;
|
|
30
|
+
const apiKey = configNode.credentials?.apiKey;
|
|
31
|
+
|
|
32
|
+
if (!siteId || !apiKey) {
|
|
33
|
+
return res.status(400).json({ error: "Config not ready" });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const baseUrl = `/api/sites/${siteId}`;
|
|
37
|
+
const headers = {
|
|
38
|
+
Authorization: `Bearer ${apiKey}`,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Fetch both areas and folders
|
|
42
|
+
let areasData = [];
|
|
43
|
+
let foldersData = [];
|
|
44
|
+
let completed = 0;
|
|
45
|
+
let hasError = false;
|
|
46
|
+
|
|
47
|
+
function checkComplete() {
|
|
48
|
+
completed++;
|
|
49
|
+
if (completed === 2 && !hasError) {
|
|
50
|
+
res.json({ areas: areasData, folders: foldersData });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fetch areas
|
|
55
|
+
const areasReq = https.request(
|
|
56
|
+
{
|
|
57
|
+
hostname: "files.processlink.com.au",
|
|
58
|
+
path: `${baseUrl}/areas`,
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers: headers,
|
|
61
|
+
},
|
|
62
|
+
(areasRes) => {
|
|
63
|
+
let data = "";
|
|
64
|
+
areasRes.on("data", (chunk) => (data += chunk));
|
|
65
|
+
areasRes.on("end", () => {
|
|
66
|
+
try {
|
|
67
|
+
areasData = JSON.parse(data);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
areasData = [];
|
|
70
|
+
}
|
|
71
|
+
checkComplete();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
areasReq.on("error", () => {
|
|
76
|
+
if (!hasError) {
|
|
77
|
+
hasError = true;
|
|
78
|
+
res.status(500).json({ error: "Failed to fetch areas" });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
areasReq.end();
|
|
82
|
+
|
|
83
|
+
// Fetch folders
|
|
84
|
+
const foldersReq = https.request(
|
|
85
|
+
{
|
|
86
|
+
hostname: "files.processlink.com.au",
|
|
87
|
+
path: `${baseUrl}/folders`,
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: headers,
|
|
90
|
+
},
|
|
91
|
+
(foldersRes) => {
|
|
92
|
+
let data = "";
|
|
93
|
+
foldersRes.on("data", (chunk) => (data += chunk));
|
|
94
|
+
foldersRes.on("end", () => {
|
|
95
|
+
try {
|
|
96
|
+
foldersData = JSON.parse(data);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
foldersData = [];
|
|
99
|
+
}
|
|
100
|
+
checkComplete();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
foldersReq.on("error", () => {
|
|
105
|
+
if (!hasError) {
|
|
106
|
+
hasError = true;
|
|
107
|
+
res.status(500).json({ error: "Failed to fetch folders" });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
foldersReq.end();
|
|
111
|
+
});
|
|
19
112
|
};
|
|
@@ -46,16 +46,13 @@
|
|
|
46
46
|
|
|
47
47
|
$location.html('<option value="">Loading...</option>');
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
).done(function (areasResult, foldersResult) {
|
|
57
|
-
var areas = areasResult[0] || [];
|
|
58
|
-
var folders = foldersResult[0] || [];
|
|
49
|
+
// Fetch locations via server-side endpoint (credentials are not accessible client-side)
|
|
50
|
+
$.ajax({
|
|
51
|
+
url: "processlink/locations/" + configId,
|
|
52
|
+
method: "GET",
|
|
53
|
+
}).done(function (result) {
|
|
54
|
+
var areas = result.areas || [];
|
|
55
|
+
var folders = result.folders || [];
|
|
59
56
|
|
|
60
57
|
// Build current selection key for matching
|
|
61
58
|
var currentAreaId = node.areaId || "";
|
|
@@ -130,7 +127,8 @@
|
|
|
130
127
|
$location.html(options);
|
|
131
128
|
}).fail(function (xhr) {
|
|
132
129
|
console.error("Failed to load locations:", xhr.responseText);
|
|
133
|
-
|
|
130
|
+
// Still allow site root if API fails
|
|
131
|
+
$location.html('<option value="|">Site root (default)</option><option disabled>-- Failed to load locations --</option>');
|
|
134
132
|
});
|
|
135
133
|
}
|
|
136
134
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.2.
|
|
6
|
+
"version": "1.2.2",
|
|
7
7
|
"description": "Node-RED nodes for Process Link platform integration - upload files, send notifications, and connect to industrial automation systems",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"node-red",
|