s3db.js 13.4.0 → 13.6.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.
Files changed (110) hide show
  1. package/README.md +25 -10
  2. package/dist/{s3db.cjs.js → s3db.cjs} +38801 -32446
  3. package/dist/s3db.cjs.map +1 -0
  4. package/dist/s3db.es.js +38653 -32291
  5. package/dist/s3db.es.js.map +1 -1
  6. package/package.json +218 -22
  7. package/src/concerns/id.js +90 -6
  8. package/src/concerns/index.js +2 -1
  9. package/src/concerns/password-hashing.js +150 -0
  10. package/src/database.class.js +6 -2
  11. package/src/plugins/api/auth/basic-auth.js +40 -10
  12. package/src/plugins/api/auth/index.js +49 -3
  13. package/src/plugins/api/auth/oauth2-auth.js +171 -0
  14. package/src/plugins/api/auth/oidc-auth.js +789 -0
  15. package/src/plugins/api/auth/oidc-client.js +462 -0
  16. package/src/plugins/api/auth/path-auth-matcher.js +284 -0
  17. package/src/plugins/api/concerns/event-emitter.js +134 -0
  18. package/src/plugins/api/concerns/failban-manager.js +651 -0
  19. package/src/plugins/api/concerns/guards-helpers.js +402 -0
  20. package/src/plugins/api/concerns/metrics-collector.js +346 -0
  21. package/src/plugins/api/index.js +510 -57
  22. package/src/plugins/api/middlewares/failban.js +305 -0
  23. package/src/plugins/api/middlewares/rate-limit.js +301 -0
  24. package/src/plugins/api/middlewares/request-id.js +74 -0
  25. package/src/plugins/api/middlewares/security-headers.js +120 -0
  26. package/src/plugins/api/middlewares/session-tracking.js +194 -0
  27. package/src/plugins/api/routes/auth-routes.js +119 -78
  28. package/src/plugins/api/routes/resource-routes.js +73 -30
  29. package/src/plugins/api/server.js +1139 -45
  30. package/src/plugins/api/utils/custom-routes.js +102 -0
  31. package/src/plugins/api/utils/guards.js +213 -0
  32. package/src/plugins/api/utils/mime-types.js +154 -0
  33. package/src/plugins/api/utils/openapi-generator.js +91 -12
  34. package/src/plugins/api/utils/path-matcher.js +173 -0
  35. package/src/plugins/api/utils/static-filesystem.js +262 -0
  36. package/src/plugins/api/utils/static-s3.js +231 -0
  37. package/src/plugins/api/utils/template-engine.js +188 -0
  38. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
  39. package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
  40. package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
  41. package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
  42. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
  43. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
  44. package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
  45. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
  46. package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
  47. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
  48. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
  49. package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
  50. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
  51. package/src/plugins/cloud-inventory/index.js +20 -0
  52. package/src/plugins/cloud-inventory/registry.js +146 -0
  53. package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
  54. package/src/plugins/cloud-inventory.plugin.js +1333 -0
  55. package/src/plugins/concerns/plugin-dependencies.js +62 -2
  56. package/src/plugins/eventual-consistency/analytics.js +1 -0
  57. package/src/plugins/eventual-consistency/consolidation.js +2 -2
  58. package/src/plugins/eventual-consistency/garbage-collection.js +2 -2
  59. package/src/plugins/eventual-consistency/install.js +2 -2
  60. package/src/plugins/identity/README.md +335 -0
  61. package/src/plugins/identity/concerns/mfa-manager.js +204 -0
  62. package/src/plugins/identity/concerns/password.js +138 -0
  63. package/src/plugins/identity/concerns/resource-schemas.js +273 -0
  64. package/src/plugins/identity/concerns/token-generator.js +172 -0
  65. package/src/plugins/identity/email-service.js +422 -0
  66. package/src/plugins/identity/index.js +1052 -0
  67. package/src/plugins/identity/oauth2-server.js +1033 -0
  68. package/src/plugins/identity/oidc-discovery.js +285 -0
  69. package/src/plugins/identity/rsa-keys.js +323 -0
  70. package/src/plugins/identity/server.js +500 -0
  71. package/src/plugins/identity/session-manager.js +453 -0
  72. package/src/plugins/identity/ui/layouts/base.js +251 -0
  73. package/src/plugins/identity/ui/middleware.js +135 -0
  74. package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
  75. package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
  76. package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
  77. package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
  78. package/src/plugins/identity/ui/pages/admin/users.js +263 -0
  79. package/src/plugins/identity/ui/pages/consent.js +262 -0
  80. package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
  81. package/src/plugins/identity/ui/pages/login.js +144 -0
  82. package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
  83. package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
  84. package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
  85. package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
  86. package/src/plugins/identity/ui/pages/profile.js +361 -0
  87. package/src/plugins/identity/ui/pages/register.js +226 -0
  88. package/src/plugins/identity/ui/pages/reset-password.js +128 -0
  89. package/src/plugins/identity/ui/pages/verify-email.js +172 -0
  90. package/src/plugins/identity/ui/routes.js +2541 -0
  91. package/src/plugins/identity/ui/styles/main.css +465 -0
  92. package/src/plugins/index.js +4 -1
  93. package/src/plugins/ml/base-model.class.js +65 -16
  94. package/src/plugins/ml/classification-model.class.js +1 -1
  95. package/src/plugins/ml/timeseries-model.class.js +3 -1
  96. package/src/plugins/ml.plugin.js +584 -31
  97. package/src/plugins/shared/error-handler.js +147 -0
  98. package/src/plugins/shared/index.js +9 -0
  99. package/src/plugins/shared/middlewares/compression.js +117 -0
  100. package/src/plugins/shared/middlewares/cors.js +49 -0
  101. package/src/plugins/shared/middlewares/index.js +11 -0
  102. package/src/plugins/shared/middlewares/logging.js +54 -0
  103. package/src/plugins/shared/middlewares/rate-limit.js +73 -0
  104. package/src/plugins/shared/middlewares/security.js +158 -0
  105. package/src/plugins/shared/response-formatter.js +264 -0
  106. package/src/plugins/state-machine.plugin.js +57 -2
  107. package/src/resource.class.js +140 -12
  108. package/src/schema.class.js +30 -1
  109. package/src/validator.class.js +57 -6
  110. package/dist/s3db.cjs.js.map +0 -1
