s3db.js 8.1.3 → 8.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/bin/cli.js +430 -0
- package/dist/s3db.cjs.js +41 -25
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +41 -25
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +41 -25
- package/dist/s3db.iife.min.js +1 -1
- package/mcp/server.js +2 -1
- package/package.json +6 -2
- package/src/database.class.js +3 -2
- package/src/plugins/audit.plugin.js +43 -22
package/bin/cli.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { readFileSync, existsSync } from 'fs';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
|
|
10
|
+
// Load environment variables
|
|
11
|
+
config();
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
17
|
+
|
|
18
|
+
// Colors for console output
|
|
19
|
+
const colors = {
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m',
|
|
23
|
+
blue: '\x1b[34m',
|
|
24
|
+
magenta: '\x1b[35m',
|
|
25
|
+
cyan: '\x1b[36m',
|
|
26
|
+
white: '\x1b[37m',
|
|
27
|
+
reset: '\x1b[0m',
|
|
28
|
+
bright: '\x1b[1m'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Helper functions
|
|
32
|
+
function log(message, color = colors.white) {
|
|
33
|
+
console.log(`${color}${message}${colors.reset}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function error(message) {
|
|
37
|
+
log(`❌ ${message}`, colors.red);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function success(message) {
|
|
41
|
+
log(`✅ ${message}`, colors.green);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function info(message) {
|
|
45
|
+
log(`ℹ️ ${message}`, colors.blue);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function warn(message) {
|
|
49
|
+
log(`⚠️ ${message}`, colors.yellow);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Auto-detect connection string from various sources
|
|
53
|
+
function detectConnectionString() {
|
|
54
|
+
// Priority order for connection string detection
|
|
55
|
+
const sources = [
|
|
56
|
+
// 1. Environment variable
|
|
57
|
+
() => process.env.S3DB_CONNECTION_STRING,
|
|
58
|
+
() => process.env.S3_CONNECTION_STRING,
|
|
59
|
+
() => process.env.DATABASE_URL,
|
|
60
|
+
|
|
61
|
+
// 2. AWS credentials from environment
|
|
62
|
+
() => {
|
|
63
|
+
const key = process.env.AWS_ACCESS_KEY_ID;
|
|
64
|
+
const secret = process.env.AWS_SECRET_ACCESS_KEY;
|
|
65
|
+
const bucket = process.env.AWS_S3_BUCKET || process.env.S3_BUCKET;
|
|
66
|
+
const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
|
|
67
|
+
|
|
68
|
+
if (key && secret && bucket) {
|
|
69
|
+
return `s3://${key}:${secret}@${bucket}?region=${region}`;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// 3. MCP config file
|
|
75
|
+
() => {
|
|
76
|
+
const mcpConfigPath = join(homedir(), '.config', 'mcp', 'config.json');
|
|
77
|
+
if (existsSync(mcpConfigPath)) {
|
|
78
|
+
try {
|
|
79
|
+
const mcpConfig = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));
|
|
80
|
+
const s3dbConfig = mcpConfig.servers?.s3db;
|
|
81
|
+
if (s3dbConfig?.env?.S3DB_CONNECTION_STRING) {
|
|
82
|
+
return s3dbConfig.env.S3DB_CONNECTION_STRING;
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
// Ignore config parsing errors
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// 4. Local .env file
|
|
92
|
+
() => {
|
|
93
|
+
const envPath = join(process.cwd(), '.env');
|
|
94
|
+
if (existsSync(envPath)) {
|
|
95
|
+
const envContent = readFileSync(envPath, 'utf-8');
|
|
96
|
+
const match = envContent.match(/^S3DB_CONNECTION_STRING=(.*)$/m);
|
|
97
|
+
if (match && match[1]) {
|
|
98
|
+
return match[1].trim().replace(/^["']|["']$/g, ''); // Remove quotes
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const source of sources) {
|
|
106
|
+
const connectionString = source();
|
|
107
|
+
if (connectionString) {
|
|
108
|
+
return connectionString;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Validate connection string format
|
|
116
|
+
function validateConnectionString(connectionString) {
|
|
117
|
+
if (!connectionString) return false;
|
|
118
|
+
|
|
119
|
+
const patterns = [
|
|
120
|
+
/^s3:\/\/[^:]+:[^@]+@[^?]+(\?.*)?$/, // s3://key:secret@bucket?region=...
|
|
121
|
+
/^https?:\/\/[^:]+:[^@]+@[^\/]+\/[^?]+(\?.*)?$/ // http(s)://key:secret@host/bucket?...
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
return patterns.some(pattern => pattern.test(connectionString));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Start MCP server function
|
|
128
|
+
async function startMcpServer(options) {
|
|
129
|
+
try {
|
|
130
|
+
// Import the MCP server
|
|
131
|
+
const { S3dbMCPServer } = await import('../mcp/server.js');
|
|
132
|
+
|
|
133
|
+
// Set environment variables from options
|
|
134
|
+
if (options.transport) process.env.MCP_TRANSPORT = options.transport;
|
|
135
|
+
if (options.host) process.env.MCP_SERVER_HOST = options.host;
|
|
136
|
+
if (options.port) process.env.MCP_SERVER_PORT = options.port.toString();
|
|
137
|
+
if (options.connectionString) process.env.S3DB_CONNECTION_STRING = options.connectionString;
|
|
138
|
+
|
|
139
|
+
// Create and start server
|
|
140
|
+
const server = new S3dbMCPServer();
|
|
141
|
+
|
|
142
|
+
info(`Starting S3DB MCP Server v${packageJson.version}`);
|
|
143
|
+
info(`Transport: ${options.transport}`);
|
|
144
|
+
info(`Host: ${options.host}`);
|
|
145
|
+
info(`Port: ${options.port}`);
|
|
146
|
+
|
|
147
|
+
if (options.connectionString) {
|
|
148
|
+
info(`Connection: ${options.connectionString.replace(/:[^@]+@/, ':***@')}`); // Hide secrets
|
|
149
|
+
} else {
|
|
150
|
+
warn('No connection string provided - server will require manual connection via MCP tools');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle graceful shutdown
|
|
154
|
+
process.on('SIGINT', () => {
|
|
155
|
+
log('\n🛑 Shutting down S3DB MCP Server...', colors.yellow);
|
|
156
|
+
process.exit(0);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
process.on('SIGTERM', () => {
|
|
160
|
+
log('\n🛑 Shutting down S3DB MCP Server...', colors.yellow);
|
|
161
|
+
process.exit(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
success('S3DB MCP Server started successfully!');
|
|
165
|
+
|
|
166
|
+
if (options.transport === 'sse') {
|
|
167
|
+
success(`Server available at: http://${options.host}:${options.port}/sse`);
|
|
168
|
+
success(`Health check: http://${options.host}:${parseInt(options.port) + 1}/health`);
|
|
169
|
+
} else {
|
|
170
|
+
info('Server running in stdio mode for MCP client communication');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
} catch (err) {
|
|
174
|
+
error(`Failed to start MCP server: ${err.message}`);
|
|
175
|
+
if (options.verbose) {
|
|
176
|
+
console.error(err.stack);
|
|
177
|
+
}
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Setup CLI program
|
|
183
|
+
program
|
|
184
|
+
.name('s3db.js')
|
|
185
|
+
.description('S3DB - Use AWS S3 as a database with ORM capabilities and MCP server')
|
|
186
|
+
.version(packageJson.version);
|
|
187
|
+
|
|
188
|
+
// MCP Server command
|
|
189
|
+
program
|
|
190
|
+
.command('mcp')
|
|
191
|
+
.alias('server')
|
|
192
|
+
.description('Start the S3DB MCP (Model Context Protocol) server')
|
|
193
|
+
.option('-p, --port <port>', 'Port for SSE transport (default: 8000)', '8000')
|
|
194
|
+
.option('-h, --host <host>', 'Host address to bind to (default: 0.0.0.0)', '0.0.0.0')
|
|
195
|
+
.option('-t, --transport <type>', 'Transport type: stdio or sse (default: stdio)', 'stdio')
|
|
196
|
+
.option('-c, --connection-string <string>', 'S3DB connection string (auto-detected if not provided)')
|
|
197
|
+
.option('-v, --verbose', 'Enable verbose logging', false)
|
|
198
|
+
.action(async (options) => {
|
|
199
|
+
// Auto-detect connection string if not provided
|
|
200
|
+
let connectionString = options.connectionString;
|
|
201
|
+
|
|
202
|
+
if (!connectionString) {
|
|
203
|
+
info('Auto-detecting connection string...');
|
|
204
|
+
connectionString = detectConnectionString();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (connectionString) {
|
|
208
|
+
if (!validateConnectionString(connectionString)) {
|
|
209
|
+
error('Invalid connection string format');
|
|
210
|
+
error('Expected formats:');
|
|
211
|
+
error(' s3://key:secret@bucket?region=us-east-1');
|
|
212
|
+
error(' http://key:secret@localhost:9000/bucket (MinIO)');
|
|
213
|
+
error(' https://key:secret@host/bucket (other S3-compatible)');
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
success('Connection string detected and validated');
|
|
217
|
+
} else {
|
|
218
|
+
warn('No connection string found. Server will start without auto-connection.');
|
|
219
|
+
warn('You can connect manually using MCP tools or set one of these:');
|
|
220
|
+
warn(' - S3DB_CONNECTION_STRING environment variable');
|
|
221
|
+
warn(' - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET env vars');
|
|
222
|
+
warn(' - ~/.config/mcp/config.json MCP configuration');
|
|
223
|
+
warn(' - .env file in current directory');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const serverOptions = {
|
|
227
|
+
...options,
|
|
228
|
+
port: parseInt(options.port),
|
|
229
|
+
connectionString
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
await startMcpServer(serverOptions);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Connection test command
|
|
236
|
+
program
|
|
237
|
+
.command('test')
|
|
238
|
+
.description('Test S3DB connection and basic operations')
|
|
239
|
+
.option('-c, --connection-string <string>', 'S3DB connection string (auto-detected if not provided)')
|
|
240
|
+
.option('-v, --verbose', 'Enable verbose output', false)
|
|
241
|
+
.action(async (options) => {
|
|
242
|
+
try {
|
|
243
|
+
// Auto-detect connection string if not provided
|
|
244
|
+
let connectionString = options.connectionString;
|
|
245
|
+
|
|
246
|
+
if (!connectionString) {
|
|
247
|
+
info('Auto-detecting connection string...');
|
|
248
|
+
connectionString = detectConnectionString();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (!connectionString) {
|
|
252
|
+
error('No connection string found. Please provide one using:');
|
|
253
|
+
error(' s3db.js test -c "s3://key:secret@bucket?region=us-east-1"');
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!validateConnectionString(connectionString)) {
|
|
258
|
+
error('Invalid connection string format');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
info('Testing S3DB connection...');
|
|
263
|
+
|
|
264
|
+
// Import and test S3DB
|
|
265
|
+
const { S3db } = await import('../dist/s3db.es.js');
|
|
266
|
+
|
|
267
|
+
const database = new S3db({
|
|
268
|
+
connectionString,
|
|
269
|
+
verbose: options.verbose
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
info('Connecting to database...');
|
|
273
|
+
await database.connect();
|
|
274
|
+
success('Connected successfully!');
|
|
275
|
+
|
|
276
|
+
info('Testing basic operations...');
|
|
277
|
+
|
|
278
|
+
// Test resource listing
|
|
279
|
+
const resources = await database.listResources();
|
|
280
|
+
success(`Found ${resources.length} resources`);
|
|
281
|
+
|
|
282
|
+
if (options.verbose && resources.length > 0) {
|
|
283
|
+
console.log('Resources:', resources);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await database.disconnect();
|
|
287
|
+
success('All tests passed!');
|
|
288
|
+
|
|
289
|
+
} catch (err) {
|
|
290
|
+
error(`Connection test failed: ${err.message}`);
|
|
291
|
+
if (options.verbose) {
|
|
292
|
+
console.error(err.stack);
|
|
293
|
+
}
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Config command
|
|
299
|
+
program
|
|
300
|
+
.command('config')
|
|
301
|
+
.description('Display current configuration and auto-detected settings')
|
|
302
|
+
.action(() => {
|
|
303
|
+
info('S3DB Configuration:');
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
log('📦 Package Information:', colors.cyan);
|
|
307
|
+
console.log(` Name: ${packageJson.name}`);
|
|
308
|
+
console.log(` Version: ${packageJson.version}`);
|
|
309
|
+
console.log(` Description: ${packageJson.description}`);
|
|
310
|
+
console.log('');
|
|
311
|
+
|
|
312
|
+
log('🔗 Connection String Detection:', colors.cyan);
|
|
313
|
+
const connectionString = detectConnectionString();
|
|
314
|
+
if (connectionString) {
|
|
315
|
+
success(` Detected: ${connectionString.replace(/:[^@]+@/, ':***@')}`);
|
|
316
|
+
} else {
|
|
317
|
+
warn(' No connection string detected');
|
|
318
|
+
}
|
|
319
|
+
console.log('');
|
|
320
|
+
|
|
321
|
+
log('🌍 Environment Variables:', colors.cyan);
|
|
322
|
+
const envVars = [
|
|
323
|
+
'S3DB_CONNECTION_STRING',
|
|
324
|
+
'AWS_ACCESS_KEY_ID',
|
|
325
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
326
|
+
'AWS_S3_BUCKET',
|
|
327
|
+
'AWS_REGION',
|
|
328
|
+
'MCP_TRANSPORT',
|
|
329
|
+
'MCP_SERVER_HOST',
|
|
330
|
+
'MCP_SERVER_PORT'
|
|
331
|
+
];
|
|
332
|
+
|
|
333
|
+
envVars.forEach(envVar => {
|
|
334
|
+
const value = process.env[envVar];
|
|
335
|
+
if (value) {
|
|
336
|
+
if (envVar.includes('SECRET') || envVar.includes('KEY')) {
|
|
337
|
+
console.log(` ${envVar}: ${'*'.repeat(Math.min(value.length, 8))}`);
|
|
338
|
+
} else {
|
|
339
|
+
console.log(` ${envVar}: ${value}`);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
console.log(` ${envVar}: ${colors.yellow}not set${colors.reset}`);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
console.log('');
|
|
346
|
+
|
|
347
|
+
log('📁 Configuration Files:', colors.cyan);
|
|
348
|
+
const configFiles = [
|
|
349
|
+
join(homedir(), '.config', 'mcp', 'config.json'),
|
|
350
|
+
join(process.cwd(), '.env')
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
configFiles.forEach(configFile => {
|
|
354
|
+
if (existsSync(configFile)) {
|
|
355
|
+
success(` ${configFile}: found`);
|
|
356
|
+
} else {
|
|
357
|
+
console.log(` ${configFile}: ${colors.yellow}not found${colors.reset}`);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Examples command
|
|
363
|
+
program
|
|
364
|
+
.command('examples')
|
|
365
|
+
.description('Show usage examples and common patterns')
|
|
366
|
+
.action(() => {
|
|
367
|
+
log('🚀 S3DB CLI Examples:', colors.bright + colors.cyan);
|
|
368
|
+
console.log('');
|
|
369
|
+
|
|
370
|
+
log('1. Start MCP Server (stdio mode for MCP clients):', colors.green);
|
|
371
|
+
console.log(' s3db.js mcp');
|
|
372
|
+
console.log(' s3db.js server # alias');
|
|
373
|
+
console.log('');
|
|
374
|
+
|
|
375
|
+
log('2. Start MCP Server with SSE transport:', colors.green);
|
|
376
|
+
console.log(' s3db.js mcp --transport sse --port 8888');
|
|
377
|
+
console.log(' s3db.js mcp -t sse -p 8888 # short form');
|
|
378
|
+
console.log('');
|
|
379
|
+
|
|
380
|
+
log('3. Start with explicit connection string:', colors.green);
|
|
381
|
+
console.log(' s3db.js mcp -c "s3://key:secret@bucket?region=us-east-1"');
|
|
382
|
+
console.log('');
|
|
383
|
+
|
|
384
|
+
log('4. Test connection:', colors.green);
|
|
385
|
+
console.log(' s3db.js test');
|
|
386
|
+
console.log(' s3db.js test --verbose');
|
|
387
|
+
console.log(' s3db.js test -c "s3://key:secret@bucket"');
|
|
388
|
+
console.log('');
|
|
389
|
+
|
|
390
|
+
log('5. View configuration:', colors.green);
|
|
391
|
+
console.log(' s3db.js config');
|
|
392
|
+
console.log('');
|
|
393
|
+
|
|
394
|
+
log('💡 Connection String Formats:', colors.yellow);
|
|
395
|
+
console.log(' AWS S3:');
|
|
396
|
+
console.log(' s3://accessKey:secretKey@bucketName?region=us-east-1');
|
|
397
|
+
console.log(' MinIO:');
|
|
398
|
+
console.log(' http://accessKey:secretKey@localhost:9000/bucketName');
|
|
399
|
+
console.log(' DigitalOcean Spaces:');
|
|
400
|
+
console.log(' https://accessKey:secretKey@nyc3.digitaloceanspaces.com/bucketName');
|
|
401
|
+
console.log('');
|
|
402
|
+
|
|
403
|
+
log('🔧 Environment Variables (auto-detected):', colors.yellow);
|
|
404
|
+
console.log(' S3DB_CONNECTION_STRING="s3://key:secret@bucket"');
|
|
405
|
+
console.log(' AWS_ACCESS_KEY_ID=your_access_key');
|
|
406
|
+
console.log(' AWS_SECRET_ACCESS_KEY=your_secret_key');
|
|
407
|
+
console.log(' AWS_S3_BUCKET=your_bucket');
|
|
408
|
+
console.log(' AWS_REGION=us-east-1');
|
|
409
|
+
console.log('');
|
|
410
|
+
|
|
411
|
+
log('📱 Usage with npx:', colors.yellow);
|
|
412
|
+
console.log(' npx s3db.js mcp --port 8888');
|
|
413
|
+
console.log(' npx s3db.js test');
|
|
414
|
+
console.log(' npx s3db.js config');
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Handle unknown commands
|
|
418
|
+
program.on('command:*', () => {
|
|
419
|
+
error(`Unknown command: ${program.args.join(' ')}`);
|
|
420
|
+
error('Use --help to see available commands');
|
|
421
|
+
process.exit(1);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Show help if no arguments provided
|
|
425
|
+
if (process.argv.length <= 2) {
|
|
426
|
+
program.help();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Parse command line arguments
|
|
430
|
+
program.parse();
|
package/dist/s3db.cjs.js
CHANGED
|
@@ -1367,17 +1367,13 @@ class AuditPlugin extends plugin_class_default {
|
|
|
1367
1367
|
const plugin = this;
|
|
1368
1368
|
resource.deleteMany = async function(ids) {
|
|
1369
1369
|
const objectsToDelete = [];
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
objectsToDelete.push({ id });
|
|
1377
|
-
}
|
|
1370
|
+
for (const id of ids) {
|
|
1371
|
+
const [ok, err, fetched] = await try_fn_default(() => resource.get(id));
|
|
1372
|
+
if (ok) {
|
|
1373
|
+
objectsToDelete.push(fetched);
|
|
1374
|
+
} else {
|
|
1375
|
+
objectsToDelete.push({ id });
|
|
1378
1376
|
}
|
|
1379
|
-
} else {
|
|
1380
|
-
objectsToDelete.push(...ids.map((id) => ({ id })));
|
|
1381
1377
|
}
|
|
1382
1378
|
const result = await originalDeleteMany(ids);
|
|
1383
1379
|
for (const oldData of objectsToDelete) {
|
|
@@ -1482,18 +1478,37 @@ class AuditPlugin extends plugin_class_default {
|
|
|
1482
1478
|
async getAuditLogs(options = {}) {
|
|
1483
1479
|
if (!this.auditResource) return [];
|
|
1484
1480
|
const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100, offset = 0 } = options;
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
if (
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1481
|
+
const hasFilters = resourceName || operation || recordId || partition || startDate || endDate;
|
|
1482
|
+
let items = [];
|
|
1483
|
+
if (hasFilters) {
|
|
1484
|
+
const fetchSize = Math.min(1e4, Math.max(1e3, (limit + offset) * 20));
|
|
1485
|
+
const result = await this.auditResource.list({ limit: fetchSize });
|
|
1486
|
+
items = result || [];
|
|
1487
|
+
if (resourceName) {
|
|
1488
|
+
items = items.filter((log) => log.resourceName === resourceName);
|
|
1489
|
+
}
|
|
1490
|
+
if (operation) {
|
|
1491
|
+
items = items.filter((log) => log.operation === operation);
|
|
1492
|
+
}
|
|
1493
|
+
if (recordId) {
|
|
1494
|
+
items = items.filter((log) => log.recordId === recordId);
|
|
1495
|
+
}
|
|
1496
|
+
if (partition) {
|
|
1497
|
+
items = items.filter((log) => log.partition === partition);
|
|
1498
|
+
}
|
|
1499
|
+
if (startDate || endDate) {
|
|
1500
|
+
items = items.filter((log) => {
|
|
1501
|
+
const timestamp = new Date(log.timestamp);
|
|
1502
|
+
if (startDate && timestamp < new Date(startDate)) return false;
|
|
1503
|
+
if (endDate && timestamp > new Date(endDate)) return false;
|
|
1504
|
+
return true;
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
return items.slice(offset, offset + limit);
|
|
1508
|
+
} else {
|
|
1509
|
+
const result = await this.auditResource.page({ size: limit, offset });
|
|
1510
|
+
return result.items || [];
|
|
1511
|
+
}
|
|
1497
1512
|
}
|
|
1498
1513
|
async getRecordHistory(resourceName, recordId) {
|
|
1499
1514
|
return await this.getAuditLogs({ resourceName, recordId });
|
|
@@ -13507,7 +13522,7 @@ class Database extends EventEmitter {
|
|
|
13507
13522
|
this.id = idGenerator(7);
|
|
13508
13523
|
this.version = "1";
|
|
13509
13524
|
this.s3dbVersion = (() => {
|
|
13510
|
-
const [ok, err, version] = try_fn_default(() => true ? "8.
|
|
13525
|
+
const [ok, err, version] = try_fn_default(() => true ? "8.2.0" : "latest");
|
|
13511
13526
|
return ok ? version : "latest";
|
|
13512
13527
|
})();
|
|
13513
13528
|
this.resources = {};
|
|
@@ -13515,7 +13530,8 @@ class Database extends EventEmitter {
|
|
|
13515
13530
|
this.options = options;
|
|
13516
13531
|
this.verbose = options.verbose || false;
|
|
13517
13532
|
this.parallelism = parseInt(options.parallelism + "") || 10;
|
|
13518
|
-
this.plugins =
|
|
13533
|
+
this.plugins = options.plugins || [];
|
|
13534
|
+
this.pluginRegistry = {};
|
|
13519
13535
|
this.pluginList = options.plugins || [];
|
|
13520
13536
|
this.cache = options.cache;
|
|
13521
13537
|
this.passphrase = options.passphrase || "secret";
|
|
@@ -13809,7 +13825,7 @@ class Database extends EventEmitter {
|
|
|
13809
13825
|
await plugin.setup(db);
|
|
13810
13826
|
if (plugin.afterSetup) await plugin.afterSetup();
|
|
13811
13827
|
const pluginName = this._getPluginName(plugin);
|
|
13812
|
-
this.
|
|
13828
|
+
this.pluginRegistry[pluginName] = plugin;
|
|
13813
13829
|
});
|
|
13814
13830
|
await Promise.all(setupProms);
|
|
13815
13831
|
const startProms = plugins.map(async (plugin) => {
|