s3db.js 13.3.0 → 13.4.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/README.md +34 -10
- package/dist/s3db.cjs.js +102 -23
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +102 -23
- package/dist/s3db.es.js.map +1 -1
- package/package.json +15 -2
- package/src/plugins/audit.plugin.js +427 -0
- package/src/plugins/costs.plugin.js +524 -0
- package/src/plugins/fulltext.plugin.js +484 -0
- package/src/plugins/metrics.plugin.js +575 -0
- package/src/plugins/queue-consumer.plugin.js +607 -19
- package/src/plugins/state-machine.plugin.js +132 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s3db.js",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.4.0",
|
|
4
4
|
"description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
|
|
5
5
|
"main": "dist/s3db.cjs.js",
|
|
6
6
|
"module": "dist/s3db.es.js",
|
|
@@ -88,6 +88,8 @@
|
|
|
88
88
|
"@google-cloud/bigquery": "^7.0.0",
|
|
89
89
|
"@hono/node-server": "^1.0.0",
|
|
90
90
|
"@hono/swagger-ui": "^0.5.0",
|
|
91
|
+
"@libsql/client": "^0.14.0",
|
|
92
|
+
"@planetscale/database": "^1.0.0",
|
|
91
93
|
"@tensorflow/tfjs-node": "^4.0.0",
|
|
92
94
|
"amqplib": "^0.10.8",
|
|
93
95
|
"hono": "^4.0.0",
|
|
@@ -107,6 +109,12 @@
|
|
|
107
109
|
"@hono/swagger-ui": {
|
|
108
110
|
"optional": true
|
|
109
111
|
},
|
|
112
|
+
"@libsql/client": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"@planetscale/database": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
110
118
|
"@tensorflow/tfjs-node": {
|
|
111
119
|
"optional": true
|
|
112
120
|
},
|
|
@@ -128,6 +136,10 @@
|
|
|
128
136
|
"@babel/core": "^7.28.4",
|
|
129
137
|
"@babel/preset-env": "^7.28.3",
|
|
130
138
|
"@google-cloud/bigquery": "^7.9.4",
|
|
139
|
+
"@hono/node-server": "^1.13.7",
|
|
140
|
+
"@hono/swagger-ui": "^0.5.3",
|
|
141
|
+
"@libsql/client": "^0.14.0",
|
|
142
|
+
"@planetscale/database": "^1.19.0",
|
|
131
143
|
"@rollup/plugin-commonjs": "^28.0.8",
|
|
132
144
|
"@rollup/plugin-json": "^6.1.0",
|
|
133
145
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
@@ -143,6 +155,7 @@
|
|
|
143
155
|
"cli-table3": "^0.6.5",
|
|
144
156
|
"commander": "^14.0.1",
|
|
145
157
|
"esbuild": "^0.25.11",
|
|
158
|
+
"hono": "^4.7.11",
|
|
146
159
|
"inquirer": "^12.10.0",
|
|
147
160
|
"jest": "^30.2.0",
|
|
148
161
|
"node-cron": "^4.2.1",
|
|
@@ -185,7 +198,7 @@
|
|
|
185
198
|
"validate:types": "pnpm run test:ts && echo 'TypeScript definitions are valid!'",
|
|
186
199
|
"test:ts:runtime": "tsx tests/typescript/types-runtime-simple.ts",
|
|
187
200
|
"test:mcp": "node mcp/entrypoint.js --help",
|
|
188
|
-
"install:peers": "pnpm add -D @aws-sdk/client-sqs @google-cloud/bigquery amqplib node-cron pg",
|
|
201
|
+
"install:peers": "pnpm add -D @aws-sdk/client-sqs @google-cloud/bigquery @hono/node-server @hono/swagger-ui @libsql/client @planetscale/database @tensorflow/tfjs-node amqplib hono node-cron pg",
|
|
189
202
|
"install:peers:script": "./scripts/install-peer-deps.sh"
|
|
190
203
|
}
|
|
191
204
|
}
|
|
@@ -1,3 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # AuditPlugin - Comprehensive Audit Trail for s3db.js
|
|
3
|
+
*
|
|
4
|
+
* ## Overview
|
|
5
|
+
*
|
|
6
|
+
* The AuditPlugin automatically tracks all changes (insert, update, delete) to your resources,
|
|
7
|
+
* creating a complete audit trail for compliance, debugging, and historical analysis.
|
|
8
|
+
*
|
|
9
|
+
* ## Features
|
|
10
|
+
*
|
|
11
|
+
* 1. **Automatic Change Tracking** - Captures all insert/update/delete operations
|
|
12
|
+
* 2. **Partition-Aware** - Efficient queries using date and resource partitions
|
|
13
|
+
* 3. **Configurable Data Inclusion** - Control whether to store full data or just metadata
|
|
14
|
+
* 4. **Data Truncation** - Automatically truncates large records to prevent storage issues
|
|
15
|
+
* 5. **Flexible Querying** - Filter by resource, operation, record ID, partition, or date range
|
|
16
|
+
* 6. **Statistics & Analytics** - Built-in aggregation methods for audit analysis
|
|
17
|
+
* 7. **Retention Management** - Automatic cleanup of old audit logs
|
|
18
|
+
*
|
|
19
|
+
* ## Configuration
|
|
20
|
+
*
|
|
21
|
+
* ```javascript
|
|
22
|
+
* import { Database } from 's3db.js';
|
|
23
|
+
* import { AuditPlugin } from 's3db.js/plugins/audit';
|
|
24
|
+
*
|
|
25
|
+
* // Basic configuration
|
|
26
|
+
* const db = new Database({
|
|
27
|
+
* connectionString: 's3://bucket/db'
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* await db.use(new AuditPlugin({
|
|
31
|
+
* includeData: true, // Store before/after data (default: true)
|
|
32
|
+
* includePartitions: true, // Track partition information (default: true)
|
|
33
|
+
* maxDataSize: 10000 // Max bytes for data field (default: 10000)
|
|
34
|
+
* }));
|
|
35
|
+
*
|
|
36
|
+
* // Minimal configuration (metadata only, faster)
|
|
37
|
+
* await db.use(new AuditPlugin({
|
|
38
|
+
* includeData: false, // Don't store data, only operation metadata
|
|
39
|
+
* includePartitions: false // Don't track partitions
|
|
40
|
+
* }));
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ## Usage Examples
|
|
44
|
+
*
|
|
45
|
+
* ### Basic Audit Trail
|
|
46
|
+
*
|
|
47
|
+
* ```javascript
|
|
48
|
+
* const users = await db.createResource({
|
|
49
|
+
* name: 'users',
|
|
50
|
+
* attributes: {
|
|
51
|
+
* email: 'string|required',
|
|
52
|
+
* name: 'string'
|
|
53
|
+
* }
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // These operations are automatically audited
|
|
57
|
+
* await users.insert({ id: 'u1', email: 'john@example.com', name: 'John' });
|
|
58
|
+
* await users.update('u1', { name: 'John Doe' });
|
|
59
|
+
* await users.delete('u1');
|
|
60
|
+
*
|
|
61
|
+
* // Query audit logs
|
|
62
|
+
* const auditPlugin = db.plugins.AuditPlugin;
|
|
63
|
+
* const logs = await auditPlugin.getAuditLogs({
|
|
64
|
+
* resourceName: 'users',
|
|
65
|
+
* recordId: 'u1'
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* console.log(logs);
|
|
69
|
+
* // [
|
|
70
|
+
* // { operation: 'insert', recordId: 'u1', newData: '{"id":"u1",...}', timestamp: '...' },
|
|
71
|
+
* // { operation: 'update', recordId: 'u1', oldData: '...', newData: '...', timestamp: '...' },
|
|
72
|
+
* // { operation: 'delete', recordId: 'u1', oldData: '...', timestamp: '...' }
|
|
73
|
+
* // ]
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* ### Querying Audit Logs
|
|
77
|
+
*
|
|
78
|
+
* ```javascript
|
|
79
|
+
* const auditPlugin = db.plugins.AuditPlugin;
|
|
80
|
+
*
|
|
81
|
+
* // Get all changes to a specific resource
|
|
82
|
+
* const userChanges = await auditPlugin.getAuditLogs({
|
|
83
|
+
* resourceName: 'users',
|
|
84
|
+
* limit: 100
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* // Get changes by operation type
|
|
88
|
+
* const deletions = await auditPlugin.getAuditLogs({
|
|
89
|
+
* resourceName: 'users',
|
|
90
|
+
* operation: 'delete'
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* // Get changes in a date range
|
|
94
|
+
* const recentChanges = await auditPlugin.getAuditLogs({
|
|
95
|
+
* startDate: '2025-01-01',
|
|
96
|
+
* endDate: '2025-01-31'
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* // Get specific record history
|
|
100
|
+
* const recordHistory = await auditPlugin.getRecordHistory('users', 'u1');
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* ### Audit Statistics
|
|
104
|
+
*
|
|
105
|
+
* ```javascript
|
|
106
|
+
* // Get comprehensive statistics
|
|
107
|
+
* const stats = await auditPlugin.getAuditStats({
|
|
108
|
+
* resourceName: 'users',
|
|
109
|
+
* startDate: '2025-01-01'
|
|
110
|
+
* });
|
|
111
|
+
*
|
|
112
|
+
* console.log(stats);
|
|
113
|
+
* // {
|
|
114
|
+
* // total: 1523,
|
|
115
|
+
* // byOperation: { insert: 500, update: 1000, delete: 23 },
|
|
116
|
+
* // byResource: { users: 1523 },
|
|
117
|
+
* // byUser: { system: 1200, 'user@example.com': 323 },
|
|
118
|
+
* // timeline: { '2025-01-01': 45, '2025-01-02': 67, ... }
|
|
119
|
+
* // }
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* ### Partition History
|
|
123
|
+
*
|
|
124
|
+
* ```javascript
|
|
125
|
+
* const orders = await db.createResource({
|
|
126
|
+
* name: 'orders',
|
|
127
|
+
* attributes: { region: 'string', amount: 'number' },
|
|
128
|
+
* partitions: {
|
|
129
|
+
* byRegion: { fields: { region: 'string' } }
|
|
130
|
+
* }
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* // Get audit trail for a specific partition
|
|
134
|
+
* const partitionLogs = await auditPlugin.getPartitionHistory(
|
|
135
|
+
* 'orders',
|
|
136
|
+
* 'byRegion',
|
|
137
|
+
* { region: 'US' }
|
|
138
|
+
* );
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* ### Cleanup Old Audit Logs
|
|
142
|
+
*
|
|
143
|
+
* ```javascript
|
|
144
|
+
* // Delete audit logs older than 90 days (default)
|
|
145
|
+
* const deletedCount = await auditPlugin.cleanupOldAudits(90);
|
|
146
|
+
* console.log(`Deleted ${deletedCount} old audit logs`);
|
|
147
|
+
*
|
|
148
|
+
* // Custom retention period (30 days)
|
|
149
|
+
* await auditPlugin.cleanupOldAudits(30);
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* ## Best Practices
|
|
153
|
+
*
|
|
154
|
+
* ### 1. Configure Data Inclusion Based on Needs
|
|
155
|
+
*
|
|
156
|
+
* ```javascript
|
|
157
|
+
* // For compliance (full audit trail)
|
|
158
|
+
* new AuditPlugin({
|
|
159
|
+
* includeData: true,
|
|
160
|
+
* includePartitions: true,
|
|
161
|
+
* maxDataSize: 50000 // Large limit for complete data
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* // For performance monitoring (metadata only)
|
|
165
|
+
* new AuditPlugin({
|
|
166
|
+
* includeData: false,
|
|
167
|
+
* includePartitions: false
|
|
168
|
+
* });
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* ### 2. Use Partition-Aware Queries for Performance
|
|
172
|
+
*
|
|
173
|
+
* ```javascript
|
|
174
|
+
* // FAST: Query by resource (uses partition)
|
|
175
|
+
* await auditPlugin.getAuditLogs({ resourceName: 'users' });
|
|
176
|
+
*
|
|
177
|
+
* // FAST: Query by date (uses partition)
|
|
178
|
+
* await auditPlugin.getAuditLogs({ startDate: '2025-01-15', endDate: '2025-01-16' });
|
|
179
|
+
*
|
|
180
|
+
* // SLOWER: Multiple filters (requires list scan)
|
|
181
|
+
* await auditPlugin.getAuditLogs({
|
|
182
|
+
* resourceName: 'users',
|
|
183
|
+
* operation: 'update',
|
|
184
|
+
* recordId: 'u1'
|
|
185
|
+
* });
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* ### 3. Implement Regular Cleanup
|
|
189
|
+
*
|
|
190
|
+
* ```javascript
|
|
191
|
+
* // Schedule monthly cleanup (using cron or scheduler)
|
|
192
|
+
* setInterval(async () => {
|
|
193
|
+
* const deleted = await auditPlugin.cleanupOldAudits(90);
|
|
194
|
+
* console.log(`Audit cleanup: removed ${deleted} records`);
|
|
195
|
+
* }, 30 * 24 * 60 * 60 * 1000); // 30 days
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* ### 4. Track User Context
|
|
199
|
+
*
|
|
200
|
+
* ```javascript
|
|
201
|
+
* // Set current user for audit trails
|
|
202
|
+
* auditPlugin.getCurrentUserId = () => {
|
|
203
|
+
* // Return current user ID from your auth system
|
|
204
|
+
* return getCurrentUser()?.email || 'system';
|
|
205
|
+
* };
|
|
206
|
+
*
|
|
207
|
+
* // Now all audit logs will include the user ID
|
|
208
|
+
* await users.insert({ email: 'jane@example.com' });
|
|
209
|
+
* // Audit log will show: userId: 'admin@example.com'
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* ## Performance Considerations
|
|
213
|
+
*
|
|
214
|
+
* ### Storage Overhead
|
|
215
|
+
*
|
|
216
|
+
* - **With includeData: true** - Approximately 2-3x storage per operation
|
|
217
|
+
* - **With includeData: false** - Approximately 200-500 bytes per operation
|
|
218
|
+
* - Large records are automatically truncated based on `maxDataSize`
|
|
219
|
+
*
|
|
220
|
+
* ### Query Performance
|
|
221
|
+
*
|
|
222
|
+
* | Query Type | Performance | Notes |
|
|
223
|
+
* |------------|-------------|-------|
|
|
224
|
+
* | By resource name | **O(n)** where n = records in resource | Uses `byResource` partition |
|
|
225
|
+
* | By date range | **O(n)** where n = records in date range | Uses `byDate` partition |
|
|
226
|
+
* | By operation | **O(n)** of all records | Requires full scan |
|
|
227
|
+
* | By record ID | **O(n)** of all records | Requires full scan |
|
|
228
|
+
* | Combined filters | **O(n)** of all records | Fetches up to 10,000 records |
|
|
229
|
+
*
|
|
230
|
+
* ### Optimization Tips
|
|
231
|
+
*
|
|
232
|
+
* ```javascript
|
|
233
|
+
* // 1. Use partition-aware queries when possible
|
|
234
|
+
* const logs = await auditPlugin.getAuditLogs({ resourceName: 'users' });
|
|
235
|
+
*
|
|
236
|
+
* // 2. Limit result sets
|
|
237
|
+
* const recent = await auditPlugin.getAuditLogs({
|
|
238
|
+
* resourceName: 'users',
|
|
239
|
+
* limit: 50
|
|
240
|
+
* });
|
|
241
|
+
*
|
|
242
|
+
* // 3. Use narrow date ranges
|
|
243
|
+
* const dailyLogs = await auditPlugin.getAuditLogs({
|
|
244
|
+
* startDate: '2025-01-15',
|
|
245
|
+
* endDate: '2025-01-15' // Single day
|
|
246
|
+
* });
|
|
247
|
+
*
|
|
248
|
+
* // 4. Disable data inclusion for high-volume resources
|
|
249
|
+
* new AuditPlugin({ includeData: false });
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* ## Troubleshooting
|
|
253
|
+
*
|
|
254
|
+
* ### Audit Logs Not Being Created
|
|
255
|
+
*
|
|
256
|
+
* ```javascript
|
|
257
|
+
* // Check if plugin is installed
|
|
258
|
+
* console.log(db.plugins.AuditPlugin); // Should exist
|
|
259
|
+
*
|
|
260
|
+
* // Check if audit resource exists
|
|
261
|
+
* console.log(db.resources.plg_audits); // Should exist
|
|
262
|
+
*
|
|
263
|
+
* // Verify plugin started
|
|
264
|
+
* await db.start(); // Must call start() to activate plugin
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* ### Large Audit Logs Slow Queries
|
|
268
|
+
*
|
|
269
|
+
* ```javascript
|
|
270
|
+
* // Solution 1: Reduce data inclusion
|
|
271
|
+
* new AuditPlugin({ includeData: false });
|
|
272
|
+
*
|
|
273
|
+
* // Solution 2: Implement regular cleanup
|
|
274
|
+
* await auditPlugin.cleanupOldAudits(30); // Keep only 30 days
|
|
275
|
+
*
|
|
276
|
+
* // Solution 3: Use more specific queries
|
|
277
|
+
* await auditPlugin.getAuditLogs({
|
|
278
|
+
* resourceName: 'users', // Use partition
|
|
279
|
+
* limit: 100 // Limit results
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* ### Data Truncation Issues
|
|
284
|
+
*
|
|
285
|
+
* ```javascript
|
|
286
|
+
* // Check if records are being truncated
|
|
287
|
+
* const logs = await auditPlugin.getAuditLogs({ resourceName: 'users' });
|
|
288
|
+
* const truncated = logs.filter(log => {
|
|
289
|
+
* const data = JSON.parse(log.newData || '{}');
|
|
290
|
+
* return data._truncated === true;
|
|
291
|
+
* });
|
|
292
|
+
*
|
|
293
|
+
* // Increase max size if needed
|
|
294
|
+
* new AuditPlugin({ maxDataSize: 50000 }); // Increase from default 10000
|
|
295
|
+
* ```
|
|
296
|
+
*
|
|
297
|
+
* ### Memory Usage with Large History
|
|
298
|
+
*
|
|
299
|
+
* ```javascript
|
|
300
|
+
* // Instead of loading all at once
|
|
301
|
+
* const all = await auditPlugin.getAuditLogs({ resourceName: 'users' });
|
|
302
|
+
*
|
|
303
|
+
* // Use pagination
|
|
304
|
+
* for (let offset = 0; offset < totalRecords; offset += 100) {
|
|
305
|
+
* const batch = await auditPlugin.getAuditLogs({
|
|
306
|
+
* resourceName: 'users',
|
|
307
|
+
* limit: 100,
|
|
308
|
+
* offset
|
|
309
|
+
* });
|
|
310
|
+
* processBatch(batch);
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
313
|
+
*
|
|
314
|
+
* ## Audit Log Schema
|
|
315
|
+
*
|
|
316
|
+
* ```javascript
|
|
317
|
+
* {
|
|
318
|
+
* id: 'audit-1234567890-abc', // Unique audit log ID
|
|
319
|
+
* resourceName: 'users', // Resource that was modified
|
|
320
|
+
* operation: 'update', // 'insert' | 'update' | 'delete' | 'deleteMany'
|
|
321
|
+
* recordId: 'u1', // ID of the modified record
|
|
322
|
+
* userId: 'admin@example.com', // User who made the change
|
|
323
|
+
* timestamp: '2025-01-15T10:30:00Z', // When the change occurred
|
|
324
|
+
* createdAt: '2025-01-15', // Date for partitioning (YYYY-MM-DD)
|
|
325
|
+
* oldData: '{"id":"u1","name":"John"}', // Data before change (JSON string)
|
|
326
|
+
* newData: '{"id":"u1","name":"Jane"}', // Data after change (JSON string)
|
|
327
|
+
* partition: 'byRegion', // Partition name (if applicable)
|
|
328
|
+
* partitionValues: '{"region":"US"}', // Partition values (JSON string)
|
|
329
|
+
* metadata: '{"source":"audit-plugin"}', // Additional metadata
|
|
330
|
+
* }
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* ## Real-World Use Cases
|
|
334
|
+
*
|
|
335
|
+
* ### 1. Compliance & Regulatory Requirements
|
|
336
|
+
*
|
|
337
|
+
* ```javascript
|
|
338
|
+
* // HIPAA, SOC2, GDPR compliance
|
|
339
|
+
* const auditPlugin = new AuditPlugin({
|
|
340
|
+
* includeData: true, // Full audit trail required
|
|
341
|
+
* includePartitions: true,
|
|
342
|
+
* maxDataSize: 100000 // Large records
|
|
343
|
+
* });
|
|
344
|
+
*
|
|
345
|
+
* // Generate compliance report
|
|
346
|
+
* const report = await auditPlugin.getAuditStats({
|
|
347
|
+
* startDate: '2025-01-01',
|
|
348
|
+
* endDate: '2025-12-31'
|
|
349
|
+
* });
|
|
350
|
+
* ```
|
|
351
|
+
*
|
|
352
|
+
* ### 2. Debugging & Troubleshooting
|
|
353
|
+
*
|
|
354
|
+
* ```javascript
|
|
355
|
+
* // Find when and who changed a specific record
|
|
356
|
+
* const history = await auditPlugin.getRecordHistory('orders', 'order-123');
|
|
357
|
+
* console.log(history.map(log => ({
|
|
358
|
+
* timestamp: log.timestamp,
|
|
359
|
+
* user: log.userId,
|
|
360
|
+
* operation: log.operation,
|
|
361
|
+
* before: JSON.parse(log.oldData || '{}'),
|
|
362
|
+
* after: JSON.parse(log.newData || '{}')
|
|
363
|
+
* })));
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* ### 3. Activity Monitoring
|
|
367
|
+
*
|
|
368
|
+
* ```javascript
|
|
369
|
+
* // Real-time activity dashboard
|
|
370
|
+
* setInterval(async () => {
|
|
371
|
+
* const recentActivity = await auditPlugin.getAuditLogs({
|
|
372
|
+
* startDate: new Date(Date.now() - 60000).toISOString(), // Last minute
|
|
373
|
+
* limit: 100
|
|
374
|
+
* });
|
|
375
|
+
*
|
|
376
|
+
* updateDashboard(recentActivity);
|
|
377
|
+
* }, 10000); // Update every 10 seconds
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* ### 4. Data Recovery
|
|
381
|
+
*
|
|
382
|
+
* ```javascript
|
|
383
|
+
* // Recover accidentally deleted record
|
|
384
|
+
* const deletedLog = await auditPlugin.getAuditLogs({
|
|
385
|
+
* resourceName: 'users',
|
|
386
|
+
* operation: 'delete',
|
|
387
|
+
* recordId: 'u1'
|
|
388
|
+
* });
|
|
389
|
+
*
|
|
390
|
+
* if (deletedLog.length > 0) {
|
|
391
|
+
* const originalData = JSON.parse(deletedLog[0].oldData);
|
|
392
|
+
* await users.insert(originalData); // Restore
|
|
393
|
+
* }
|
|
394
|
+
* ```
|
|
395
|
+
*
|
|
396
|
+
* ## API Reference
|
|
397
|
+
*
|
|
398
|
+
* ### Constructor Options
|
|
399
|
+
*
|
|
400
|
+
* - `includeData` (boolean, default: true) - Store before/after data in audit logs
|
|
401
|
+
* - `includePartitions` (boolean, default: true) - Track partition information
|
|
402
|
+
* - `maxDataSize` (number, default: 10000) - Maximum bytes for data field
|
|
403
|
+
*
|
|
404
|
+
* ### Methods
|
|
405
|
+
*
|
|
406
|
+
* - `getAuditLogs(options)` - Query audit logs with filters
|
|
407
|
+
* - `getRecordHistory(resourceName, recordId)` - Get complete history of a record
|
|
408
|
+
* - `getPartitionHistory(resourceName, partition, values)` - Get partition-specific history
|
|
409
|
+
* - `getAuditStats(options)` - Get aggregated statistics
|
|
410
|
+
* - `cleanupOldAudits(retentionDays)` - Delete old audit logs
|
|
411
|
+
*
|
|
412
|
+
* ### Query Options
|
|
413
|
+
*
|
|
414
|
+
* ```typescript
|
|
415
|
+
* interface AuditQueryOptions {
|
|
416
|
+
* resourceName?: string; // Filter by resource
|
|
417
|
+
* operation?: string; // Filter by operation ('insert' | 'update' | 'delete')
|
|
418
|
+
* recordId?: string; // Filter by record ID
|
|
419
|
+
* partition?: string; // Filter by partition name
|
|
420
|
+
* startDate?: string; // Filter by start date (ISO format)
|
|
421
|
+
* endDate?: string; // Filter by end date (ISO format)
|
|
422
|
+
* limit?: number; // Max results (default: 100)
|
|
423
|
+
* offset?: number; // Pagination offset (default: 0)
|
|
424
|
+
* }
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
|
|
1
428
|
import { Plugin } from "./plugin.class.js";
|
|
2
429
|
import tryFn from "../concerns/try-fn.js";
|
|
3
430
|
|