@@ -0,0 +1,636 @@
1
+ import { BaseCloudDriver } from './base-driver.js';
2
+
3
+ /**
4
+ * Production-ready Vultr inventory driver using official @vultr/vultr-node SDK.
5
+ *
6
+ * Covers 12+ services with 15+ resource types:
7
+ * - Compute (instances, bare metal)
8
+ * - Kubernetes (VKE clusters)
9
+ * - Storage (block storage, snapshots, object storage)
10
+ * - Networking (load balancers, firewalls, VPC)
11
+ * - DNS (domains, records)
12
+ * - Databases (managed databases)
13
+ * - SSH Keys
14
+ *
15
+ * @see https://www.vultr.com/api/
16
+ * @see https://github.com/vultr/vultr-node
17
+ */
18
+ export class VultrInventoryDriver extends BaseCloudDriver {
19
+ constructor(options = {}) {
20
+ super({ ...options, driver: options.driver || 'vultr' });
21
+
22
+ this._apiKey = null;
23
+ this._client = null;
24
+ this._accountId = this.config?.accountId || 'vultr';
25
+
26
+ // Services to collect (can be filtered via config.services)
27
+ this._services = this.config?.services || [
28
+ 'instances',
29
+ 'baremetal',
30
+ 'kubernetes',
31
+ 'blockstorage',
32
+ 'snapshots',
33
+ 'loadbalancers',
34
+ 'firewalls',
35
+ 'vpc',
36
+ 'dns',
37
+ 'databases',
38
+ 'sshkeys',
39
+ 'objectstorage'
40
+ ];
41
+ }
42
+
43
+ /**
44
+ * Initialize the Vultr API client.
45
+ */
46
+ async _initializeClient() {
47
+ if (this._client) return;
48
+
49
+ const credentials = this.credentials || {};
50
+ this._apiKey = credentials.apiKey || credentials.token || process.env.VULTR_API_KEY;
51
+
52
+ if (!this._apiKey) {
53
+ throw new Error('Vultr API key is required. Provide via credentials.apiKey or VULTR_API_KEY env var.');
54
+ }
55
+
56
+ // Lazy import to keep core package lightweight
57
+ const { VultrNode } = await import('@vultr/vultr-node');
58
+
59
+ this._client = VultrNode.initialize({ apiKey: this._apiKey });
60
+
61
+ this.logger('info', 'Vultr API client initialized', {
62
+ accountId: this._accountId,
63
+ services: this._services.length
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Main entry point - lists all resources from configured services.
69
+ */
70
+ async *listResources(options = {}) {
71
+ await this._initializeClient();
72
+
73
+ const serviceCollectors = {
74
+ instances: () => this._collectInstances(),
75
+ baremetal: () => this._collectBareMetal(),
76
+ kubernetes: () => this._collectKubernetes(),
77
+ blockstorage: () => this._collectBlockStorage(),
78
+ snapshots: () => this._collectSnapshots(),
79
+ loadbalancers: () => this._collectLoadBalancers(),
80
+ firewalls: () => this._collectFirewalls(),
81
+ vpc: () => this._collectVPC(),
82
+ dns: () => this._collectDNS(),
83
+ databases: () => this._collectDatabases(),
84
+ sshkeys: () => this._collectSSHKeys(),
85
+ objectstorage: () => this._collectObjectStorage()
86
+ };
87
+
88
+ for (const service of this._services) {
89
+ const collector = serviceCollectors[service];
90
+ if (!collector) {
91
+ this.logger('warn', `Unknown Vultr service: ${service}`, { service });
92
+ continue;
93
+ }
94
+
95
+ try {
96
+ this.logger('info', `Collecting Vultr ${service} resources`, { service });
97
+ yield* collector();
98
+ } catch (err) {
99
+ // Continue with next service instead of failing entire sync
100
+ this.logger('error', `Vultr service collection failed, skipping to next service`, {
101
+ service,
102
+ error: err.message,
103
+ errorName: err.name,
104
+ stack: err.stack
105
+ });
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Collect Compute Instances (VPS).
112
+ */
113
+ async *_collectInstances() {
114
+ try {
115
+ const response = await this._client.instances.listInstances();
116
+ const instances = response.instances || [];
117
+
118
+ for (const instance of instances) {
119
+ yield {
120
+ provider: 'vultr',
121
+ accountId: this._accountId,
122
+ region: instance.region,
123
+ service: 'instances',
124
+ resourceType: 'vultr.compute.instance',
125
+ resourceId: instance.id,
126
+ name: instance.label || instance.hostname || instance.id,
127
+ tags: instance.tags || [],
128
+ configuration: this._sanitize(instance)
129
+ };
130
+ }
131
+
132
+ this.logger('info', `Collected ${instances.length} Vultr instances`);
133
+ } catch (err) {
134
+ this.logger('error', 'Failed to collect Vultr instances', {
135
+ error: err.message,
136
+ stack: err.stack
137
+ });
138
+ throw err;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Collect Bare Metal servers.
144
+ */
145
+ async *_collectBareMetal() {
146
+ try {
147
+ const response = await this._client.bareMetal.listBareMetalServers();
148
+ const servers = response.bare_metals || [];
149
+
150
+ for (const server of servers) {
151
+ yield {
152
+ provider: 'vultr',
153
+ accountId: this._accountId,
154
+ region: server.region,
155
+ service: 'baremetal',
156
+ resourceType: 'vultr.baremetal.server',
157
+ resourceId: server.id,
158
+ name: server.label || server.id,
159
+ tags: server.tags || [],
160
+ configuration: this._sanitize(server)
161
+ };
162
+ }
163
+
164
+ this.logger('info', `Collected ${servers.length} Vultr bare metal servers`);
165
+ } catch (err) {
166
+ this.logger('error', 'Failed to collect Vultr bare metal', {
167
+ error: err.message,
168
+ stack: err.stack
169
+ });
170
+ throw err;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Collect Kubernetes clusters (VKE).
176
+ */
177
+ async *_collectKubernetes() {
178
+ try {
179
+ const response = await this._client.kubernetes.listKubernetesClusters();
180
+ const clusters = response.vke_clusters || [];
181
+
182
+ for (const cluster of clusters) {
183
+ yield {
184
+ provider: 'vultr',
185
+ accountId: this._accountId,
186
+ region: cluster.region,
187
+ service: 'kubernetes',
188
+ resourceType: 'vultr.kubernetes.cluster',
189
+ resourceId: cluster.id,
190
+ name: cluster.label || cluster.id,
191
+ tags: [],
192
+ configuration: this._sanitize(cluster)
193
+ };
194
+
195
+ // Collect node pools
196
+ try {
197
+ const npResponse = await this._client.kubernetes.listNodePools({ 'vke-id': cluster.id });
198
+ const nodePools = npResponse.node_pools || [];
199
+
200
+ for (const nodePool of nodePools) {
201
+ yield {
202
+ provider: 'vultr',
203
+ accountId: this._accountId,
204
+ region: cluster.region,
205
+ service: 'kubernetes',
206
+ resourceType: 'vultr.kubernetes.nodepool',
207
+ resourceId: nodePool.id,
208
+ name: nodePool.label || nodePool.id,
209
+ tags: [],
210
+ metadata: { clusterId: cluster.id, clusterLabel: cluster.label },
211
+ configuration: this._sanitize(nodePool)
212
+ };
213
+ }
214
+ } catch (npErr) {
215
+ this.logger('warn', `Failed to collect node pools for cluster ${cluster.id}`, {
216
+ clusterId: cluster.id,
217
+ error: npErr.message
218
+ });
219
+ }
220
+ }
221
+
222
+ this.logger('info', `Collected ${clusters.length} Vultr Kubernetes clusters`);
223
+ } catch (err) {
224
+ this.logger('error', 'Failed to collect Vultr Kubernetes', {
225
+ error: err.message,
226
+ stack: err.stack
227
+ });
228
+ throw err;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Collect Block Storage volumes.
234
+ */
235
+ async *_collectBlockStorage() {
236
+ try {
237
+ const response = await this._client.blockStorage.listBlockStorages();
238
+ const volumes = response.blocks || [];
239
+
240
+ for (const volume of volumes) {
241
+ yield {
242
+ provider: 'vultr',
243
+ accountId: this._accountId,
244
+ region: volume.region,
245
+ service: 'blockstorage',
246
+ resourceType: 'vultr.blockstorage.volume',
247
+ resourceId: volume.id,
248
+ name: volume.label || volume.id,
249
+ tags: [],
250
+ configuration: this._sanitize(volume)
251
+ };
252
+ }
253
+
254
+ this.logger('info', `Collected ${volumes.length} Vultr block storage volumes`);
255
+ } catch (err) {
256
+ this.logger('error', 'Failed to collect Vultr block storage', {
257
+ error: err.message,
258
+ stack: err.stack
259
+ });
260
+ throw err;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Collect Snapshots.
266
+ */
267
+ async *_collectSnapshots() {
268
+ try {
269
+ const response = await this._client.snapshots.listSnapshots();
270
+ const snapshots = response.snapshots || [];
271
+
272
+ for (const snapshot of snapshots) {
273
+ yield {
274
+ provider: 'vultr',
275
+ accountId: this._accountId,
276
+ region: null, // Snapshots are global
277
+ service: 'snapshots',
278
+ resourceType: 'vultr.snapshot',
279
+ resourceId: snapshot.id,
280
+ name: snapshot.description || snapshot.id,
281
+ tags: [],
282
+ configuration: this._sanitize(snapshot)
283
+ };
284
+ }
285
+
286
+ this.logger('info', `Collected ${snapshots.length} Vultr snapshots`);
287
+ } catch (err) {
288
+ this.logger('error', 'Failed to collect Vultr snapshots', {
289
+ error: err.message,
290
+ stack: err.stack
291
+ });
292
+ throw err;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Collect Load Balancers.
298
+ */
299
+ async *_collectLoadBalancers() {
300
+ try {
301
+ const response = await this._client.loadBalancers.listLoadBalancers();
302
+ const lbs = response.load_balancers || [];
303
+
304
+ for (const lb of lbs) {
305
+ yield {
306
+ provider: 'vultr',
307
+ accountId: this._accountId,
308
+ region: lb.region,
309
+ service: 'loadbalancers',
310
+ resourceType: 'vultr.loadbalancer',
311
+ resourceId: lb.id,
312
+ name: lb.label || lb.id,
313
+ tags: [],
314
+ configuration: this._sanitize(lb)
315
+ };
316
+ }
317
+
318
+ this.logger('info', `Collected ${lbs.length} Vultr load balancers`);
319
+ } catch (err) {
320
+ this.logger('error', 'Failed to collect Vultr load balancers', {
321
+ error: err.message,
322
+ stack: err.stack
323
+ });
324
+ throw err;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Collect Firewall Groups.
330
+ */
331
+ async *_collectFirewalls() {
332
+ try {
333
+ const response = await this._client.firewalls.listFirewallGroups();
334
+ const firewalls = response.firewall_groups || [];
335
+
336
+ for (const firewall of firewalls) {
337
+ yield {
338
+ provider: 'vultr',
339
+ accountId: this._accountId,
340
+ region: null, // Firewalls are global
341
+ service: 'firewalls',
342
+ resourceType: 'vultr.firewall.group',
343
+ resourceId: firewall.id,
344
+ name: firewall.description || firewall.id,
345
+ tags: [],
346
+ configuration: this._sanitize(firewall)
347
+ };
348
+
349
+ // Collect firewall rules
350
+ try {
351
+ const rulesResponse = await this._client.firewalls.listFirewallGroupRules({
352
+ 'firewall-group-id': firewall.id
353
+ });
354
+ const rules = rulesResponse.firewall_rules || [];
355
+
356
+ for (const rule of rules) {
357
+ yield {
358
+ provider: 'vultr',
359
+ accountId: this._accountId,
360
+ region: null,
361
+ service: 'firewalls',
362
+ resourceType: 'vultr.firewall.rule',
363
+ resourceId: `${firewall.id}/${rule.id}`,
364
+ name: `${firewall.description || firewall.id} - Rule ${rule.id}`,
365
+ tags: [],
366
+ metadata: { firewallGroupId: firewall.id },
367
+ configuration: this._sanitize(rule)
368
+ };
369
+ }
370
+ } catch (rulesErr) {
371
+ this.logger('warn', `Failed to collect firewall rules for ${firewall.id}`, {
372
+ firewallId: firewall.id,
373
+ error: rulesErr.message
374
+ });
375
+ }
376
+ }
377
+
378
+ this.logger('info', `Collected ${firewalls.length} Vultr firewall groups`);
379
+ } catch (err) {
380
+ this.logger('error', 'Failed to collect Vultr firewalls', {
381
+ error: err.message,
382
+ stack: err.stack
383
+ });
384
+ throw err;
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Collect VPC/VPC 2.0 networks.
390
+ */
391
+ async *_collectVPC() {
392
+ try {
393
+ // VPC 2.0 (newer)
394
+ try {
395
+ const response = await this._client.vpc2.listVPC2s();
396
+ const vpcs = response.vpcs || [];
397
+
398
+ for (const vpc of vpcs) {
399
+ yield {
400
+ provider: 'vultr',
401
+ accountId: this._accountId,
402
+ region: vpc.region,
403
+ service: 'vpc',
404
+ resourceType: 'vultr.vpc.network',
405
+ resourceId: vpc.id,
406
+ name: vpc.description || vpc.id,
407
+ tags: [],
408
+ configuration: this._sanitize(vpc)
409
+ };
410
+ }
411
+
412
+ this.logger('info', `Collected ${vpcs.length} Vultr VPC 2.0 networks`);
413
+ } catch (vpc2Err) {
414
+ this.logger('warn', 'Failed to collect VPC 2.0, trying legacy VPC', {
415
+ error: vpc2Err.message
416
+ });
417
+
418
+ // Fallback to legacy VPC
419
+ const legacyResponse = await this._client.vpc.listVPCs();
420
+ const legacyVpcs = legacyResponse.vpcs || [];
421
+
422
+ for (const vpc of legacyVpcs) {
423
+ yield {
424
+ provider: 'vultr',
425
+ accountId: this._accountId,
426
+ region: vpc.region,
427
+ service: 'vpc',
428
+ resourceType: 'vultr.vpc.network.legacy',
429
+ resourceId: vpc.id,
430
+ name: vpc.description || vpc.id,
431
+ tags: [],
432
+ configuration: this._sanitize(vpc)
433
+ };
434
+ }
435
+
436
+ this.logger('info', `Collected ${legacyVpcs.length} Vultr legacy VPC networks`);
437
+ }
438
+ } catch (err) {
439
+ this.logger('error', 'Failed to collect Vultr VPC', {
440
+ error: err.message,
441
+ stack: err.stack
442
+ });
443
+ throw err;
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Collect DNS domains and records.
449
+ */
450
+ async *_collectDNS() {
451
+ try {
452
+ const response = await this._client.dns.listDomains();
453
+ const domains = response.domains || [];
454
+
455
+ for (const domain of domains) {
456
+ yield {
457
+ provider: 'vultr',
458
+ accountId: this._accountId,
459
+ region: null, // DNS is global
460
+ service: 'dns',
461
+ resourceType: 'vultr.dns.domain',
462
+ resourceId: domain.domain,
463
+ name: domain.domain,
464
+ tags: [],
465
+ configuration: this._sanitize(domain)
466
+ };
467
+
468
+ // Collect DNS records for this domain
469
+ try {
470
+ const recordsResponse = await this._client.dns.listRecords({ 'dns-domain': domain.domain });
471
+ const records = recordsResponse.records || [];
472
+
473
+ for (const record of records) {
474
+ yield {
475
+ provider: 'vultr',
476
+ accountId: this._accountId,
477
+ region: null,
478
+ service: 'dns',
479
+ resourceType: 'vultr.dns.record',
480
+ resourceId: record.id,
481
+ name: `${record.name}.${domain.domain}`,
482
+ tags: [],
483
+ metadata: { domain: domain.domain },
484
+ configuration: this._sanitize(record)
485
+ };
486
+ }
487
+ } catch (recordsErr) {
488
+ this.logger('warn', `Failed to collect DNS records for ${domain.domain}`, {
489
+ domain: domain.domain,
490
+ error: recordsErr.message
491
+ });
492
+ }
493
+ }
494
+
495
+ this.logger('info', `Collected ${domains.length} Vultr DNS domains`);
496
+ } catch (err) {
497
+ this.logger('error', 'Failed to collect Vultr DNS', {
498
+ error: err.message,
499
+ stack: err.stack
500
+ });
501
+ throw err;
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Collect Managed Databases.
507
+ */
508
+ async *_collectDatabases() {
509
+ try {
510
+ const response = await this._client.databases.listDatabases();
511
+ const databases = response.databases || [];
512
+
513
+ for (const db of databases) {
514
+ yield {
515
+ provider: 'vultr',
516
+ accountId: this._accountId,
517
+ region: db.region,
518
+ service: 'databases',
519
+ resourceType: 'vultr.database',
520
+ resourceId: db.id,
521
+ name: db.label || db.id,
522
+ tags: db.tag ? [db.tag] : [],
523
+ configuration: this._sanitize(db)
524
+ };
525
+ }
526
+
527
+ this.logger('info', `Collected ${databases.length} Vultr managed databases`);
528
+ } catch (err) {
529
+ this.logger('error', 'Failed to collect Vultr databases', {
530
+ error: err.message,
531
+ stack: err.stack
532
+ });
533
+ throw err;
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Collect SSH Keys.
539
+ */
540
+ async *_collectSSHKeys() {
541
+ try {
542
+ const response = await this._client.sshKeys.listSshKeys();
543
+ const keys = response.ssh_keys || [];
544
+
545
+ for (const key of keys) {
546
+ yield {
547
+ provider: 'vultr',
548
+ accountId: this._accountId,
549
+ region: null, // SSH keys are global
550
+ service: 'sshkeys',
551
+ resourceType: 'vultr.sshkey',
552
+ resourceId: key.id,
553
+ name: key.name || key.id,
554
+ tags: [],
555
+ configuration: this._sanitize(key)
556
+ };
557
+ }
558
+
559
+ this.logger('info', `Collected ${keys.length} Vultr SSH keys`);
560
+ } catch (err) {
561
+ this.logger('error', 'Failed to collect Vultr SSH keys', {
562
+ error: err.message,
563
+ stack: err.stack
564
+ });
565
+ throw err;
566
+ }
567
+ }
568
+
569
+ /**
570
+ * Collect Object Storage buckets.
571
+ */
572
+ async *_collectObjectStorage() {
573
+ try {
574
+ // List object storage clusters first
575
+ const clustersResponse = await this._client.objectStorage.listObjectStorageClusters();
576
+ const clusters = clustersResponse.clusters || [];
577
+
578
+ // For each cluster, list object storage instances
579
+ for (const cluster of clusters) {
580
+ try {
581
+ const response = await this._client.objectStorage.listObjectStorages();
582
+ const storages = response.object_storages || [];
583
+
584
+ for (const storage of storages) {
585
+ // Filter by cluster if needed
586
+ if (storage.object_storage_cluster_id === cluster.id) {
587
+ yield {
588
+ provider: 'vultr',
589
+ accountId: this._accountId,
590
+ region: cluster.region,
591
+ service: 'objectstorage',
592
+ resourceType: 'vultr.objectstorage.bucket',
593
+ resourceId: storage.id,
594
+ name: storage.label || storage.id,
595
+ tags: [],
596
+ metadata: { clusterId: cluster.id, clusterRegion: cluster.region },
597
+ configuration: this._sanitize(storage)
598
+ };
599
+ }
600
+ }
601
+ } catch (storageErr) {
602
+ this.logger('warn', `Failed to collect object storage for cluster ${cluster.id}`, {
603
+ clusterId: cluster.id,
604
+ error: storageErr.message
605
+ });
606
+ }
607
+ }
608
+
609
+ this.logger('info', `Collected object storage from ${clusters.length} Vultr clusters`);
610
+ } catch (err) {
611
+ this.logger('error', 'Failed to collect Vultr object storage', {
612
+ error: err.message,
613
+ stack: err.stack
614
+ });
615
+ throw err;
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Sanitize configuration by removing sensitive data.
621
+ */
622
+ _sanitize(config) {
623
+ if (!config || typeof config !== 'object') return config;
624
+
625
+ const sanitized = { ...config };
626
+ const sensitiveFields = ['api_key', 'password', 'secret', 'token', 'ssh_key', 'private_key'];
627
+
628
+ for (const field of sensitiveFields) {
629
+ if (field in sanitized) {
630
+ sanitized[field] = '***REDACTED***';
631
+ }
632
+ }
633
+
634
+ return sanitized;
635
+ }
636
+ }
@@ -0,0 +1,20 @@
1
+ export {
2
+ registerCloudDriver,
3
+ createCloudDriver,
4
+ listCloudDrivers,
5
+ validateCloudDefinition,
6
+ BaseCloudDriver
7
+ } from './registry.js';
8
+
9
+ export {
10
+ AwsInventoryDriver
11
+ } from './drivers/aws-driver.js';
12
+
13
+ export {
14
+ AwsMockDriver,
15
+ GcpMockDriver,
16
+ DigitalOceanMockDriver,
17
+ OracleMockDriver,
18
+ AzureMockDriver,
19
+ VultrMockDriver
20
+ } from './drivers/mock-drivers.js';