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,768 @@
1
+ import { BaseCloudDriver } from './base-driver.js';
2
+
3
+ /**
4
+ * Production-ready Oracle Cloud Infrastructure (OCI) inventory driver using official oci-sdk.
5
+ *
6
+ * Covers 15+ services with 25+ resource types:
7
+ * - Compute (instances, boot volumes, images)
8
+ * - Kubernetes (OKE clusters, node pools)
9
+ * - Databases (Autonomous DB, DB Systems)
10
+ * - Storage (block volumes, buckets, file systems)
11
+ * - Networking (VCNs, subnets, load balancers, network load balancers, gateways)
12
+ * - Identity (users, groups, compartments, policies)
13
+ * - DNS (zones, records)
14
+ *
15
+ * @see https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/typescriptsdk.htm
16
+ * @see https://github.com/oracle/oci-typescript-sdk
17
+ */
18
+ export class OracleInventoryDriver extends BaseCloudDriver {
19
+ constructor(options = {}) {
20
+ super({ ...options, driver: options.driver || 'oracle' });
21
+
22
+ this._provider = null;
23
+ this._tenancyId = null;
24
+ this._compartmentId = null;
25
+ this._accountId = this.config?.accountId || 'oracle';
26
+
27
+ // Services to collect (can be filtered via config.services)
28
+ this._services = this.config?.services || [
29
+ 'compute',
30
+ 'kubernetes',
31
+ 'database',
32
+ 'blockstorage',
33
+ 'objectstorage',
34
+ 'filestorage',
35
+ 'vcn',
36
+ 'loadbalancer',
37
+ 'identity',
38
+ 'dns'
39
+ ];
40
+
41
+ // Regions to scan (can be filtered via config.regions)
42
+ this._regions = this.config?.regions || null; // null = all subscribed regions
43
+ }
44
+
45
+ /**
46
+ * Initialize the OCI provider and clients.
47
+ */
48
+ async _initializeProvider() {
49
+ if (this._provider) return;
50
+
51
+ const credentials = this.credentials || {};
52
+
53
+ // Import OCI common module
54
+ const common = await import('oci-common');
55
+
56
+ // Setup authentication provider
57
+ // Support multiple auth methods: config file, instance principal, resource principal
58
+ if (credentials.configFilePath) {
59
+ // Config file authentication
60
+ this._provider = new common.ConfigFileAuthenticationDetailsProvider(
61
+ credentials.configFilePath,
62
+ credentials.profile || 'DEFAULT'
63
+ );
64
+ } else if (credentials.instancePrincipal) {
65
+ // Instance principal authentication (for OCI compute instances)
66
+ this._provider = await common.ResourcePrincipalAuthenticationDetailsProvider.builder();
67
+ } else if (credentials.user && credentials.fingerprint && credentials.privateKey) {
68
+ // Direct credentials
69
+ this._provider = new common.SimpleAuthenticationDetailsProvider(
70
+ credentials.tenancy || this.config?.tenancyId,
71
+ credentials.user,
72
+ credentials.fingerprint,
73
+ credentials.privateKey,
74
+ credentials.passphrase || null,
75
+ credentials.region || this.config?.region || common.Region.US_ASHBURN_1
76
+ );
77
+ } else {
78
+ // Default to config file
79
+ this._provider = new common.ConfigFileAuthenticationDetailsProvider();
80
+ }
81
+
82
+ this._tenancyId = credentials.tenancy || this.config?.tenancyId;
83
+ this._compartmentId = this.config?.compartmentId || this._tenancyId;
84
+
85
+ this.logger('info', 'OCI provider initialized', {
86
+ accountId: this._accountId,
87
+ services: this._services.length
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Main entry point - lists all resources from configured services.
93
+ */
94
+ async *listResources(options = {}) {
95
+ await this._initializeProvider();
96
+
97
+ const serviceCollectors = {
98
+ compute: () => this._collectCompute(),
99
+ kubernetes: () => this._collectKubernetes(),
100
+ database: () => this._collectDatabases(),
101
+ blockstorage: () => this._collectBlockStorage(),
102
+ objectstorage: () => this._collectObjectStorage(),
103
+ filestorage: () => this._collectFileStorage(),
104
+ vcn: () => this._collectVCN(),
105
+ loadbalancer: () => this._collectLoadBalancers(),
106
+ identity: () => this._collectIdentity(),
107
+ dns: () => this._collectDNS()
108
+ };
109
+
110
+ for (const service of this._services) {
111
+ const collector = serviceCollectors[service];
112
+ if (!collector) {
113
+ this.logger('warn', `Unknown OCI service: ${service}`, { service });
114
+ continue;
115
+ }
116
+
117
+ try {
118
+ this.logger('info', `Collecting OCI ${service} resources`, { service });
119
+ yield* collector();
120
+ } catch (err) {
121
+ // Continue with next service instead of failing entire sync
122
+ this.logger('error', `OCI service collection failed, skipping to next service`, {
123
+ service,
124
+ error: err.message,
125
+ errorName: err.name,
126
+ stack: err.stack
127
+ });
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Collect Compute instances.
134
+ */
135
+ async *_collectCompute() {
136
+ try {
137
+ const { core } = await import('oci-core');
138
+ const computeClient = new core.ComputeClient({ authenticationDetailsProvider: this._provider });
139
+
140
+ const regions = await this._getRegions();
141
+
142
+ for (const region of regions) {
143
+ computeClient.region = region;
144
+
145
+ // List instances
146
+ const instancesResponse = await computeClient.listInstances({
147
+ compartmentId: this._compartmentId
148
+ });
149
+ const instances = instancesResponse.items || [];
150
+
151
+ for (const instance of instances) {
152
+ yield {
153
+ provider: 'oracle',
154
+ accountId: this._accountId,
155
+ region: region.regionName,
156
+ service: 'compute',
157
+ resourceType: 'oracle.compute.instance',
158
+ resourceId: instance.id,
159
+ name: instance.displayName || instance.id,
160
+ tags: this._extractTags(instance.freeformTags, instance.definedTags),
161
+ configuration: this._sanitize(instance)
162
+ };
163
+ }
164
+
165
+ this.logger('info', `Collected ${instances.length} OCI compute instances in ${region.regionName}`);
166
+ }
167
+ } catch (err) {
168
+ this.logger('error', 'Failed to collect OCI compute', {
169
+ error: err.message,
170
+ stack: err.stack
171
+ });
172
+ throw err;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Collect Kubernetes (OKE) clusters.
178
+ */
179
+ async *_collectKubernetes() {
180
+ try {
181
+ const { containerengine } = await import('oci-containerengine');
182
+ const containerClient = new containerengine.ContainerEngineClient({
183
+ authenticationDetailsProvider: this._provider
184
+ });
185
+
186
+ const regions = await this._getRegions();
187
+
188
+ for (const region of regions) {
189
+ containerClient.region = region;
190
+
191
+ const clustersResponse = await containerClient.listClusters({
192
+ compartmentId: this._compartmentId
193
+ });
194
+ const clusters = clustersResponse.items || [];
195
+
196
+ for (const cluster of clusters) {
197
+ yield {
198
+ provider: 'oracle',
199
+ accountId: this._accountId,
200
+ region: region.regionName,
201
+ service: 'kubernetes',
202
+ resourceType: 'oracle.kubernetes.cluster',
203
+ resourceId: cluster.id,
204
+ name: cluster.name,
205
+ tags: this._extractTags(cluster.freeformTags, cluster.definedTags),
206
+ configuration: this._sanitize(cluster)
207
+ };
208
+
209
+ // Collect node pools
210
+ try {
211
+ const nodePoolsResponse = await containerClient.listNodePools({
212
+ compartmentId: this._compartmentId,
213
+ clusterId: cluster.id
214
+ });
215
+ const nodePools = nodePoolsResponse.items || [];
216
+
217
+ for (const nodePool of nodePools) {
218
+ yield {
219
+ provider: 'oracle',
220
+ accountId: this._accountId,
221
+ region: region.regionName,
222
+ service: 'kubernetes',
223
+ resourceType: 'oracle.kubernetes.nodepool',
224
+ resourceId: nodePool.id,
225
+ name: nodePool.name,
226
+ tags: this._extractTags(nodePool.freeformTags, nodePool.definedTags),
227
+ metadata: { clusterId: cluster.id, clusterName: cluster.name },
228
+ configuration: this._sanitize(nodePool)
229
+ };
230
+ }
231
+ } catch (npErr) {
232
+ this.logger('warn', `Failed to collect node pools for cluster ${cluster.id}`, {
233
+ clusterId: cluster.id,
234
+ error: npErr.message
235
+ });
236
+ }
237
+ }
238
+
239
+ this.logger('info', `Collected ${clusters.length} OCI OKE clusters in ${region.regionName}`);
240
+ }
241
+ } catch (err) {
242
+ this.logger('error', 'Failed to collect OCI Kubernetes', {
243
+ error: err.message,
244
+ stack: err.stack
245
+ });
246
+ throw err;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Collect Databases (Autonomous Database, DB Systems).
252
+ */
253
+ async *_collectDatabases() {
254
+ try {
255
+ const { database } = await import('oci-database');
256
+ const databaseClient = new database.DatabaseClient({
257
+ authenticationDetailsProvider: this._provider
258
+ });
259
+
260
+ const regions = await this._getRegions();
261
+
262
+ for (const region of regions) {
263
+ databaseClient.region = region;
264
+
265
+ // Autonomous Databases
266
+ const autonomousDbsResponse = await databaseClient.listAutonomousDatabases({
267
+ compartmentId: this._compartmentId
268
+ });
269
+ const autonomousDbs = autonomousDbsResponse.items || [];
270
+
271
+ for (const db of autonomousDbs) {
272
+ yield {
273
+ provider: 'oracle',
274
+ accountId: this._accountId,
275
+ region: region.regionName,
276
+ service: 'database',
277
+ resourceType: 'oracle.database.autonomous',
278
+ resourceId: db.id,
279
+ name: db.displayName || db.dbName,
280
+ tags: this._extractTags(db.freeformTags, db.definedTags),
281
+ configuration: this._sanitize(db)
282
+ };
283
+ }
284
+
285
+ // DB Systems
286
+ const dbSystemsResponse = await databaseClient.listDbSystems({
287
+ compartmentId: this._compartmentId
288
+ });
289
+ const dbSystems = dbSystemsResponse.items || [];
290
+
291
+ for (const dbSystem of dbSystems) {
292
+ yield {
293
+ provider: 'oracle',
294
+ accountId: this._accountId,
295
+ region: region.regionName,
296
+ service: 'database',
297
+ resourceType: 'oracle.database.system',
298
+ resourceId: dbSystem.id,
299
+ name: dbSystem.displayName,
300
+ tags: this._extractTags(dbSystem.freeformTags, dbSystem.definedTags),
301
+ configuration: this._sanitize(dbSystem)
302
+ };
303
+ }
304
+
305
+ this.logger('info', `Collected ${autonomousDbs.length} Autonomous DBs and ${dbSystems.length} DB Systems in ${region.regionName}`);
306
+ }
307
+ } catch (err) {
308
+ this.logger('error', 'Failed to collect OCI databases', {
309
+ error: err.message,
310
+ stack: err.stack
311
+ });
312
+ throw err;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Collect Block Storage volumes.
318
+ */
319
+ async *_collectBlockStorage() {
320
+ try {
321
+ const { core } = await import('oci-core');
322
+ const blockstorageClient = new core.BlockstorageClient({
323
+ authenticationDetailsProvider: this._provider
324
+ });
325
+
326
+ const regions = await this._getRegions();
327
+
328
+ for (const region of regions) {
329
+ blockstorageClient.region = region;
330
+
331
+ const volumesResponse = await blockstorageClient.listVolumes({
332
+ compartmentId: this._compartmentId
333
+ });
334
+ const volumes = volumesResponse.items || [];
335
+
336
+ for (const volume of volumes) {
337
+ yield {
338
+ provider: 'oracle',
339
+ accountId: this._accountId,
340
+ region: region.regionName,
341
+ service: 'blockstorage',
342
+ resourceType: 'oracle.blockstorage.volume',
343
+ resourceId: volume.id,
344
+ name: volume.displayName,
345
+ tags: this._extractTags(volume.freeformTags, volume.definedTags),
346
+ configuration: this._sanitize(volume)
347
+ };
348
+ }
349
+
350
+ this.logger('info', `Collected ${volumes.length} OCI block volumes in ${region.regionName}`);
351
+ }
352
+ } catch (err) {
353
+ this.logger('error', 'Failed to collect OCI block storage', {
354
+ error: err.message,
355
+ stack: err.stack
356
+ });
357
+ throw err;
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Collect Object Storage buckets.
363
+ */
364
+ async *_collectObjectStorage() {
365
+ try {
366
+ const { objectstorage } = await import('oci-objectstorage');
367
+ const objectStorageClient = new objectstorage.ObjectStorageClient({
368
+ authenticationDetailsProvider: this._provider
369
+ });
370
+
371
+ const regions = await this._getRegions();
372
+
373
+ // Get namespace
374
+ const namespaceResponse = await objectStorageClient.getNamespace({});
375
+ const namespace = namespaceResponse.value;
376
+
377
+ for (const region of regions) {
378
+ objectStorageClient.region = region;
379
+
380
+ const bucketsResponse = await objectStorageClient.listBuckets({
381
+ namespaceName: namespace,
382
+ compartmentId: this._compartmentId
383
+ });
384
+ const buckets = bucketsResponse.items || [];
385
+
386
+ for (const bucket of buckets) {
387
+ yield {
388
+ provider: 'oracle',
389
+ accountId: this._accountId,
390
+ region: region.regionName,
391
+ service: 'objectstorage',
392
+ resourceType: 'oracle.objectstorage.bucket',
393
+ resourceId: bucket.name,
394
+ name: bucket.name,
395
+ tags: this._extractTags(bucket.freeformTags, bucket.definedTags),
396
+ metadata: { namespace },
397
+ configuration: this._sanitize(bucket)
398
+ };
399
+ }
400
+
401
+ this.logger('info', `Collected ${buckets.length} OCI object storage buckets in ${region.regionName}`);
402
+ }
403
+ } catch (err) {
404
+ this.logger('error', 'Failed to collect OCI object storage', {
405
+ error: err.message,
406
+ stack: err.stack
407
+ });
408
+ throw err;
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Collect File Storage file systems.
414
+ */
415
+ async *_collectFileStorage() {
416
+ try {
417
+ const { filestorage } = await import('oci-filestorage');
418
+ const fileStorageClient = new filestorage.FileStorageClient({
419
+ authenticationDetailsProvider: this._provider
420
+ });
421
+
422
+ const regions = await this._getRegions();
423
+
424
+ for (const region of regions) {
425
+ fileStorageClient.region = region;
426
+
427
+ const fileSystemsResponse = await fileStorageClient.listFileSystems({
428
+ compartmentId: this._compartmentId,
429
+ availabilityDomain: region.regionName // Note: may need proper AD
430
+ });
431
+ const fileSystems = fileSystemsResponse.items || [];
432
+
433
+ for (const fs of fileSystems) {
434
+ yield {
435
+ provider: 'oracle',
436
+ accountId: this._accountId,
437
+ region: region.regionName,
438
+ service: 'filestorage',
439
+ resourceType: 'oracle.filestorage.filesystem',
440
+ resourceId: fs.id,
441
+ name: fs.displayName,
442
+ tags: this._extractTags(fs.freeformTags, fs.definedTags),
443
+ configuration: this._sanitize(fs)
444
+ };
445
+ }
446
+
447
+ this.logger('info', `Collected ${fileSystems.length} OCI file systems in ${region.regionName}`);
448
+ }
449
+ } catch (err) {
450
+ this.logger('error', 'Failed to collect OCI file storage', {
451
+ error: err.message,
452
+ stack: err.stack
453
+ });
454
+ throw err;
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Collect VCN (Virtual Cloud Network) resources.
460
+ */
461
+ async *_collectVCN() {
462
+ try {
463
+ const { core } = await import('oci-core');
464
+ const vcnClient = new core.VirtualNetworkClient({
465
+ authenticationDetailsProvider: this._provider
466
+ });
467
+
468
+ const regions = await this._getRegions();
469
+
470
+ for (const region of regions) {
471
+ vcnClient.region = region;
472
+
473
+ // VCNs
474
+ const vcnsResponse = await vcnClient.listVcns({
475
+ compartmentId: this._compartmentId
476
+ });
477
+ const vcns = vcnsResponse.items || [];
478
+
479
+ for (const vcn of vcns) {
480
+ yield {
481
+ provider: 'oracle',
482
+ accountId: this._accountId,
483
+ region: region.regionName,
484
+ service: 'vcn',
485
+ resourceType: 'oracle.vcn.network',
486
+ resourceId: vcn.id,
487
+ name: vcn.displayName,
488
+ tags: this._extractTags(vcn.freeformTags, vcn.definedTags),
489
+ configuration: this._sanitize(vcn)
490
+ };
491
+
492
+ // Collect subnets for this VCN
493
+ try {
494
+ const subnetsResponse = await vcnClient.listSubnets({
495
+ compartmentId: this._compartmentId,
496
+ vcnId: vcn.id
497
+ });
498
+ const subnets = subnetsResponse.items || [];
499
+
500
+ for (const subnet of subnets) {
501
+ yield {
502
+ provider: 'oracle',
503
+ accountId: this._accountId,
504
+ region: region.regionName,
505
+ service: 'vcn',
506
+ resourceType: 'oracle.vcn.subnet',
507
+ resourceId: subnet.id,
508
+ name: subnet.displayName,
509
+ tags: this._extractTags(subnet.freeformTags, subnet.definedTags),
510
+ metadata: { vcnId: vcn.id, vcnName: vcn.displayName },
511
+ configuration: this._sanitize(subnet)
512
+ };
513
+ }
514
+ } catch (subnetErr) {
515
+ this.logger('warn', `Failed to collect subnets for VCN ${vcn.id}`, {
516
+ vcnId: vcn.id,
517
+ error: subnetErr.message
518
+ });
519
+ }
520
+ }
521
+
522
+ this.logger('info', `Collected ${vcns.length} OCI VCNs in ${region.regionName}`);
523
+ }
524
+ } catch (err) {
525
+ this.logger('error', 'Failed to collect OCI VCN', {
526
+ error: err.message,
527
+ stack: err.stack
528
+ });
529
+ throw err;
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Collect Load Balancers.
535
+ */
536
+ async *_collectLoadBalancers() {
537
+ try {
538
+ const { loadbalancer } = await import('oci-loadbalancer');
539
+ const lbClient = new loadbalancer.LoadBalancerClient({
540
+ authenticationDetailsProvider: this._provider
541
+ });
542
+
543
+ const regions = await this._getRegions();
544
+
545
+ for (const region of regions) {
546
+ lbClient.region = region;
547
+
548
+ const lbsResponse = await lbClient.listLoadBalancers({
549
+ compartmentId: this._compartmentId
550
+ });
551
+ const lbs = lbsResponse.items || [];
552
+
553
+ for (const lb of lbs) {
554
+ yield {
555
+ provider: 'oracle',
556
+ accountId: this._accountId,
557
+ region: region.regionName,
558
+ service: 'loadbalancer',
559
+ resourceType: 'oracle.loadbalancer',
560
+ resourceId: lb.id,
561
+ name: lb.displayName,
562
+ tags: this._extractTags(lb.freeformTags, lb.definedTags),
563
+ configuration: this._sanitize(lb)
564
+ };
565
+ }
566
+
567
+ this.logger('info', `Collected ${lbs.length} OCI load balancers in ${region.regionName}`);
568
+ }
569
+ } catch (err) {
570
+ this.logger('error', 'Failed to collect OCI load balancers', {
571
+ error: err.message,
572
+ stack: err.stack
573
+ });
574
+ throw err;
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Collect Identity resources (users, groups, compartments).
580
+ */
581
+ async *_collectIdentity() {
582
+ try {
583
+ const { identity } = await import('oci-identity');
584
+ const identityClient = new identity.IdentityClient({
585
+ authenticationDetailsProvider: this._provider
586
+ });
587
+
588
+ // Users
589
+ const usersResponse = await identityClient.listUsers({
590
+ compartmentId: this._tenancyId
591
+ });
592
+ const users = usersResponse.items || [];
593
+
594
+ for (const user of users) {
595
+ yield {
596
+ provider: 'oracle',
597
+ accountId: this._accountId,
598
+ region: null, // Identity is global
599
+ service: 'identity',
600
+ resourceType: 'oracle.identity.user',
601
+ resourceId: user.id,
602
+ name: user.name,
603
+ tags: this._extractTags(user.freeformTags, user.definedTags),
604
+ configuration: this._sanitize(user)
605
+ };
606
+ }
607
+
608
+ // Groups
609
+ const groupsResponse = await identityClient.listGroups({
610
+ compartmentId: this._tenancyId
611
+ });
612
+ const groups = groupsResponse.items || [];
613
+
614
+ for (const group of groups) {
615
+ yield {
616
+ provider: 'oracle',
617
+ accountId: this._accountId,
618
+ region: null,
619
+ service: 'identity',
620
+ resourceType: 'oracle.identity.group',
621
+ resourceId: group.id,
622
+ name: group.name,
623
+ tags: this._extractTags(group.freeformTags, group.definedTags),
624
+ configuration: this._sanitize(group)
625
+ };
626
+ }
627
+
628
+ // Compartments
629
+ const compartmentsResponse = await identityClient.listCompartments({
630
+ compartmentId: this._tenancyId
631
+ });
632
+ const compartments = compartmentsResponse.items || [];
633
+
634
+ for (const compartment of compartments) {
635
+ yield {
636
+ provider: 'oracle',
637
+ accountId: this._accountId,
638
+ region: null,
639
+ service: 'identity',
640
+ resourceType: 'oracle.identity.compartment',
641
+ resourceId: compartment.id,
642
+ name: compartment.name,
643
+ tags: this._extractTags(compartment.freeformTags, compartment.definedTags),
644
+ configuration: this._sanitize(compartment)
645
+ };
646
+ }
647
+
648
+ this.logger('info', `Collected ${users.length} users, ${groups.length} groups, ${compartments.length} compartments`);
649
+ } catch (err) {
650
+ this.logger('error', 'Failed to collect OCI identity', {
651
+ error: err.message,
652
+ stack: err.stack
653
+ });
654
+ throw err;
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Collect DNS zones.
660
+ */
661
+ async *_collectDNS() {
662
+ try {
663
+ const { dns } = await import('oci-dns');
664
+ const dnsClient = new dns.DnsClient({
665
+ authenticationDetailsProvider: this._provider
666
+ });
667
+
668
+ const zonesResponse = await dnsClient.listZones({
669
+ compartmentId: this._compartmentId
670
+ });
671
+ const zones = zonesResponse.items || [];
672
+
673
+ for (const zone of zones) {
674
+ yield {
675
+ provider: 'oracle',
676
+ accountId: this._accountId,
677
+ region: null, // DNS is global
678
+ service: 'dns',
679
+ resourceType: 'oracle.dns.zone',
680
+ resourceId: zone.id,
681
+ name: zone.name,
682
+ tags: this._extractTags(zone.freeformTags, zone.definedTags),
683
+ configuration: this._sanitize(zone)
684
+ };
685
+ }
686
+
687
+ this.logger('info', `Collected ${zones.length} OCI DNS zones`);
688
+ } catch (err) {
689
+ this.logger('error', 'Failed to collect OCI DNS', {
690
+ error: err.message,
691
+ stack: err.stack
692
+ });
693
+ throw err;
694
+ }
695
+ }
696
+
697
+ /**
698
+ * Get list of subscribed regions.
699
+ */
700
+ async _getRegions() {
701
+ if (this._regions && Array.isArray(this._regions)) {
702
+ // Use configured regions
703
+ const common = await import('oci-common');
704
+ return this._regions.map(r => ({ regionName: r, region: common.Region[r] }));
705
+ }
706
+
707
+ // Get all subscribed regions
708
+ const { identity } = await import('oci-identity');
709
+ const identityClient = new identity.IdentityClient({
710
+ authenticationDetailsProvider: this._provider
711
+ });
712
+
713
+ const regionsResponse = await identityClient.listRegionSubscriptions({
714
+ tenancyId: this._tenancyId
715
+ });
716
+
717
+ return regionsResponse.items || [];
718
+ }
719
+
720
+ /**
721
+ * Extract tags from OCI freeform and defined tags.
722
+ */
723
+ _extractTags(freeformTags, definedTags) {
724
+ const tags = {};
725
+
726
+ if (freeformTags && typeof freeformTags === 'object') {
727
+ Object.assign(tags, freeformTags);
728
+ }
729
+
730
+ if (definedTags && typeof definedTags === 'object') {
731
+ // Flatten defined tags
732
+ for (const [namespace, namespaceTags] of Object.entries(definedTags)) {
733
+ for (const [key, value] of Object.entries(namespaceTags)) {
734
+ tags[`${namespace}.${key}`] = value;
735
+ }
736
+ }
737
+ }
738
+
739
+ return tags;
740
+ }
741
+
742
+ /**
743
+ * Sanitize configuration by removing sensitive data.
744
+ */
745
+ _sanitize(config) {
746
+ if (!config || typeof config !== 'object') return config;
747
+
748
+ const sanitized = { ...config };
749
+ const sensitiveFields = [
750
+ 'password',
751
+ 'adminPassword',
752
+ 'privateKey',
753
+ 'publicKey',
754
+ 'secret',
755
+ 'token',
756
+ 'connectionString',
757
+ 'connectionStrings'
758
+ ];
759
+
760
+ for (const field of sensitiveFields) {
761
+ if (field in sanitized) {
762
+ sanitized[field] = '***REDACTED***';
763
+ }
764
+ }
765
+
766
+ return sanitized;
767
+ }
768
+ }