node-red-contrib-redis-variable 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 +769 -0
- package/examples/example.json +427 -0
- package/package.json +46 -0
- package/redis-variable-config.html +731 -0
- package/redis-variable-config.js +363 -0
- package/redis-variable.html +284 -0
- package/redis-variable.js +621 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
const Redis = require("ioredis");
|
|
2
|
+
|
|
3
|
+
module.exports = function (RED) {
|
|
4
|
+
let connections = {};
|
|
5
|
+
let usedConn = {};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper function to get value from different contexts
|
|
9
|
+
* @param {Object} node - Node instance
|
|
10
|
+
* @param {string} value - Value to get
|
|
11
|
+
* @param {string} type - Type of value (str, flow, global, env)
|
|
12
|
+
* @param {Object} msg - Message object
|
|
13
|
+
* @returns {string} Retrieved value
|
|
14
|
+
*/
|
|
15
|
+
function getValueFromContext(node, value, type, msg) {
|
|
16
|
+
if (value === null || value === undefined) return null;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
let result;
|
|
20
|
+
switch (type) {
|
|
21
|
+
case 'flow':
|
|
22
|
+
const flowContext = node.context().flow;
|
|
23
|
+
if (!flowContext) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
result = getNestedValue(flowContext, value);
|
|
27
|
+
break;
|
|
28
|
+
case 'global':
|
|
29
|
+
const globalContext = node.context().global;
|
|
30
|
+
if (!globalContext) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
result = getNestedValue(globalContext, value);
|
|
34
|
+
break;
|
|
35
|
+
case 'env':
|
|
36
|
+
result = process.env[value];
|
|
37
|
+
break;
|
|
38
|
+
case 'msg':
|
|
39
|
+
result = RED.util.getMessageProperty(msg, value);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
result = value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result !== undefined ? result : null;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
throw new Error(`Failed to get value for type: ${type}, value: ${value}. Error: ${err.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Helper function to get nested values like "redis_config.host"
|
|
52
|
+
function getNestedValue(context, path) {
|
|
53
|
+
if (!context) return undefined;
|
|
54
|
+
|
|
55
|
+
if (path.includes('.')) {
|
|
56
|
+
const parts = path.split('.');
|
|
57
|
+
let result = context.get(parts[0]);
|
|
58
|
+
for (let i = 1; i < parts.length; i++) {
|
|
59
|
+
if (result && typeof result === 'object') {
|
|
60
|
+
result = result[parts[i]];
|
|
61
|
+
} else {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
} else {
|
|
67
|
+
return context.get(path);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function RedisConfigNode(config) {
|
|
72
|
+
RED.nodes.createNode(this, config);
|
|
73
|
+
this.name = config.name || "Redis Config";
|
|
74
|
+
this.cluster = config.cluster || false;
|
|
75
|
+
|
|
76
|
+
// Connection configuration
|
|
77
|
+
this.hostType = config.hostType || 'str';
|
|
78
|
+
this.host = config.host || 'localhost';
|
|
79
|
+
this.hostContext = config.hostContext;
|
|
80
|
+
this.port = config.port || 6379;
|
|
81
|
+
this.portType = config.portType || 'str';
|
|
82
|
+
this.portContext = config.portContext;
|
|
83
|
+
|
|
84
|
+
// Authentication
|
|
85
|
+
this.passwordType = config.passwordType || 'str';
|
|
86
|
+
this.password = config.password;
|
|
87
|
+
this.passwordContext = config.passwordContext;
|
|
88
|
+
this.username = config.username;
|
|
89
|
+
this.usernameType = config.usernameType || 'str';
|
|
90
|
+
this.usernameContext = config.usernameContext;
|
|
91
|
+
|
|
92
|
+
// SSL/TLS Configuration
|
|
93
|
+
this.enableTLS = config.enableTLS || false;
|
|
94
|
+
this.tlsRejectUnauthorized = config.tlsRejectUnauthorized !== false; // Default to true
|
|
95
|
+
this.tlsCertType = config.tlsCertType || 'str';
|
|
96
|
+
this.tlsCertContext = config.tlsCertContext;
|
|
97
|
+
this.tlsKeyType = config.tlsKeyType || 'str';
|
|
98
|
+
this.tlsKeyContext = config.tlsKeyContext;
|
|
99
|
+
this.tlsCaType = config.tlsCaType || 'str';
|
|
100
|
+
this.tlsCaContext = config.tlsCaContext;
|
|
101
|
+
|
|
102
|
+
// Database and other options
|
|
103
|
+
this.database = config.database || 0;
|
|
104
|
+
this.databaseType = config.databaseType || 'str';
|
|
105
|
+
this.databaseContext = config.databaseContext;
|
|
106
|
+
|
|
107
|
+
// Advanced options
|
|
108
|
+
this.optionsType = config.optionsType || 'json';
|
|
109
|
+
this.options = config.options || '{}';
|
|
110
|
+
this.optionsContext = config.optionsContext;
|
|
111
|
+
|
|
112
|
+
// Get credentials for string passwords
|
|
113
|
+
const credentials = this.credentials || {};
|
|
114
|
+
|
|
115
|
+
// Helper method to parse credential values
|
|
116
|
+
this.parseCredentialValue = function(value, type, msg, executingNode) {
|
|
117
|
+
if (!value && value !== 0) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
let result;
|
|
123
|
+
switch (type) {
|
|
124
|
+
case 'str':
|
|
125
|
+
result = value;
|
|
126
|
+
break;
|
|
127
|
+
case 'flow':
|
|
128
|
+
result = getValueFromContext(executingNode || this, value, 'flow', msg);
|
|
129
|
+
break;
|
|
130
|
+
case 'global':
|
|
131
|
+
result = getValueFromContext(executingNode || this, value, 'global', msg);
|
|
132
|
+
break;
|
|
133
|
+
case 'env':
|
|
134
|
+
result = process.env[value] || null;
|
|
135
|
+
break;
|
|
136
|
+
case 'json':
|
|
137
|
+
try {
|
|
138
|
+
result = JSON.parse(value);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
result = value;
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'num':
|
|
144
|
+
result = Number(value);
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
result = value;
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (executingNode) {
|
|
152
|
+
executingNode.error(`Error parsing credential value: ${error.message}`);
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Get Redis connection options
|
|
159
|
+
this.getConnectionOptions = function(msg, executingNode) {
|
|
160
|
+
try {
|
|
161
|
+
// Parse host
|
|
162
|
+
let host = this.parseCredentialValue(
|
|
163
|
+
this.hostType === 'str' ? this.host : this.hostContext,
|
|
164
|
+
this.hostType,
|
|
165
|
+
msg,
|
|
166
|
+
executingNode
|
|
167
|
+
) || 'localhost';
|
|
168
|
+
|
|
169
|
+
// Parse port
|
|
170
|
+
let port = this.parseCredentialValue(
|
|
171
|
+
this.portType === 'str' ? this.port : this.portContext,
|
|
172
|
+
this.portType,
|
|
173
|
+
msg,
|
|
174
|
+
executingNode
|
|
175
|
+
) || 6379;
|
|
176
|
+
|
|
177
|
+
// Parse database
|
|
178
|
+
let database = this.parseCredentialValue(
|
|
179
|
+
this.databaseType === 'str' ? this.database : this.databaseContext,
|
|
180
|
+
this.databaseType,
|
|
181
|
+
msg,
|
|
182
|
+
executingNode
|
|
183
|
+
) || 0;
|
|
184
|
+
|
|
185
|
+
// Parse password
|
|
186
|
+
let password = null;
|
|
187
|
+
if (this.passwordType === 'str') {
|
|
188
|
+
password = this.credentials?.password;
|
|
189
|
+
} else {
|
|
190
|
+
password = this.parseCredentialValue(this.passwordContext, this.passwordType, msg, executingNode);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Parse username
|
|
194
|
+
let username = null;
|
|
195
|
+
if (this.usernameType === 'str') {
|
|
196
|
+
username = this.credentials?.username;
|
|
197
|
+
} else {
|
|
198
|
+
username = this.parseCredentialValue(this.usernameContext, this.usernameType, msg, executingNode);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Parse additional options
|
|
202
|
+
let additionalOptions = {};
|
|
203
|
+
if (this.optionsType === 'json') {
|
|
204
|
+
try {
|
|
205
|
+
additionalOptions = JSON.parse(this.options || '{}');
|
|
206
|
+
} catch (e) {
|
|
207
|
+
additionalOptions = {};
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
additionalOptions = this.parseCredentialValue(this.optionsContext, this.optionsType, msg, executingNode) || {};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Build connection options
|
|
214
|
+
const connectionOptions = {
|
|
215
|
+
host: host,
|
|
216
|
+
port: parseInt(port),
|
|
217
|
+
db: parseInt(database),
|
|
218
|
+
retryDelayOnFailover: 100,
|
|
219
|
+
enableReadyCheck: false,
|
|
220
|
+
maxRetriesPerRequest: null,
|
|
221
|
+
...additionalOptions
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Add authentication if provided
|
|
225
|
+
if (password) {
|
|
226
|
+
connectionOptions.password = password;
|
|
227
|
+
}
|
|
228
|
+
if (username) {
|
|
229
|
+
connectionOptions.username = username;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Add SSL/TLS configuration if enabled
|
|
233
|
+
if (this.enableTLS) {
|
|
234
|
+
connectionOptions.tls = {
|
|
235
|
+
rejectUnauthorized: this.tlsRejectUnauthorized
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Parse TLS Certificate
|
|
239
|
+
let tlsCert = null;
|
|
240
|
+
if (this.tlsCertType === 'str') {
|
|
241
|
+
tlsCert = this.credentials?.tlsCert;
|
|
242
|
+
} else {
|
|
243
|
+
tlsCert = this.parseCredentialValue(this.tlsCertContext, this.tlsCertType, msg, executingNode);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Parse TLS Key
|
|
247
|
+
let tlsKey = null;
|
|
248
|
+
if (this.tlsKeyType === 'str') {
|
|
249
|
+
tlsKey = this.credentials?.tlsKey;
|
|
250
|
+
} else {
|
|
251
|
+
tlsKey = this.parseCredentialValue(this.tlsKeyContext, this.tlsKeyType, msg, executingNode);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Parse TLS CA
|
|
255
|
+
let tlsCa = null;
|
|
256
|
+
if (this.tlsCaType === 'str') {
|
|
257
|
+
tlsCa = this.credentials?.tlsCa;
|
|
258
|
+
} else {
|
|
259
|
+
tlsCa = this.parseCredentialValue(this.tlsCaContext, this.tlsCaType, msg, executingNode);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add TLS options if provided
|
|
263
|
+
if (tlsCert && tlsKey) {
|
|
264
|
+
connectionOptions.tls.cert = tlsCert;
|
|
265
|
+
connectionOptions.tls.key = tlsKey;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (tlsCa) {
|
|
269
|
+
connectionOptions.tls.ca = tlsCa;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// If using custom CA or self-signed certificates, might need to disable rejection
|
|
273
|
+
if (!this.tlsRejectUnauthorized) {
|
|
274
|
+
connectionOptions.tls.rejectUnauthorized = false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return connectionOptions;
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (executingNode) {
|
|
282
|
+
executingNode.error(`Failed to get Redis connection options: ${error.message}`);
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Get Redis client
|
|
289
|
+
this.getClient = function(msg, executingNode, nodeId) {
|
|
290
|
+
try {
|
|
291
|
+
const id = nodeId || this.id;
|
|
292
|
+
|
|
293
|
+
// Return existing connection if available
|
|
294
|
+
if (connections[id]) {
|
|
295
|
+
usedConn[id]++;
|
|
296
|
+
return connections[id];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const options = this.getConnectionOptions(msg, executingNode);
|
|
300
|
+
|
|
301
|
+
// Create Redis client
|
|
302
|
+
let client;
|
|
303
|
+
if (this.cluster) {
|
|
304
|
+
// For cluster mode, options should be an array of nodes
|
|
305
|
+
const clusterNodes = Array.isArray(options) ? options : [options];
|
|
306
|
+
client = new Redis.Cluster(clusterNodes);
|
|
307
|
+
} else {
|
|
308
|
+
client = new Redis(options);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Handle connection errors
|
|
312
|
+
client.on("error", (e) => {
|
|
313
|
+
if (executingNode) {
|
|
314
|
+
executingNode.error(`Redis connection error: ${e.message}`, {});
|
|
315
|
+
} else {
|
|
316
|
+
this.error(`Redis connection error: ${e.message}`, {});
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Store connection
|
|
321
|
+
connections[id] = client;
|
|
322
|
+
if (usedConn[id] === undefined) {
|
|
323
|
+
usedConn[id] = 1;
|
|
324
|
+
} else {
|
|
325
|
+
usedConn[id]++;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return client;
|
|
329
|
+
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (executingNode) {
|
|
332
|
+
executingNode.error(`Failed to create Redis client: ${error.message}`);
|
|
333
|
+
}
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Disconnect method
|
|
339
|
+
this.disconnect = function(nodeId) {
|
|
340
|
+
const id = nodeId || this.id;
|
|
341
|
+
if (usedConn[id] !== undefined) {
|
|
342
|
+
usedConn[id]--;
|
|
343
|
+
}
|
|
344
|
+
if (connections[id] && usedConn[id] <= 0) {
|
|
345
|
+
connections[id].disconnect();
|
|
346
|
+
delete connections[id];
|
|
347
|
+
delete usedConn[id];
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Clean up on node close
|
|
352
|
+
this.on('close', function() {
|
|
353
|
+
this.disconnect();
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
RED.nodes.registerType("redis-variable-config", RedisConfigNode, {
|
|
358
|
+
credentials: {
|
|
359
|
+
password: { type: "password" },
|
|
360
|
+
username: { type: "text" }
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("redis-variable", {
|
|
3
|
+
category: "Redis",
|
|
4
|
+
color: "#D8BFD8",
|
|
5
|
+
defaults: {
|
|
6
|
+
name: { value: "" },
|
|
7
|
+
redisConfig: { value: "", type: "redis-variable-config" },
|
|
8
|
+
operation: { value: "get" }
|
|
9
|
+
},
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 1,
|
|
12
|
+
icon: "font-awesome/fa-database",
|
|
13
|
+
label: function() {
|
|
14
|
+
return this.name || "Redis (" + (this.operation || "get") + ")";
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script type="text/html" data-template-name="redis-variable">
|
|
20
|
+
<div class="form-row">
|
|
21
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
22
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
23
|
+
</div>
|
|
24
|
+
<div class="form-row">
|
|
25
|
+
<label for="node-input-redisConfig"><i class="fa fa-cog"></i> Redis Config</label>
|
|
26
|
+
<input type="text" id="node-input-redisConfig">
|
|
27
|
+
</div>
|
|
28
|
+
<div class="form-row">
|
|
29
|
+
<label for="node-input-operation"><i class="fa fa-tasks"></i> Operation</label>
|
|
30
|
+
<select id="node-input-operation">
|
|
31
|
+
<optgroup label="Basic Operations">
|
|
32
|
+
<option value="get">GET - Get value</option>
|
|
33
|
+
<option value="set">SET - Set value</option>
|
|
34
|
+
<option value="del">DEL - Delete key</option>
|
|
35
|
+
<option value="exists">EXISTS - Check if key exists</option>
|
|
36
|
+
</optgroup>
|
|
37
|
+
<optgroup label="TTL Operations">
|
|
38
|
+
<option value="ttl">TTL - Get time to live</option>
|
|
39
|
+
<option value="expire">EXPIRE - Set expiration</option>
|
|
40
|
+
<option value="persist">PERSIST - Remove expiration</option>
|
|
41
|
+
</optgroup>
|
|
42
|
+
<optgroup label="Counter Operations">
|
|
43
|
+
<option value="incr">INCR - Increment by 1</option>
|
|
44
|
+
<option value="decr">DECR - Decrement by 1</option>
|
|
45
|
+
<option value="incrby">INCRBY - Increment by N</option>
|
|
46
|
+
<option value="decrby">DECRBY - Decrement by N</option>
|
|
47
|
+
</optgroup>
|
|
48
|
+
<optgroup label="List Operations">
|
|
49
|
+
<option value="lpush">LPUSH - Push to left</option>
|
|
50
|
+
<option value="rpush">RPUSH - Push to right</option>
|
|
51
|
+
<option value="lpop">LPOP - Pop from left</option>
|
|
52
|
+
<option value="rpop">RPOP - Pop from right</option>
|
|
53
|
+
<option value="llen">LLEN - List length</option>
|
|
54
|
+
<option value="lrange">LRANGE - Get range</option>
|
|
55
|
+
</optgroup>
|
|
56
|
+
<optgroup label="Hash Operations">
|
|
57
|
+
<option value="hset">HSET - Set hash field</option>
|
|
58
|
+
<option value="hget">HGET - Get hash field</option>
|
|
59
|
+
<option value="hgetall">HGETALL - Get all fields</option>
|
|
60
|
+
<option value="hdel">HDEL - Delete hash field</option>
|
|
61
|
+
</optgroup>
|
|
62
|
+
<optgroup label="Pub/Sub Operations">
|
|
63
|
+
<option value="publish">PUBLISH - Publish message</option>
|
|
64
|
+
</optgroup>
|
|
65
|
+
</select>
|
|
66
|
+
</div>
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<script type="text/x-red" data-help-name="redis-variable">
|
|
70
|
+
<p>Redis node for comprehensive operations with universal payload-based configuration.</p>
|
|
71
|
+
|
|
72
|
+
<h3>Configuration</h3>
|
|
73
|
+
<p>Select or create a Redis configuration that contains your connection settings.</p>
|
|
74
|
+
|
|
75
|
+
<h3>Operations</h3>
|
|
76
|
+
|
|
77
|
+
<h4>Basic Operations</h4>
|
|
78
|
+
<ul>
|
|
79
|
+
<li><b>GET</b>: Retrieve value by key</li>
|
|
80
|
+
<li><b>SET</b>: Store value with key (supports TTL)</li>
|
|
81
|
+
<li><b>DEL</b>: Delete key(s)</li>
|
|
82
|
+
<li><b>EXISTS</b>: Check if key(s) exist</li>
|
|
83
|
+
</ul>
|
|
84
|
+
|
|
85
|
+
<h4>TTL Operations</h4>
|
|
86
|
+
<ul>
|
|
87
|
+
<li><b>TTL</b>: Get remaining time to live in seconds</li>
|
|
88
|
+
<li><b>EXPIRE</b>: Set expiration time for existing key</li>
|
|
89
|
+
<li><b>PERSIST</b>: Remove expiration from key</li>
|
|
90
|
+
</ul>
|
|
91
|
+
|
|
92
|
+
<h4>Counter Operations</h4>
|
|
93
|
+
<ul>
|
|
94
|
+
<li><b>INCR</b>: Increment value by 1</li>
|
|
95
|
+
<li><b>DECR</b>: Decrement value by 1</li>
|
|
96
|
+
<li><b>INCRBY</b>: Increment value by N</li>
|
|
97
|
+
<li><b>DECRBY</b>: Decrement value by N</li>
|
|
98
|
+
</ul>
|
|
99
|
+
|
|
100
|
+
<h4>List Operations</h4>
|
|
101
|
+
<ul>
|
|
102
|
+
<li><b>LPUSH</b>: Add element to beginning of list</li>
|
|
103
|
+
<li><b>RPUSH</b>: Add element to end of list</li>
|
|
104
|
+
<li><b>LPOP</b>: Remove and return first element</li>
|
|
105
|
+
<li><b>RPOP</b>: Remove and return last element</li>
|
|
106
|
+
<li><b>LLEN</b>: Get list length</li>
|
|
107
|
+
<li><b>LRANGE</b>: Get range of elements</li>
|
|
108
|
+
</ul>
|
|
109
|
+
|
|
110
|
+
<h4>Hash Operations</h4>
|
|
111
|
+
<ul>
|
|
112
|
+
<li><b>HSET</b>: Set hash field value</li>
|
|
113
|
+
<li><b>HGET</b>: Get hash field value</li>
|
|
114
|
+
<li><b>HGETALL</b>: Get all hash fields</li>
|
|
115
|
+
<li><b>HDEL</b>: Delete hash field(s)</li>
|
|
116
|
+
</ul>
|
|
117
|
+
|
|
118
|
+
<h4>Pub/Sub Operations</h4>
|
|
119
|
+
<ul>
|
|
120
|
+
<li><b>PUBLISH</b>: Publish message to channel</li>
|
|
121
|
+
</ul>
|
|
122
|
+
|
|
123
|
+
<h3>Universal Payload Format</h3>
|
|
124
|
+
<p>All parameters are passed via <code>msg.payload</code>. The payload can be a string (for simple operations) or an object with specific properties.</p>
|
|
125
|
+
|
|
126
|
+
<h3>Examples</h3>
|
|
127
|
+
|
|
128
|
+
<h4>Basic Operations</h4>
|
|
129
|
+
<pre>// GET - simple format
|
|
130
|
+
{
|
|
131
|
+
"payload": "user:123"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// GET - object format
|
|
135
|
+
{
|
|
136
|
+
"payload": {"key": "user:123"}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// SET with value
|
|
140
|
+
{
|
|
141
|
+
"payload": {
|
|
142
|
+
"key": "user:123",
|
|
143
|
+
"value": "John Doe"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// SET with TTL (expires in 1 hour)
|
|
148
|
+
{
|
|
149
|
+
"payload": {
|
|
150
|
+
"key": "session:abc123",
|
|
151
|
+
"value": {"userId": 42, "role": "admin"},
|
|
152
|
+
"ttl": 3600
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// DELETE multiple keys
|
|
157
|
+
{
|
|
158
|
+
"payload": {
|
|
159
|
+
"keys": ["cache:page1", "cache:page2", "temp:data"]
|
|
160
|
+
}
|
|
161
|
+
}</pre>
|
|
162
|
+
|
|
163
|
+
<h4>TTL Operations</h4>
|
|
164
|
+
<pre>// Check TTL
|
|
165
|
+
{
|
|
166
|
+
"payload": "session:abc123"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Set expiration
|
|
170
|
+
{
|
|
171
|
+
"payload": {
|
|
172
|
+
"key": "temp:data",
|
|
173
|
+
"ttl": 1800
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Remove expiration
|
|
178
|
+
{
|
|
179
|
+
"payload": "permanent:key"
|
|
180
|
+
}</pre>
|
|
181
|
+
|
|
182
|
+
<h4>Counter Operations</h4>
|
|
183
|
+
<pre>// Increment by 1
|
|
184
|
+
{
|
|
185
|
+
"payload": "page:views"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Increment by amount
|
|
189
|
+
{
|
|
190
|
+
"payload": {
|
|
191
|
+
"key": "score:player1",
|
|
192
|
+
"amount": 100
|
|
193
|
+
}
|
|
194
|
+
}</pre>
|
|
195
|
+
|
|
196
|
+
<h4>List Operations</h4>
|
|
197
|
+
<pre>// Add to list
|
|
198
|
+
{
|
|
199
|
+
"payload": {
|
|
200
|
+
"key": "queue:tasks",
|
|
201
|
+
"value": {"task": "process_order", "id": 123}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Get range
|
|
206
|
+
{
|
|
207
|
+
"payload": {
|
|
208
|
+
"key": "queue:tasks",
|
|
209
|
+
"start": 0,
|
|
210
|
+
"stop": 9
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Pop from list
|
|
215
|
+
{
|
|
216
|
+
"payload": "queue:tasks"
|
|
217
|
+
}</pre>
|
|
218
|
+
|
|
219
|
+
<h4>Hash Operations</h4>
|
|
220
|
+
<pre>// Set single field
|
|
221
|
+
{
|
|
222
|
+
"payload": {
|
|
223
|
+
"key": "user:123",
|
|
224
|
+
"field": "email",
|
|
225
|
+
"value": "user@example.com"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Set multiple fields
|
|
230
|
+
{
|
|
231
|
+
"payload": {
|
|
232
|
+
"key": "user:123",
|
|
233
|
+
"fields": {
|
|
234
|
+
"name": "John Doe",
|
|
235
|
+
"age": 30,
|
|
236
|
+
"city": "New York"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Get field
|
|
242
|
+
{
|
|
243
|
+
"payload": {
|
|
244
|
+
"key": "user:123",
|
|
245
|
+
"field": "email"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get all fields
|
|
250
|
+
{
|
|
251
|
+
"payload": "user:123"
|
|
252
|
+
}</pre>
|
|
253
|
+
|
|
254
|
+
<h4>Pub/Sub Operations</h4>
|
|
255
|
+
<pre>// Publish message
|
|
256
|
+
{
|
|
257
|
+
"payload": {
|
|
258
|
+
"channel": "notifications",
|
|
259
|
+
"message": {
|
|
260
|
+
"type": "alert",
|
|
261
|
+
"text": "System maintenance"
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}</pre>
|
|
265
|
+
|
|
266
|
+
<h3>Automatic JSON Handling</h3>
|
|
267
|
+
<p>The node automatically detects and handles JSON data:</p>
|
|
268
|
+
<ul>
|
|
269
|
+
<li><b>When storing</b>: JavaScript objects are automatically serialized to JSON strings</li>
|
|
270
|
+
<li><b>When retrieving</b>: Valid JSON strings are automatically parsed back to objects</li>
|
|
271
|
+
<li><b>Simple values</b>: Strings, numbers, and other simple types are handled as-is</li>
|
|
272
|
+
<li>Applies to all operations that handle values (GET, SET, lists, hashes, pub/sub)</li>
|
|
273
|
+
</ul>
|
|
274
|
+
|
|
275
|
+
<h3>Output Examples</h3>
|
|
276
|
+
<ul>
|
|
277
|
+
<li><b>GET</b>: Returns the stored value (auto-parsed if JSON)</li>
|
|
278
|
+
<li><b>SET</b>: <code>{ success: true, result: "OK", ttl: 3600 }</code></li>
|
|
279
|
+
<li><b>TTL</b>: <code>{ key: "mykey", ttl: 3600, status: "expires in 3600 seconds" }</code></li>
|
|
280
|
+
<li><b>INCR</b>: <code>{ key: "counter", value: 42 }</code></li>
|
|
281
|
+
<li><b>LPUSH</b>: <code>{ success: true, key: "list", length: 5, operation: "lpush" }</code></li>
|
|
282
|
+
<li><b>HGETALL</b>: <code>{ name: "John", age: 30, email: "john@example.com" }</code> (auto-parsed)</li>
|
|
283
|
+
</ul>
|
|
284
|
+
</script>
|