node-red-contrib-db-storage 0.2.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 +557 -0
- package/adapters/AdapterFactory.js +87 -0
- package/adapters/DatabaseAdapter.js +135 -0
- package/adapters/MongoAdapter.js +187 -0
- package/adapters/MySQLAdapter.js +172 -0
- package/adapters/PostgresAdapter.js +167 -0
- package/adapters/SQLiteAdapter.js +161 -0
- package/adapters/index.js +15 -0
- package/constants.js +8 -0
- package/examples/node-red-app.js +115 -0
- package/index.js +185 -0
- package/package.json +69 -0
- package/utils/UrlParser.js +140 -0
- package/utils/index.js +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Muralidhar Reddy Challa
|
|
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,557 @@
|
|
|
1
|
+
# Node-RED Storage Plugin
|
|
2
|
+
|
|
3
|
+
A flexible storage plugin for Node-RED that supports multiple database backends including MongoDB, PostgreSQL, MySQL/MariaDB, and SQLite.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-database support**: MongoDB, PostgreSQL, MySQL/MariaDB, SQLite
|
|
8
|
+
- Store all Node-RED data in your preferred database
|
|
9
|
+
- Separate collections/tables for each entity type
|
|
10
|
+
- Configurable collection/table names
|
|
11
|
+
- Support for Node-RED library entries
|
|
12
|
+
- Backward compatible with existing MongoDB configurations
|
|
13
|
+
- **Extensible adapter registry** - easily add custom database adapters
|
|
14
|
+
- Standardized configuration interface across all adapters
|
|
15
|
+
- Input validation and SQL injection protection
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install node-red-contrib-db-storage
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Database-specific dependencies
|
|
24
|
+
|
|
25
|
+
**MongoDB** (included by default):
|
|
26
|
+
```bash
|
|
27
|
+
# MongoDB driver is included
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**PostgreSQL**:
|
|
31
|
+
```bash
|
|
32
|
+
npm install pg
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**MySQL/MariaDB**:
|
|
36
|
+
```bash
|
|
37
|
+
npm install mysql2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**SQLite**:
|
|
41
|
+
```bash
|
|
42
|
+
npm install better-sqlite3
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- Node.js >= 10.x
|
|
48
|
+
- Node-RED (embedded mode)
|
|
49
|
+
- One of the following databases:
|
|
50
|
+
- MongoDB >= 3.4
|
|
51
|
+
- PostgreSQL >= 9.6
|
|
52
|
+
- MySQL >= 5.7 or MariaDB >= 10.2
|
|
53
|
+
- SQLite 3
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### MongoDB Configuration
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const settings = {
|
|
61
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
62
|
+
storageModuleOptions: {
|
|
63
|
+
type: 'mongodb',
|
|
64
|
+
url: 'mongodb://localhost:27017',
|
|
65
|
+
database: 'nodered'
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### PostgreSQL Configuration
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const settings = {
|
|
74
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
75
|
+
storageModuleOptions: {
|
|
76
|
+
type: 'postgres',
|
|
77
|
+
url: 'postgresql://user:password@localhost:5432/nodered'
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### MySQL/MariaDB Configuration
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
const settings = {
|
|
86
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
87
|
+
storageModuleOptions: {
|
|
88
|
+
type: 'mysql', // or 'mariadb'
|
|
89
|
+
url: 'mysql://user:password@localhost:3306/nodered'
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### SQLite Configuration
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
const settings = {
|
|
98
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
99
|
+
storageModuleOptions: {
|
|
100
|
+
type: 'sqlite',
|
|
101
|
+
url: '/path/to/nodered.db'
|
|
102
|
+
// or use ':memory:' for in-memory database
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
This plugin requires running Node-RED in embedded mode. See the [Node-RED embedding guide](http://nodered.org/docs/embedding.html) for details.
|
|
110
|
+
|
|
111
|
+
### Complete Example
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const http = require('http');
|
|
115
|
+
const express = require('express');
|
|
116
|
+
const RED = require('node-red');
|
|
117
|
+
|
|
118
|
+
const app = express();
|
|
119
|
+
const server = http.createServer(app);
|
|
120
|
+
|
|
121
|
+
const settings = {
|
|
122
|
+
httpAdminRoot: '/red',
|
|
123
|
+
httpNodeRoot: '/api',
|
|
124
|
+
userDir: './user',
|
|
125
|
+
|
|
126
|
+
// Storage Plugin Configuration
|
|
127
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
128
|
+
storageModuleOptions: {
|
|
129
|
+
type: 'mongodb', // or 'postgres', 'mysql', 'sqlite'
|
|
130
|
+
url: 'mongodb://localhost:27017',
|
|
131
|
+
database: 'nodered',
|
|
132
|
+
|
|
133
|
+
// Optional: custom collection/table names
|
|
134
|
+
collectionNames: {
|
|
135
|
+
flows: 'my-flows',
|
|
136
|
+
credentials: 'my-credentials',
|
|
137
|
+
settings: 'my-settings',
|
|
138
|
+
sessions: 'my-sessions'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
RED.init(server, settings);
|
|
144
|
+
|
|
145
|
+
app.use(settings.httpAdminRoot, RED.httpAdmin);
|
|
146
|
+
app.use(settings.httpNodeRoot, RED.httpNode);
|
|
147
|
+
|
|
148
|
+
server.listen(8000, () => {
|
|
149
|
+
console.log('Node-RED server running on port 8000');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
RED.start();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Configuration Options
|
|
156
|
+
|
|
157
|
+
| Option | Type | Required | Description |
|
|
158
|
+
|--------|------|----------|-------------|
|
|
159
|
+
| `type` | string | Yes* | Database type: `'mongodb'`, `'postgres'`, `'mysql'`, `'sqlite'` |
|
|
160
|
+
| `url` | string | Yes | Database connection URL or file path |
|
|
161
|
+
| `database` | string | No** | Database name |
|
|
162
|
+
| `collectionNames` | object | No | Custom collection/table names |
|
|
163
|
+
|
|
164
|
+
*For backward compatibility, `mongoUrl` or `postgresUrl` can be used instead of `type` + `url`
|
|
165
|
+
|
|
166
|
+
**Database name is automatically extracted from the URL for PostgreSQL, MySQL, and SQLite. Required for MongoDB if not in URL.
|
|
167
|
+
|
|
168
|
+
### Legacy Configuration (Backward Compatible)
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
// Old MongoDB configuration still works
|
|
172
|
+
storageModuleOptions: {
|
|
173
|
+
mongoUrl: 'mongodb://localhost:27017',
|
|
174
|
+
database: 'nodered'
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Environment Variables
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const settings = {
|
|
182
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
183
|
+
storageModuleOptions: {
|
|
184
|
+
type: process.env.DB_TYPE || 'mongodb',
|
|
185
|
+
url: process.env.DATABASE_URL || 'mongodb://localhost:27017',
|
|
186
|
+
database: process.env.DB_NAME || 'nodered'
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Default Collection/Table Names
|
|
192
|
+
|
|
193
|
+
| Entity | Default Name |
|
|
194
|
+
|--------|-------------|
|
|
195
|
+
| Flows | `nodered-flows` |
|
|
196
|
+
| Credentials | `nodered-credentials` |
|
|
197
|
+
| Settings | `nodered-settings` |
|
|
198
|
+
| Sessions | `nodered-sessions` |
|
|
199
|
+
|
|
200
|
+
## API
|
|
201
|
+
|
|
202
|
+
The plugin implements the Node-RED storage module interface:
|
|
203
|
+
|
|
204
|
+
- `init(settings)` - Initialize the storage module
|
|
205
|
+
- `getFlows()` / `saveFlows(flows)` - Manage flows
|
|
206
|
+
- `getCredentials()` / `saveCredentials(credentials)` - Manage credentials
|
|
207
|
+
- `getSettings()` / `saveSettings(settings)` - Manage settings
|
|
208
|
+
- `getSessions()` / `saveSessions(sessions)` - Manage sessions
|
|
209
|
+
- `getLibraryEntry(type, path)` / `saveLibraryEntry(type, path, meta, body)` - Manage library entries
|
|
210
|
+
- `close()` - Close database connection
|
|
211
|
+
|
|
212
|
+
## Creating Custom Adapters
|
|
213
|
+
|
|
214
|
+
You can create your own database adapter by extending the `DatabaseAdapter` class and registering it with the factory:
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
const { DatabaseAdapter, AdapterFactory } = require('node-red-contrib-db-storage/adapters');
|
|
218
|
+
|
|
219
|
+
class MyCustomAdapter extends DatabaseAdapter {
|
|
220
|
+
constructor(config) {
|
|
221
|
+
super(config);
|
|
222
|
+
// Initialize your adapter
|
|
223
|
+
// this.url and this.databaseName are set by parent class
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async connect() {
|
|
227
|
+
// Connect to your database
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async close() {
|
|
231
|
+
// Close connection
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async findAll(collectionName) {
|
|
235
|
+
// Validate input (inherited from parent)
|
|
236
|
+
this.validateCollectionName(collectionName);
|
|
237
|
+
// Return all documents as array
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async saveAll(collectionName, objects) {
|
|
241
|
+
this.validateCollectionName(collectionName);
|
|
242
|
+
this.validateObjectArray(objects);
|
|
243
|
+
// Save all objects (replace existing)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async findOneByPath(collectionName, path) {
|
|
247
|
+
this.validateCollectionName(collectionName);
|
|
248
|
+
this.validatePath(path);
|
|
249
|
+
// Find by path, return body or {}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async saveOrUpdateByPath(collectionName, path, meta, body) {
|
|
253
|
+
this.validateCollectionName(collectionName);
|
|
254
|
+
this.validatePath(path);
|
|
255
|
+
// Upsert by path
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Register the adapter with the factory
|
|
260
|
+
AdapterFactory.register(['mycustom', 'custom'], MyCustomAdapter);
|
|
261
|
+
|
|
262
|
+
// Now you can use it in configuration:
|
|
263
|
+
// { type: 'mycustom', url: '...', database: '...' }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Using the Adapter Registry
|
|
267
|
+
|
|
268
|
+
The `AdapterFactory` uses a registry pattern for extensibility:
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
const { AdapterFactory } = require('node-red-contrib-db-storage/adapters');
|
|
272
|
+
|
|
273
|
+
// Register a new adapter type
|
|
274
|
+
AdapterFactory.register('redis', RedisAdapter);
|
|
275
|
+
|
|
276
|
+
// Register with multiple aliases
|
|
277
|
+
AdapterFactory.register(['cockroachdb', 'crdb'], CockroachAdapter);
|
|
278
|
+
|
|
279
|
+
// Check if a type is registered
|
|
280
|
+
AdapterFactory.isRegistered('redis'); // true
|
|
281
|
+
|
|
282
|
+
// Get all supported types
|
|
283
|
+
AdapterFactory.getSupportedTypes(); // ['mongodb', 'postgres', 'mysql', 'sqlite', 'redis', ...]
|
|
284
|
+
|
|
285
|
+
// Unregister an adapter
|
|
286
|
+
AdapterFactory.unregister('redis');
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
### Setup
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
git clone https://github.com/srmurali002/node-red-contrib-db-storage.git
|
|
295
|
+
cd node-red-storage-plugin
|
|
296
|
+
npm install
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Running Tests
|
|
300
|
+
|
|
301
|
+
**Unit Tests** (with mocks, no database required):
|
|
302
|
+
```bash
|
|
303
|
+
npm test
|
|
304
|
+
# or
|
|
305
|
+
npm run test:unit
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Integration Tests** (require running databases):
|
|
309
|
+
```bash
|
|
310
|
+
# Start test databases
|
|
311
|
+
npm run docker:up
|
|
312
|
+
|
|
313
|
+
# Run all integration tests
|
|
314
|
+
npm run test:integration
|
|
315
|
+
|
|
316
|
+
# Run MongoDB integration tests only
|
|
317
|
+
npm run test:integration:mongo
|
|
318
|
+
|
|
319
|
+
# Run PostgreSQL integration tests only
|
|
320
|
+
npm run test:integration:postgres
|
|
321
|
+
|
|
322
|
+
# Run all tests (unit + integration)
|
|
323
|
+
npm run test:all
|
|
324
|
+
|
|
325
|
+
# Stop test databases
|
|
326
|
+
npm run docker:down
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Environment Variables for Integration Tests:**
|
|
330
|
+
```bash
|
|
331
|
+
MONGO_URL=mongodb://localhost:27017
|
|
332
|
+
MONGO_DATABASE=nodered_test
|
|
333
|
+
POSTGRES_URL=postgresql://nodered:nodered@localhost:5432/nodered_test
|
|
334
|
+
TEST_DATABASES=mongodb,postgres # or just one
|
|
335
|
+
SKIP_INTEGRATION=true # skip integration tests
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Project Structure
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
node-red-contrib-db-storage/
|
|
342
|
+
├── index.js # Main module
|
|
343
|
+
├── constants.js # Default configuration
|
|
344
|
+
├── adapters/
|
|
345
|
+
│ ├── index.js # Adapter exports
|
|
346
|
+
│ ├── DatabaseAdapter.js # Base adapter class
|
|
347
|
+
│ ├── AdapterFactory.js # Adapter factory with registry
|
|
348
|
+
│ ├── MongoAdapter.js # MongoDB implementation
|
|
349
|
+
│ ├── PostgresAdapter.js # PostgreSQL implementation
|
|
350
|
+
│ ├── MySQLAdapter.js # MySQL/MariaDB implementation
|
|
351
|
+
│ └── SQLiteAdapter.js # SQLite implementation
|
|
352
|
+
├── utils/
|
|
353
|
+
│ ├── index.js # Utility exports
|
|
354
|
+
│ └── UrlParser.js # URL parsing utilities
|
|
355
|
+
├── tests/
|
|
356
|
+
│ └── UrlParser.test.js # URL parser tests
|
|
357
|
+
├── examples/
|
|
358
|
+
│ └── node-red-app.js # Example application
|
|
359
|
+
└── package.json
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Troubleshooting
|
|
363
|
+
|
|
364
|
+
### Common Issues
|
|
365
|
+
|
|
366
|
+
#### "storageModuleOptions is required"
|
|
367
|
+
**Cause:** The settings object passed to Node-RED is missing the `storageModuleOptions` property.
|
|
368
|
+
|
|
369
|
+
**Solution:** Ensure your settings include the required options:
|
|
370
|
+
```javascript
|
|
371
|
+
const settings = {
|
|
372
|
+
storageModule: require('node-red-contrib-db-storage'),
|
|
373
|
+
storageModuleOptions: {
|
|
374
|
+
type: 'mongodb',
|
|
375
|
+
url: 'mongodb://localhost:27017',
|
|
376
|
+
database: 'nodered'
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### "Database URL is required"
|
|
382
|
+
**Cause:** No database URL was provided in the configuration.
|
|
383
|
+
|
|
384
|
+
**Solution:** Provide either:
|
|
385
|
+
- `url` with `type` (new format)
|
|
386
|
+
- `mongoUrl` (legacy MongoDB format)
|
|
387
|
+
- `postgresUrl` (legacy PostgreSQL format)
|
|
388
|
+
|
|
389
|
+
#### "Database name is required"
|
|
390
|
+
**Cause:** The database name could not be determined from the URL or configuration.
|
|
391
|
+
|
|
392
|
+
**Solution:** Either include the database name in the URL or add the `database` property:
|
|
393
|
+
```javascript
|
|
394
|
+
storageModuleOptions: {
|
|
395
|
+
type: 'mongodb',
|
|
396
|
+
url: 'mongodb://localhost:27017/nodered', // Database in URL
|
|
397
|
+
// or
|
|
398
|
+
database: 'nodered' // Explicit database name
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### "PostgreSQL adapter requires the 'pg' package"
|
|
403
|
+
**Cause:** The `pg` package is not installed but you're trying to use PostgreSQL.
|
|
404
|
+
|
|
405
|
+
**Solution:** Install the PostgreSQL driver:
|
|
406
|
+
```bash
|
|
407
|
+
npm install pg
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### "MySQL adapter requires the 'mysql2' package"
|
|
411
|
+
**Cause:** The `mysql2` package is not installed but you're trying to use MySQL/MariaDB.
|
|
412
|
+
|
|
413
|
+
**Solution:** Install the MySQL driver:
|
|
414
|
+
```bash
|
|
415
|
+
npm install mysql2
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
#### "SQLite adapter requires the 'better-sqlite3' package"
|
|
419
|
+
**Cause:** The `better-sqlite3` package is not installed but you're trying to use SQLite.
|
|
420
|
+
|
|
421
|
+
**Solution:** Install the SQLite driver:
|
|
422
|
+
```bash
|
|
423
|
+
npm install better-sqlite3
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
#### Connection Timeouts
|
|
427
|
+
**Cause:** Database server is not running or not accessible.
|
|
428
|
+
|
|
429
|
+
**Solutions:**
|
|
430
|
+
1. Verify the database server is running
|
|
431
|
+
2. Check the connection URL is correct
|
|
432
|
+
3. Ensure network/firewall allows the connection
|
|
433
|
+
4. For Docker, ensure the container is healthy:
|
|
434
|
+
```bash
|
|
435
|
+
docker ps
|
|
436
|
+
docker logs <container-name>
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Data Not Persisting
|
|
440
|
+
**Cause:** Multiple Node-RED instances using the same database, or data being overwritten.
|
|
441
|
+
|
|
442
|
+
**Solutions:**
|
|
443
|
+
1. Use unique collection names for each Node-RED instance
|
|
444
|
+
2. Check that `saveFlows()` is being called on deploy
|
|
445
|
+
3. Verify database connection is still active
|
|
446
|
+
|
|
447
|
+
### Debug Mode
|
|
448
|
+
|
|
449
|
+
Enable debug logging by setting the `DEBUG` environment variable:
|
|
450
|
+
```bash
|
|
451
|
+
DEBUG=* node your-app.js
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Database-Specific Issues
|
|
455
|
+
|
|
456
|
+
#### MongoDB: "MongoNetworkError"
|
|
457
|
+
- Check MongoDB is running: `mongosh --eval "db.runCommand('ping')"`
|
|
458
|
+
- Verify connection string format: `mongodb://[user:pass@]host[:port]`
|
|
459
|
+
|
|
460
|
+
#### PostgreSQL: "ECONNREFUSED"
|
|
461
|
+
- Check PostgreSQL is running: `pg_isready`
|
|
462
|
+
- Verify connection string format: `postgresql://user:pass@host:port/database`
|
|
463
|
+
- Ensure the database exists: `psql -l`
|
|
464
|
+
|
|
465
|
+
#### MySQL: "ECONNREFUSED" or "Access denied"
|
|
466
|
+
- Check MySQL is running: `mysqladmin ping`
|
|
467
|
+
- Verify connection string format: `mysql://user:pass@host:port/database`
|
|
468
|
+
- Ensure user has necessary permissions: `GRANT ALL PRIVILEGES ON database.* TO 'user'@'host'`
|
|
469
|
+
|
|
470
|
+
#### SQLite: "SQLITE_CANTOPEN"
|
|
471
|
+
- Ensure the directory for the database file exists
|
|
472
|
+
- Check file permissions for the target path
|
|
473
|
+
- For in-memory databases, use `:memory:` as the URL
|
|
474
|
+
|
|
475
|
+
### Getting Help
|
|
476
|
+
|
|
477
|
+
If you're still experiencing issues:
|
|
478
|
+
1. Check [existing issues](https://github.com/srmurali002/node-red-contrib-db-storage/issues)
|
|
479
|
+
2. Create a new issue with:
|
|
480
|
+
- Node.js version
|
|
481
|
+
- Node-RED version
|
|
482
|
+
- Database type and version
|
|
483
|
+
- Full error message and stack trace
|
|
484
|
+
- Configuration (without sensitive data)
|
|
485
|
+
|
|
486
|
+
## Changelog
|
|
487
|
+
|
|
488
|
+
### v0.3.0 (2025)
|
|
489
|
+
- Added MySQL/MariaDB adapter
|
|
490
|
+
- Added SQLite adapter
|
|
491
|
+
- **New**: Adapter registry pattern for extensibility (Open/Closed Principle)
|
|
492
|
+
- **New**: URL parsing utility for automatic database name extraction
|
|
493
|
+
- **New**: Input validation to prevent SQL injection
|
|
494
|
+
- Refactored to encapsulate module state
|
|
495
|
+
- Standardized configuration interface across all adapters
|
|
496
|
+
- Comprehensive test coverage (136 tests)
|
|
497
|
+
|
|
498
|
+
### v0.2.0 (2025)
|
|
499
|
+
- **Breaking**: Restructured to support multiple databases
|
|
500
|
+
- Added PostgreSQL adapter
|
|
501
|
+
- Added database adapter abstraction layer
|
|
502
|
+
- Added AdapterFactory for creating adapters
|
|
503
|
+
- Backward compatible with existing MongoDB configurations
|
|
504
|
+
- Improved error handling and validation
|
|
505
|
+
- Comprehensive test suite
|
|
506
|
+
|
|
507
|
+
### v0.1.3 (2025)
|
|
508
|
+
- Fixed critical bugs in MongoDB operations
|
|
509
|
+
- Added unit tests
|
|
510
|
+
|
|
511
|
+
### v0.1.2 (2019)
|
|
512
|
+
- Added configurable collection names
|
|
513
|
+
|
|
514
|
+
## Roadmap
|
|
515
|
+
|
|
516
|
+
- [x] Integration tests
|
|
517
|
+
- [x] MySQL/MariaDB adapter
|
|
518
|
+
- [x] SQLite adapter
|
|
519
|
+
- [x] Adapter registry pattern
|
|
520
|
+
- [x] Input validation and security
|
|
521
|
+
- [ ] Redis adapter
|
|
522
|
+
- [ ] Connection pooling configuration
|
|
523
|
+
- [ ] Retry logic for connection failures
|
|
524
|
+
- [ ] TypeScript definitions
|
|
525
|
+
- [ ] Migration utilities between databases
|
|
526
|
+
- [ ] Per-data-type adapter configuration (e.g., Redis for sessions, PostgreSQL for flows)
|
|
527
|
+
|
|
528
|
+
## Contributing
|
|
529
|
+
|
|
530
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
531
|
+
|
|
532
|
+
1. Fork the repository
|
|
533
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
534
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
535
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
536
|
+
5. Open a Pull Request
|
|
537
|
+
|
|
538
|
+
### Adding a New Database Adapter
|
|
539
|
+
|
|
540
|
+
1. Create a new adapter file in `adapters/` extending `DatabaseAdapter`
|
|
541
|
+
2. Register it with `AdapterFactory.register()` in `AdapterFactory.js`
|
|
542
|
+
3. Export it from `adapters/index.js`
|
|
543
|
+
4. Add unit tests in `adapters/YourAdapter.test.js`
|
|
544
|
+
5. Add integration tests in `adapters/YourAdapter.integration.test.js`
|
|
545
|
+
6. Update documentation in README.md
|
|
546
|
+
|
|
547
|
+
## License
|
|
548
|
+
|
|
549
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
550
|
+
|
|
551
|
+
## Credits
|
|
552
|
+
|
|
553
|
+
Multi-database support and continued development by [Muralidhar Reddy Challa](https://github.com/srmurali002).
|
|
554
|
+
|
|
555
|
+
## Support
|
|
556
|
+
|
|
557
|
+
If you encounter any issues or have questions, please [open an issue](https://github.com/srmurali002/node-red-contrib-db-storage/issues).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const MongoAdapter = require('./MongoAdapter');
|
|
2
|
+
const PostgresAdapter = require('./PostgresAdapter');
|
|
3
|
+
const MySQLAdapter = require('./MySQLAdapter');
|
|
4
|
+
const SQLiteAdapter = require('./SQLiteAdapter');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Factory for creating database adapters using a registry pattern.
|
|
8
|
+
* Follows Open/Closed Principle - open for extension, closed for modification.
|
|
9
|
+
*
|
|
10
|
+
* All adapters use a standardized configuration interface:
|
|
11
|
+
* @typedef {Object} AdapterConfig
|
|
12
|
+
* @property {string} url - Required: Database connection URL
|
|
13
|
+
* @property {string} database - Required: Database name
|
|
14
|
+
*/
|
|
15
|
+
class AdapterFactory {
|
|
16
|
+
// Private registry for adapter types
|
|
17
|
+
static #registry = new Map();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register a new adapter type
|
|
21
|
+
* @param {string|Array<string>} types - The type name(s) to register (e.g., 'mongodb' or ['mongodb', 'mongo'])
|
|
22
|
+
* @param {Function} AdapterClass - The adapter class constructor
|
|
23
|
+
*/
|
|
24
|
+
static register(types, AdapterClass) {
|
|
25
|
+
const typeArray = Array.isArray(types) ? types : [types];
|
|
26
|
+
for (const type of typeArray) {
|
|
27
|
+
this.#registry.set(type.toLowerCase(), AdapterClass);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Unregister an adapter type
|
|
33
|
+
* @param {string} type - The type name to unregister
|
|
34
|
+
*/
|
|
35
|
+
static unregister(type) {
|
|
36
|
+
this.#registry.delete(type.toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a database adapter based on the type
|
|
41
|
+
* @param {string} type - The database type ('mongodb', 'postgres', 'mysql', 'sqlite')
|
|
42
|
+
* @param {AdapterConfig} config - Standardized configuration for the adapter
|
|
43
|
+
* @returns {DatabaseAdapter} The database adapter instance
|
|
44
|
+
*/
|
|
45
|
+
static create(type, config) {
|
|
46
|
+
const AdapterClass = this.#registry.get(type.toLowerCase());
|
|
47
|
+
|
|
48
|
+
if (!AdapterClass) {
|
|
49
|
+
const supportedTypes = this.getSupportedTypes().join(', ');
|
|
50
|
+
throw new Error(`Unsupported database type: ${type}. Supported types: ${supportedTypes}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return new AdapterClass(config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if an adapter type is registered
|
|
58
|
+
* @param {string} type - The type name to check
|
|
59
|
+
* @returns {boolean} True if the type is registered
|
|
60
|
+
*/
|
|
61
|
+
static isRegistered(type) {
|
|
62
|
+
return this.#registry.has(type.toLowerCase());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get list of supported database types
|
|
67
|
+
* @returns {Array<string>} List of supported database types
|
|
68
|
+
*/
|
|
69
|
+
static getSupportedTypes() {
|
|
70
|
+
return [...new Set(this.#registry.keys())];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Clear all registered adapters (useful for testing)
|
|
75
|
+
*/
|
|
76
|
+
static clearRegistry() {
|
|
77
|
+
this.#registry.clear();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Register built-in adapters
|
|
82
|
+
AdapterFactory.register(['mongodb', 'mongo'], MongoAdapter);
|
|
83
|
+
AdapterFactory.register(['postgresql', 'postgres', 'pg'], PostgresAdapter);
|
|
84
|
+
AdapterFactory.register(['mysql', 'mariadb'], MySQLAdapter);
|
|
85
|
+
AdapterFactory.register(['sqlite', 'sqlite3'], SQLiteAdapter);
|
|
86
|
+
|
|
87
|
+
module.exports = AdapterFactory;
|