s3db.js 13.6.0 → 14.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -43
- package/dist/s3db.cjs +72425 -38970
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72177 -38764
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +94 -49
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/opengraph-helper.js +116 -0
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/concerns/state-machine.js +288 -0
- package/src/plugins/api/index.js +180 -41
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/api/utils/template-engine.js +77 -3
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/README.md +126 -126
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -4,6 +4,7 @@ import { flatten } from 'flat';
|
|
|
4
4
|
import isEqual from 'lodash-es/isEqual.js';
|
|
5
5
|
|
|
6
6
|
import { Plugin } from './plugin.class.js';
|
|
7
|
+
import { PluginError } from '../errors.js';
|
|
7
8
|
import tryFn from '../concerns/try-fn.js';
|
|
8
9
|
import { requirePluginDependency } from './concerns/plugin-dependencies.js';
|
|
9
10
|
|
|
@@ -14,13 +15,7 @@ import {
|
|
|
14
15
|
registerCloudDriver,
|
|
15
16
|
BaseCloudDriver
|
|
16
17
|
} from './cloud-inventory/index.js';
|
|
17
|
-
|
|
18
|
-
const DEFAULT_RESOURCES = {
|
|
19
|
-
snapshots: 'plg_cloud_inventory_snapshots',
|
|
20
|
-
versions: 'plg_cloud_inventory_versions',
|
|
21
|
-
changes: 'plg_cloud_inventory_changes',
|
|
22
|
-
clouds: 'plg_cloud_inventory_clouds'
|
|
23
|
-
};
|
|
18
|
+
import { resolveResourceNames } from './concerns/resource-names.js';
|
|
24
19
|
|
|
25
20
|
const DEFAULT_DISCOVERY = {
|
|
26
21
|
concurrency: 3,
|
|
@@ -77,16 +72,34 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
77
72
|
(level, message, meta) => pendingLogs.push({ level, message, meta })
|
|
78
73
|
);
|
|
79
74
|
|
|
75
|
+
this._internalResourceOverrides = options.resourceNames || {};
|
|
76
|
+
this._internalResourceDescriptors = {
|
|
77
|
+
snapshots: {
|
|
78
|
+
defaultName: 'plg_cloud_inventory_snapshots',
|
|
79
|
+
override: this._internalResourceOverrides.snapshots
|
|
80
|
+
},
|
|
81
|
+
versions: {
|
|
82
|
+
defaultName: 'plg_cloud_inventory_versions',
|
|
83
|
+
override: this._internalResourceOverrides.versions
|
|
84
|
+
},
|
|
85
|
+
changes: {
|
|
86
|
+
defaultName: 'plg_cloud_inventory_changes',
|
|
87
|
+
override: this._internalResourceOverrides.changes
|
|
88
|
+
},
|
|
89
|
+
clouds: {
|
|
90
|
+
defaultName: 'plg_cloud_inventory_clouds',
|
|
91
|
+
override: this._internalResourceOverrides.clouds
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
this.internalResourceNames = this._resolveInternalResourceNames();
|
|
95
|
+
|
|
80
96
|
this.config = {
|
|
81
97
|
clouds: normalizedClouds,
|
|
82
98
|
discovery: {
|
|
83
99
|
...DEFAULT_DISCOVERY,
|
|
84
100
|
...(options.discovery || {})
|
|
85
101
|
},
|
|
86
|
-
|
|
87
|
-
...DEFAULT_RESOURCES,
|
|
88
|
-
...(options.resources || {})
|
|
89
|
-
},
|
|
102
|
+
resourceNames: this.internalResourceNames,
|
|
90
103
|
logger: typeof options.logger === 'function' ? options.logger : null,
|
|
91
104
|
verbose: options.verbose === true,
|
|
92
105
|
scheduled: normalizeSchedule(options.scheduled),
|
|
@@ -108,6 +121,7 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
108
121
|
this._resourceHandles = {};
|
|
109
122
|
this._scheduledJobs = [];
|
|
110
123
|
this._cron = null;
|
|
124
|
+
this.resourceNames = this.internalResourceNames;
|
|
111
125
|
|
|
112
126
|
for (const entry of pendingLogs) {
|
|
113
127
|
this._log(entry.level, entry.message, entry.meta);
|
|
@@ -138,6 +152,15 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
138
152
|
await this._destroyDrivers();
|
|
139
153
|
}
|
|
140
154
|
|
|
155
|
+
onNamespaceChanged() {
|
|
156
|
+
this.internalResourceNames = this._resolveInternalResourceNames();
|
|
157
|
+
if (this.config) {
|
|
158
|
+
this.config.resourceNames = this.internalResourceNames;
|
|
159
|
+
}
|
|
160
|
+
this.resourceNames = this.internalResourceNames;
|
|
161
|
+
this._resourceHandles = {};
|
|
162
|
+
}
|
|
163
|
+
|
|
141
164
|
async syncAll(options = {}) {
|
|
142
165
|
const results = [];
|
|
143
166
|
for (const cloud of this.config.clouds) {
|
|
@@ -156,7 +179,14 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
156
179
|
async syncCloud(cloudId, options = {}) {
|
|
157
180
|
const driverEntry = this.cloudDrivers.get(cloudId);
|
|
158
181
|
if (!driverEntry) {
|
|
159
|
-
throw new
|
|
182
|
+
throw new PluginError(`Cloud "${cloudId}" is not registered`, {
|
|
183
|
+
pluginName: 'CloudInventoryPlugin',
|
|
184
|
+
operation: 'syncCloud',
|
|
185
|
+
statusCode: 404,
|
|
186
|
+
retriable: false,
|
|
187
|
+
suggestion: `Register the cloud definition in CloudInventoryPlugin configuration. Available: ${[...this.cloudDrivers.keys()].join(', ') || 'none'}.`,
|
|
188
|
+
cloudId
|
|
189
|
+
});
|
|
160
190
|
}
|
|
161
191
|
|
|
162
192
|
const { driver, definition } = driverEntry;
|
|
@@ -228,136 +258,144 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
228
258
|
}
|
|
229
259
|
};
|
|
230
260
|
|
|
231
|
-
let items;
|
|
232
261
|
try {
|
|
233
|
-
items
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
let items;
|
|
263
|
+
try {
|
|
264
|
+
items = await driver.listResources({
|
|
265
|
+
discovery: this.config.discovery,
|
|
266
|
+
checkpoint: runtimeContext.checkpoint,
|
|
267
|
+
state: runtimeContext.state,
|
|
268
|
+
runtime: runtimeContext,
|
|
269
|
+
...options
|
|
270
|
+
});
|
|
271
|
+
} catch (err) {
|
|
272
|
+
await this._updateCloudSummary(cloudId, {
|
|
273
|
+
status: 'error',
|
|
274
|
+
lastErrorAt: new Date().toISOString(),
|
|
275
|
+
lastError: err.message || 'Driver failure during listResources'
|
|
276
|
+
});
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
249
279
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
280
|
+
let countCreated = 0;
|
|
281
|
+
let countUpdated = 0;
|
|
282
|
+
let countUnchanged = 0;
|
|
283
|
+
let processed = 0;
|
|
284
|
+
let errorDuringRun = null;
|
|
285
|
+
const startMs = Date.now();
|
|
286
|
+
|
|
287
|
+
const processItem = async (rawItem) => {
|
|
288
|
+
const normalized = this._normalizeResource(definition, rawItem);
|
|
289
|
+
if (!normalized) return;
|
|
290
|
+
|
|
291
|
+
const persisted = await this._persistSnapshot(normalized, rawItem);
|
|
292
|
+
processed += 1;
|
|
293
|
+
if (persisted?.status === 'created') countCreated += 1;
|
|
294
|
+
else if (persisted?.status === 'updated') countUpdated += 1;
|
|
295
|
+
else countUnchanged += 1;
|
|
296
|
+
};
|
|
267
297
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
298
|
+
try {
|
|
299
|
+
if (isAsyncIterable(items)) {
|
|
300
|
+
for await (const item of items) {
|
|
301
|
+
await processItem(item);
|
|
302
|
+
}
|
|
303
|
+
} else if (Array.isArray(items)) {
|
|
304
|
+
for (const item of items) {
|
|
305
|
+
await processItem(item);
|
|
306
|
+
}
|
|
307
|
+
} else if (items) {
|
|
308
|
+
await processItem(items);
|
|
276
309
|
}
|
|
277
|
-
}
|
|
278
|
-
|
|
310
|
+
} catch (err) {
|
|
311
|
+
errorDuringRun = err;
|
|
279
312
|
}
|
|
280
|
-
} catch (err) {
|
|
281
|
-
errorDuringRun = err;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const finishedAt = new Date().toISOString();
|
|
285
|
-
const durationMs = Date.now() - startMs;
|
|
286
313
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
314
|
+
const finishedAt = new Date().toISOString();
|
|
315
|
+
const durationMs = Date.now() - startMs;
|
|
316
|
+
|
|
317
|
+
const summaryPatch = {
|
|
318
|
+
status: errorDuringRun ? 'error' : 'idle',
|
|
319
|
+
lastRunAt: startedAt,
|
|
320
|
+
lastRunId: runId,
|
|
321
|
+
lastResult: {
|
|
322
|
+
runId,
|
|
323
|
+
startedAt,
|
|
324
|
+
finishedAt,
|
|
325
|
+
durationMs,
|
|
326
|
+
counts: {
|
|
327
|
+
created: countCreated,
|
|
328
|
+
updated: countUpdated,
|
|
329
|
+
unchanged: countUnchanged
|
|
330
|
+
},
|
|
331
|
+
processed,
|
|
332
|
+
checkpoint: pendingCheckpoint
|
|
300
333
|
},
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
stateUpdatedAt: pendingState !== summaryBefore?.state ? finishedAt : summaryBefore?.stateUpdatedAt,
|
|
312
|
-
progress: null
|
|
313
|
-
};
|
|
334
|
+
totalResources: Math.max(0, (summaryBefore?.totalResources ?? 0) + countCreated),
|
|
335
|
+
totalVersions: Math.max(0, (summaryBefore?.totalVersions ?? 0) + countCreated + countUpdated),
|
|
336
|
+
checkpoint: pendingCheckpoint,
|
|
337
|
+
checkpointUpdatedAt: pendingCheckpoint !== summaryBefore?.checkpoint ? finishedAt : summaryBefore?.checkpointUpdatedAt,
|
|
338
|
+
rateLimit: pendingRateLimit,
|
|
339
|
+
rateLimitUpdatedAt: pendingRateLimit !== summaryBefore?.rateLimit ? finishedAt : summaryBefore?.rateLimitUpdatedAt,
|
|
340
|
+
state: pendingState,
|
|
341
|
+
stateUpdatedAt: pendingState !== summaryBefore?.state ? finishedAt : summaryBefore?.stateUpdatedAt,
|
|
342
|
+
progress: null
|
|
343
|
+
};
|
|
314
344
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
345
|
+
if (errorDuringRun) {
|
|
346
|
+
summaryPatch.lastError = errorDuringRun.message;
|
|
347
|
+
summaryPatch.lastErrorAt = finishedAt;
|
|
348
|
+
} else {
|
|
349
|
+
summaryPatch.lastError = null;
|
|
350
|
+
summaryPatch.lastSuccessAt = finishedAt;
|
|
351
|
+
}
|
|
322
352
|
|
|
323
|
-
|
|
353
|
+
await this._updateCloudSummary(cloudId, summaryPatch);
|
|
324
354
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this._log('warn', 'Failed to release sync lock', { cloudId, error: releaseErr.message });
|
|
329
|
-
}
|
|
355
|
+
if (errorDuringRun) {
|
|
356
|
+
throw errorDuringRun;
|
|
357
|
+
}
|
|
330
358
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
359
|
+
const summary = {
|
|
360
|
+
cloudId,
|
|
361
|
+
driver: definition.driver,
|
|
362
|
+
created: countCreated,
|
|
363
|
+
updated: countUpdated,
|
|
364
|
+
unchanged: countUnchanged,
|
|
365
|
+
processed,
|
|
366
|
+
durationMs
|
|
367
|
+
};
|
|
334
368
|
|
|
335
|
-
|
|
336
|
-
cloudId,
|
|
337
|
-
driver: definition.driver,
|
|
338
|
-
created: countCreated,
|
|
339
|
-
updated: countUpdated,
|
|
340
|
-
unchanged: countUnchanged,
|
|
341
|
-
processed,
|
|
342
|
-
durationMs
|
|
343
|
-
};
|
|
369
|
+
this._log('info', 'Cloud sync finished', summary);
|
|
344
370
|
|
|
345
|
-
|
|
371
|
+
// Auto-export to Terraform if configured
|
|
372
|
+
if (this.config.terraform.enabled && this.config.terraform.autoExport) {
|
|
373
|
+
await this._autoExportTerraform(cloudId);
|
|
374
|
+
}
|
|
346
375
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
376
|
+
return summary;
|
|
377
|
+
} finally {
|
|
378
|
+
try {
|
|
379
|
+
await storage.releaseLock(lock);
|
|
380
|
+
} catch (releaseErr) {
|
|
381
|
+
this._log('warn', 'Failed to release sync lock', {
|
|
382
|
+
cloudId,
|
|
383
|
+
lockName: lock?.name ?? lockKey,
|
|
384
|
+
error: releaseErr.message
|
|
385
|
+
});
|
|
386
|
+
}
|
|
350
387
|
}
|
|
351
|
-
|
|
352
|
-
return summary;
|
|
353
388
|
}
|
|
354
389
|
|
|
355
390
|
_validateConfiguration() {
|
|
356
391
|
if (!Array.isArray(this.config.clouds) || this.config.clouds.length === 0) {
|
|
357
|
-
throw new
|
|
358
|
-
'CloudInventoryPlugin
|
|
359
|
-
|
|
360
|
-
|
|
392
|
+
throw new PluginError('CloudInventoryPlugin requires a "clouds" array in the configuration', {
|
|
393
|
+
pluginName: 'CloudInventoryPlugin',
|
|
394
|
+
operation: 'validateConfiguration',
|
|
395
|
+
statusCode: 400,
|
|
396
|
+
retriable: false,
|
|
397
|
+
suggestion: `Provide at least one cloud definition. Registered drivers: ${listCloudDrivers().join(', ') || 'none'}.`
|
|
398
|
+
});
|
|
361
399
|
}
|
|
362
400
|
|
|
363
401
|
for (const cloud of this.config.clouds) {
|
|
@@ -366,7 +404,15 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
366
404
|
try {
|
|
367
405
|
normalizeSchedule(cloud.scheduled);
|
|
368
406
|
} catch (err) {
|
|
369
|
-
throw new
|
|
407
|
+
throw new PluginError(`Cloud "${cloud.id}" has an invalid scheduled configuration`, {
|
|
408
|
+
pluginName: 'CloudInventoryPlugin',
|
|
409
|
+
operation: 'validateConfiguration',
|
|
410
|
+
statusCode: 400,
|
|
411
|
+
retriable: false,
|
|
412
|
+
suggestion: 'Provide a valid cron expression and timezone when enabling scheduled discovery.',
|
|
413
|
+
cloudId: cloud.id,
|
|
414
|
+
original: err
|
|
415
|
+
});
|
|
370
416
|
}
|
|
371
417
|
}
|
|
372
418
|
}
|
|
@@ -410,7 +456,13 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
410
456
|
// Get snapshots resource
|
|
411
457
|
const snapshotsResource = this._resourceHandles.snapshots;
|
|
412
458
|
if (!snapshotsResource) {
|
|
413
|
-
throw new
|
|
459
|
+
throw new PluginError('Snapshots resource not initialized', {
|
|
460
|
+
pluginName: 'CloudInventoryPlugin',
|
|
461
|
+
operation: 'exportToTerraformState',
|
|
462
|
+
statusCode: 500,
|
|
463
|
+
retriable: false,
|
|
464
|
+
suggestion: 'Call database.usePlugin(new CloudInventoryPlugin(...)) and ensure onInstall completed before exporting.'
|
|
465
|
+
});
|
|
414
466
|
}
|
|
415
467
|
|
|
416
468
|
// Build query filter
|
|
@@ -497,7 +549,13 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
497
549
|
// Get S3 client from database
|
|
498
550
|
const s3Client = this.database.client;
|
|
499
551
|
if (!s3Client || typeof s3Client.putObject !== 'function') {
|
|
500
|
-
throw new
|
|
552
|
+
throw new PluginError('S3 client not available. Database must use S3-compatible storage.', {
|
|
553
|
+
pluginName: 'CloudInventoryPlugin',
|
|
554
|
+
operation: 'exportToTerraformStateToS3',
|
|
555
|
+
statusCode: 500,
|
|
556
|
+
retriable: false,
|
|
557
|
+
suggestion: 'Initialize the database with an S3-compatible client before exporting Terraform state to S3.'
|
|
558
|
+
});
|
|
501
559
|
}
|
|
502
560
|
|
|
503
561
|
// Upload to S3
|
|
@@ -548,14 +606,27 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
548
606
|
// Parse S3 URL: s3://bucket/path/to/file.tfstate
|
|
549
607
|
const s3Match = terraform.output?.match(/^s3:\/\/([^/]+)\/(.+)$/);
|
|
550
608
|
if (!s3Match) {
|
|
551
|
-
throw new
|
|
609
|
+
throw new PluginError(`Invalid S3 URL format: ${terraform.output}`, {
|
|
610
|
+
pluginName: 'CloudInventoryPlugin',
|
|
611
|
+
operation: '_autoExportTerraform',
|
|
612
|
+
statusCode: 400,
|
|
613
|
+
retriable: false,
|
|
614
|
+
suggestion: 'Provide a Terraform export destination using s3://bucket/path/file.tfstate.',
|
|
615
|
+
output: terraform.output
|
|
616
|
+
});
|
|
552
617
|
}
|
|
553
618
|
const [, bucket, key] = s3Match;
|
|
554
619
|
result = await this.exportToTerraformStateToS3(bucket, key, exportOptions);
|
|
555
620
|
} else if (terraform.outputType === 'file') {
|
|
556
621
|
// File path
|
|
557
622
|
if (!terraform.output) {
|
|
558
|
-
throw new
|
|
623
|
+
throw new PluginError('Terraform output path not configured', {
|
|
624
|
+
pluginName: 'CloudInventoryPlugin',
|
|
625
|
+
operation: '_autoExportTerraform',
|
|
626
|
+
statusCode: 400,
|
|
627
|
+
retriable: false,
|
|
628
|
+
suggestion: 'Set terraform.output to a file path (e.g., ./terraform/state.tfstate) when using outputType "file".'
|
|
629
|
+
});
|
|
559
630
|
}
|
|
560
631
|
result = await this.exportToTerraformStateFile(terraform.output, exportOptions);
|
|
561
632
|
} else {
|
|
@@ -564,7 +635,14 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
564
635
|
const stateData = await this.exportToTerraformState(exportOptions);
|
|
565
636
|
result = await terraform.output(stateData);
|
|
566
637
|
} else {
|
|
567
|
-
throw new
|
|
638
|
+
throw new PluginError(`Unknown terraform.outputType: ${terraform.outputType}`, {
|
|
639
|
+
pluginName: 'CloudInventoryPlugin',
|
|
640
|
+
operation: '_autoExportTerraform',
|
|
641
|
+
statusCode: 400,
|
|
642
|
+
retriable: false,
|
|
643
|
+
suggestion: 'Use one of the supported output types: "file", "s3", or provide a custom function.',
|
|
644
|
+
outputType: terraform.outputType
|
|
645
|
+
});
|
|
568
646
|
}
|
|
569
647
|
}
|
|
570
648
|
|
|
@@ -579,12 +657,11 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
579
657
|
}
|
|
580
658
|
|
|
581
659
|
async _ensureResources() {
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
} = this.config.resources;
|
|
660
|
+
const names = this.internalResourceNames;
|
|
661
|
+
const snapshots = names.snapshots;
|
|
662
|
+
const versions = names.versions;
|
|
663
|
+
const changes = names.changes;
|
|
664
|
+
const clouds = names.clouds;
|
|
588
665
|
|
|
589
666
|
const resourceDefinitions = [
|
|
590
667
|
{
|
|
@@ -740,6 +817,13 @@ export class CloudInventoryPlugin extends Plugin {
|
|
|
740
817
|
this._resourceHandles.versions = this.database.resources[versions];
|
|
741
818
|
this._resourceHandles.changes = this.database.resources[changes];
|
|
742
819
|
this._resourceHandles.clouds = this.database.resources[clouds];
|
|
820
|
+
this.resourceNames = this.internalResourceNames;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
_resolveInternalResourceNames() {
|
|
824
|
+
return resolveResourceNames('cloud_inventory', this._internalResourceDescriptors, {
|
|
825
|
+
namespace: this.namespace
|
|
826
|
+
});
|
|
743
827
|
}
|
|
744
828
|
|
|
745
829
|
async _initializeDrivers() {
|
|
@@ -1173,7 +1257,13 @@ function normalizeSchedule(input) {
|
|
|
1173
1257
|
schedule.runOnStart = Boolean(schedule.runOnStart);
|
|
1174
1258
|
|
|
1175
1259
|
if (schedule.enabled && !schedule.cron) {
|
|
1176
|
-
throw new
|
|
1260
|
+
throw new PluginError('Scheduled configuration requires a valid cron expression when enabled is true', {
|
|
1261
|
+
pluginName: 'CloudInventoryPlugin',
|
|
1262
|
+
operation: 'normalizeSchedule',
|
|
1263
|
+
statusCode: 400,
|
|
1264
|
+
retriable: false,
|
|
1265
|
+
suggestion: 'Set scheduled.cron to a valid cron expression (e.g., "0 * * * *") when enabling scheduled discovery.'
|
|
1266
|
+
});
|
|
1177
1267
|
}
|
|
1178
1268
|
|
|
1179
1269
|
return schedule;
|
|
@@ -1207,7 +1297,13 @@ function resolveDriverReference(driverInput, logFn) {
|
|
|
1207
1297
|
return candidate;
|
|
1208
1298
|
}
|
|
1209
1299
|
|
|
1210
|
-
throw new
|
|
1300
|
+
throw new PluginError('Cloud driver must be a string identifier or a factory/class that produces a BaseCloudDriver instance', {
|
|
1301
|
+
pluginName: 'CloudInventoryPlugin',
|
|
1302
|
+
operation: 'resolveDriverReference',
|
|
1303
|
+
statusCode: 400,
|
|
1304
|
+
retriable: false,
|
|
1305
|
+
suggestion: 'Register the driver name via registerCloudDriver() or supply a factory/class returning BaseCloudDriver.'
|
|
1306
|
+
});
|
|
1211
1307
|
}
|
|
1212
1308
|
|
|
1213
1309
|
function instantiateInlineDriver(driverInput, options) {
|
|
@@ -1224,7 +1320,13 @@ function instantiateInlineDriver(driverInput, options) {
|
|
|
1224
1320
|
return result;
|
|
1225
1321
|
}
|
|
1226
1322
|
|
|
1227
|
-
throw new
|
|
1323
|
+
throw new PluginError('Inline driver factory must return an instance of BaseCloudDriver', {
|
|
1324
|
+
pluginName: 'CloudInventoryPlugin',
|
|
1325
|
+
operation: 'instantiateInlineDriver',
|
|
1326
|
+
statusCode: 500,
|
|
1327
|
+
retriable: false,
|
|
1328
|
+
suggestion: 'Ensure the inline driver function returns a BaseCloudDriver instance or class.'
|
|
1329
|
+
});
|
|
1228
1330
|
}
|
|
1229
1331
|
|
|
1230
1332
|
function isSubclassOfBase(fn) {
|
|
@@ -175,6 +175,56 @@ export const PLUGIN_DEPENDENCIES = {
|
|
|
175
175
|
npmUrl: 'https://www.npmjs.com/package/@tensorflow/tfjs-node'
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
+
},
|
|
179
|
+
'puppeteer': {
|
|
180
|
+
name: 'Puppeteer Suite',
|
|
181
|
+
docsUrl: 'https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/puppeteer/README.md',
|
|
182
|
+
dependencies: {
|
|
183
|
+
'puppeteer-extra': {
|
|
184
|
+
version: '^3.3.4',
|
|
185
|
+
description: 'Headless Chrome automation toolkit',
|
|
186
|
+
installCommand: 'pnpm add puppeteer-extra',
|
|
187
|
+
npmUrl: 'https://www.npmjs.com/package/puppeteer-extra'
|
|
188
|
+
},
|
|
189
|
+
'puppeteer-extra-plugin-stealth': {
|
|
190
|
+
version: '^2.11.2',
|
|
191
|
+
description: 'Stealth plugin to evade bot detection',
|
|
192
|
+
installCommand: 'pnpm add puppeteer-extra-plugin-stealth',
|
|
193
|
+
npmUrl: 'https://www.npmjs.com/package/puppeteer-extra-plugin-stealth'
|
|
194
|
+
},
|
|
195
|
+
'user-agents': {
|
|
196
|
+
version: '^2.0.0',
|
|
197
|
+
description: 'Randomized user agent generator',
|
|
198
|
+
installCommand: 'pnpm add user-agents',
|
|
199
|
+
npmUrl: 'https://www.npmjs.com/package/user-agents'
|
|
200
|
+
},
|
|
201
|
+
'ghost-cursor': {
|
|
202
|
+
version: '^1.4.1',
|
|
203
|
+
description: 'Human-like mouse movement generator',
|
|
204
|
+
installCommand: 'pnpm add ghost-cursor',
|
|
205
|
+
npmUrl: 'https://www.npmjs.com/package/ghost-cursor'
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
'puppeteer-extra': {
|
|
210
|
+
name: 'puppeteer-extra',
|
|
211
|
+
docsUrl: 'https://github.com/berstend/puppeteer-extra',
|
|
212
|
+
dependencies: {}
|
|
213
|
+
},
|
|
214
|
+
'puppeteer-extra-plugin-stealth': {
|
|
215
|
+
name: 'puppeteer-extra-plugin-stealth',
|
|
216
|
+
docsUrl: 'https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth',
|
|
217
|
+
dependencies: {}
|
|
218
|
+
},
|
|
219
|
+
'user-agents': {
|
|
220
|
+
name: 'user-agents',
|
|
221
|
+
docsUrl: 'https://github.com/intoli/user-agents',
|
|
222
|
+
dependencies: {}
|
|
223
|
+
},
|
|
224
|
+
'ghost-cursor': {
|
|
225
|
+
name: 'ghost-cursor',
|
|
226
|
+
docsUrl: 'https://github.com/Xetera/ghost-cursor',
|
|
227
|
+
dependencies: {}
|
|
178
228
|
}
|
|
179
229
|
};
|
|
180
230
|
|
|
@@ -270,6 +320,10 @@ export async function requirePluginDependency(pluginId, options = {}) {
|
|
|
270
320
|
return { valid: false, missing: [], incompatible: [], messages: [error.message] };
|
|
271
321
|
}
|
|
272
322
|
|
|
323
|
+
if (process?.env?.S3DB_SKIP_PLUGIN_DEP_CHECK === '1') {
|
|
324
|
+
return { valid: true, missing: [], incompatible: [], messages: [] };
|
|
325
|
+
}
|
|
326
|
+
|
|
273
327
|
const missing = [];
|
|
274
328
|
const incompatible = [];
|
|
275
329
|
const messages = [];
|