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,853 @@
1
+ import { BaseCloudDriver } from './base-driver.js';
2
+
3
+ /**
4
+ * Production-ready Alibaba Cloud (Aliyun) inventory driver using @alicloud SDK.
5
+ *
6
+ * Covers 15+ services with 40+ resource types:
7
+ * - Compute (ECS instances, ACK clusters, snapshots)
8
+ * - Storage (OSS buckets, disks)
9
+ * - Databases (RDS instances, Redis)
10
+ * - Networking (VPC, vSwitches, SLB, EIP, Security Groups, NAT Gateway)
11
+ * - Auto Scaling (Scaling Groups, Scaling Configurations)
12
+ * - Container Registry (ACR repositories)
13
+ * - CDN, DNS
14
+ *
15
+ * @see https://www.alibabacloud.com/help/doc-detail/57342.htm
16
+ * @see https://github.com/aliyun/aliyun-openapi-nodejs-sdk
17
+ */
18
+ export class AlibabaInventoryDriver extends BaseCloudDriver {
19
+ constructor(options = {}) {
20
+ super({ ...options, driver: options.driver || 'alibaba' });
21
+
22
+ this._accessKeyId = null;
23
+ this._accessKeySecret = null;
24
+ this._accountId = this.config?.accountId || 'alibaba';
25
+
26
+ // Services to collect (can be filtered via config.services)
27
+ this._services = this.config?.services || [
28
+ 'ecs',
29
+ 'ack',
30
+ 'oss',
31
+ 'rds',
32
+ 'redis',
33
+ 'vpc',
34
+ 'slb',
35
+ 'eip',
36
+ 'cdn',
37
+ 'dns',
38
+ 'securitygroups',
39
+ 'snapshots',
40
+ 'autoscaling',
41
+ 'natgateway',
42
+ 'acr'
43
+ ];
44
+
45
+ // Regions to scan (can be filtered via config.regions)
46
+ this._regions = this.config?.regions || ['cn-hangzhou', 'cn-shanghai', 'cn-beijing'];
47
+ }
48
+
49
+ /**
50
+ * Initialize Alibaba Cloud credentials.
51
+ */
52
+ async _initializeCredentials() {
53
+ if (this._accessKeyId) return;
54
+
55
+ const credentials = this.credentials || {};
56
+ this._accessKeyId = credentials.accessKeyId || process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
57
+ this._accessKeySecret = credentials.accessKeySecret || process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET;
58
+
59
+ if (!this._accessKeyId || !this._accessKeySecret) {
60
+ throw new Error('Alibaba Cloud AccessKeyId and AccessKeySecret are required. Provide via credentials or env vars.');
61
+ }
62
+
63
+ this.logger('info', 'Alibaba Cloud credentials initialized', {
64
+ accountId: this._accountId,
65
+ services: this._services.length,
66
+ regions: this._regions.length
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Create RPC client for a specific service.
72
+ */
73
+ async _createRPCClient(endpoint, apiVersion) {
74
+ const RPCClient = await import('@alicloud/pop-core');
75
+
76
+ return new RPCClient.default({
77
+ accessKeyId: this._accessKeyId,
78
+ accessKeySecret: this._accessKeySecret,
79
+ endpoint,
80
+ apiVersion
81
+ });
82
+ }
83
+
84
+ /**
85
+ * Main entry point - lists all resources from configured services.
86
+ */
87
+ async *listResources(options = {}) {
88
+ await this._initializeCredentials();
89
+
90
+ const serviceCollectors = {
91
+ ecs: () => this._collectECS(),
92
+ ack: () => this._collectACK(),
93
+ oss: () => this._collectOSS(),
94
+ rds: () => this._collectRDS(),
95
+ redis: () => this._collectRedis(),
96
+ vpc: () => this._collectVPC(),
97
+ slb: () => this._collectSLB(),
98
+ eip: () => this._collectEIP(),
99
+ cdn: () => this._collectCDN(),
100
+ dns: () => this._collectDNS(),
101
+ securitygroups: () => this._collectSecurityGroups(),
102
+ snapshots: () => this._collectSnapshots(),
103
+ autoscaling: () => this._collectAutoScaling(),
104
+ natgateway: () => this._collectNATGateway(),
105
+ acr: () => this._collectACR()
106
+ };
107
+
108
+ for (const service of this._services) {
109
+ const collector = serviceCollectors[service];
110
+ if (!collector) {
111
+ this.logger('warn', `Unknown Alibaba Cloud service: ${service}`, { service });
112
+ continue;
113
+ }
114
+
115
+ try {
116
+ this.logger('info', `Collecting Alibaba Cloud ${service} resources`, { service });
117
+ yield* collector();
118
+ } catch (err) {
119
+ // Continue with next service instead of failing entire sync
120
+ this.logger('error', `Alibaba Cloud service collection failed, skipping to next service`, {
121
+ service,
122
+ error: err.message,
123
+ errorName: err.name,
124
+ stack: err.stack
125
+ });
126
+ }
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Collect ECS instances.
132
+ */
133
+ async *_collectECS() {
134
+ try {
135
+ for (const region of this._regions) {
136
+ const client = await this._createRPCClient(`https://ecs.${region}.aliyuncs.com`, '2014-05-26');
137
+
138
+ const params = {
139
+ RegionId: region,
140
+ PageSize: 100
141
+ };
142
+
143
+ const response = await client.request('DescribeInstances', params, { method: 'POST' });
144
+ const instances = response.Instances?.Instance || [];
145
+
146
+ for (const instance of instances) {
147
+ yield {
148
+ provider: 'alibaba',
149
+ accountId: this._accountId,
150
+ region,
151
+ service: 'ecs',
152
+ resourceType: 'alibaba.ecs.instance',
153
+ resourceId: instance.InstanceId,
154
+ name: instance.InstanceName,
155
+ tags: this._extractTags(instance.Tags?.Tag),
156
+ configuration: this._sanitize(instance)
157
+ };
158
+ }
159
+
160
+ this.logger('info', `Collected ${instances.length} ECS instances in ${region}`);
161
+ }
162
+ } catch (err) {
163
+ this.logger('error', 'Failed to collect Alibaba Cloud ECS', {
164
+ error: err.message,
165
+ stack: err.stack
166
+ });
167
+ throw err;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Collect ACK (Container Service for Kubernetes) clusters.
173
+ */
174
+ async *_collectACK() {
175
+ try {
176
+ for (const region of this._regions) {
177
+ const client = await this._createRPCClient(`https://cs.${region}.aliyuncs.com`, '2015-12-15');
178
+
179
+ try {
180
+ const response = await client.request('DescribeClustersV1', {}, { method: 'GET' });
181
+ const clusters = response.clusters || [];
182
+
183
+ for (const cluster of clusters) {
184
+ yield {
185
+ provider: 'alibaba',
186
+ accountId: this._accountId,
187
+ region,
188
+ service: 'ack',
189
+ resourceType: 'alibaba.ack.cluster',
190
+ resourceId: cluster.cluster_id,
191
+ name: cluster.name,
192
+ tags: this._extractTags(cluster.tags),
193
+ configuration: this._sanitize(cluster)
194
+ };
195
+ }
196
+
197
+ this.logger('info', `Collected ${clusters.length} ACK clusters in ${region}`);
198
+ } catch (regionErr) {
199
+ // ACK may not be available in all regions
200
+ this.logger('debug', `ACK not available in ${region}`, { region, error: regionErr.message });
201
+ }
202
+ }
203
+ } catch (err) {
204
+ this.logger('error', 'Failed to collect Alibaba Cloud ACK', {
205
+ error: err.message,
206
+ stack: err.stack
207
+ });
208
+ throw err;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Collect OSS buckets.
214
+ */
215
+ async *_collectOSS() {
216
+ try {
217
+ const OSS = await import('ali-oss');
218
+
219
+ // OSS client uses a different pattern
220
+ const ossClient = new OSS.default({
221
+ accessKeyId: this._accessKeyId,
222
+ accessKeySecret: this._accessKeySecret,
223
+ region: this._regions[0] // Use first region as default
224
+ });
225
+
226
+ const response = await ossClient.listBuckets();
227
+ const buckets = response.buckets || [];
228
+
229
+ for (const bucket of buckets) {
230
+ yield {
231
+ provider: 'alibaba',
232
+ accountId: this._accountId,
233
+ region: bucket.region,
234
+ service: 'oss',
235
+ resourceType: 'alibaba.oss.bucket',
236
+ resourceId: bucket.name,
237
+ name: bucket.name,
238
+ tags: {},
239
+ configuration: this._sanitize(bucket)
240
+ };
241
+ }
242
+
243
+ this.logger('info', `Collected ${buckets.length} OSS buckets`);
244
+ } catch (err) {
245
+ this.logger('error', 'Failed to collect Alibaba Cloud OSS', {
246
+ error: err.message,
247
+ stack: err.stack
248
+ });
249
+ throw err;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Collect RDS instances.
255
+ */
256
+ async *_collectRDS() {
257
+ try {
258
+ for (const region of this._regions) {
259
+ const client = await this._createRPCClient(`https://rds.${region}.aliyuncs.com`, '2014-08-15');
260
+
261
+ const params = {
262
+ RegionId: region,
263
+ PageSize: 100
264
+ };
265
+
266
+ const response = await client.request('DescribeDBInstances', params, { method: 'POST' });
267
+ const instances = response.Items?.DBInstance || [];
268
+
269
+ for (const instance of instances) {
270
+ yield {
271
+ provider: 'alibaba',
272
+ accountId: this._accountId,
273
+ region,
274
+ service: 'rds',
275
+ resourceType: 'alibaba.rds.instance',
276
+ resourceId: instance.DBInstanceId,
277
+ name: instance.DBInstanceDescription || instance.DBInstanceId,
278
+ tags: this._extractTags(instance.Tags?.Tag),
279
+ configuration: this._sanitize(instance)
280
+ };
281
+ }
282
+
283
+ this.logger('info', `Collected ${instances.length} RDS instances in ${region}`);
284
+ }
285
+ } catch (err) {
286
+ this.logger('error', 'Failed to collect Alibaba Cloud RDS', {
287
+ error: err.message,
288
+ stack: err.stack
289
+ });
290
+ throw err;
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Collect Redis instances.
296
+ */
297
+ async *_collectRedis() {
298
+ try {
299
+ for (const region of this._regions) {
300
+ const client = await this._createRPCClient(`https://r-kvstore.${region}.aliyuncs.com`, '2015-01-01');
301
+
302
+ const params = {
303
+ RegionId: region,
304
+ PageSize: 100
305
+ };
306
+
307
+ const response = await client.request('DescribeInstances', params, { method: 'POST' });
308
+ const instances = response.Instances?.KVStoreInstance || [];
309
+
310
+ for (const instance of instances) {
311
+ yield {
312
+ provider: 'alibaba',
313
+ accountId: this._accountId,
314
+ region,
315
+ service: 'redis',
316
+ resourceType: 'alibaba.redis.instance',
317
+ resourceId: instance.InstanceId,
318
+ name: instance.InstanceName,
319
+ tags: this._extractTags(instance.Tags?.Tag),
320
+ configuration: this._sanitize(instance)
321
+ };
322
+ }
323
+
324
+ this.logger('info', `Collected ${instances.length} Redis instances in ${region}`);
325
+ }
326
+ } catch (err) {
327
+ this.logger('error', 'Failed to collect Alibaba Cloud Redis', {
328
+ error: err.message,
329
+ stack: err.stack
330
+ });
331
+ throw err;
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Collect VPC resources.
337
+ */
338
+ async *_collectVPC() {
339
+ try {
340
+ for (const region of this._regions) {
341
+ const client = await this._createRPCClient(`https://vpc.${region}.aliyuncs.com`, '2016-04-28');
342
+
343
+ // VPCs
344
+ const vpcParams = {
345
+ RegionId: region,
346
+ PageSize: 50
347
+ };
348
+
349
+ const vpcResponse = await client.request('DescribeVpcs', vpcParams, { method: 'POST' });
350
+ const vpcs = vpcResponse.Vpcs?.Vpc || [];
351
+
352
+ for (const vpc of vpcs) {
353
+ yield {
354
+ provider: 'alibaba',
355
+ accountId: this._accountId,
356
+ region,
357
+ service: 'vpc',
358
+ resourceType: 'alibaba.vpc.network',
359
+ resourceId: vpc.VpcId,
360
+ name: vpc.VpcName,
361
+ tags: this._extractTags(vpc.Tags?.Tag),
362
+ configuration: this._sanitize(vpc)
363
+ };
364
+
365
+ // vSwitches (subnets)
366
+ try {
367
+ const vswitchParams = {
368
+ VpcId: vpc.VpcId,
369
+ PageSize: 50
370
+ };
371
+
372
+ const vswitchResponse = await client.request('DescribeVSwitches', vswitchParams, { method: 'POST' });
373
+ const vswitches = vswitchResponse.VSwitches?.VSwitch || [];
374
+
375
+ for (const vswitch of vswitches) {
376
+ yield {
377
+ provider: 'alibaba',
378
+ accountId: this._accountId,
379
+ region,
380
+ service: 'vpc',
381
+ resourceType: 'alibaba.vpc.vswitch',
382
+ resourceId: vswitch.VSwitchId,
383
+ name: vswitch.VSwitchName,
384
+ tags: this._extractTags(vswitch.Tags?.Tag),
385
+ metadata: { vpcId: vpc.VpcId, vpcName: vpc.VpcName },
386
+ configuration: this._sanitize(vswitch)
387
+ };
388
+ }
389
+ } catch (vswitchErr) {
390
+ this.logger('warn', `Failed to collect vSwitches for VPC ${vpc.VpcId}`, {
391
+ vpcId: vpc.VpcId,
392
+ error: vswitchErr.message
393
+ });
394
+ }
395
+ }
396
+
397
+ this.logger('info', `Collected ${vpcs.length} VPCs in ${region}`);
398
+ }
399
+ } catch (err) {
400
+ this.logger('error', 'Failed to collect Alibaba Cloud VPC', {
401
+ error: err.message,
402
+ stack: err.stack
403
+ });
404
+ throw err;
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Collect SLB (Server Load Balancer) instances.
410
+ */
411
+ async *_collectSLB() {
412
+ try {
413
+ for (const region of this._regions) {
414
+ const client = await this._createRPCClient(`https://slb.${region}.aliyuncs.com`, '2014-05-15');
415
+
416
+ const params = {
417
+ RegionId: region,
418
+ PageSize: 50
419
+ };
420
+
421
+ const response = await client.request('DescribeLoadBalancers', params, { method: 'POST' });
422
+ const loadBalancers = response.LoadBalancers?.LoadBalancer || [];
423
+
424
+ for (const lb of loadBalancers) {
425
+ yield {
426
+ provider: 'alibaba',
427
+ accountId: this._accountId,
428
+ region,
429
+ service: 'slb',
430
+ resourceType: 'alibaba.slb.loadbalancer',
431
+ resourceId: lb.LoadBalancerId,
432
+ name: lb.LoadBalancerName,
433
+ tags: this._extractTags(lb.Tags?.Tag),
434
+ configuration: this._sanitize(lb)
435
+ };
436
+ }
437
+
438
+ this.logger('info', `Collected ${loadBalancers.length} SLB instances in ${region}`);
439
+ }
440
+ } catch (err) {
441
+ this.logger('error', 'Failed to collect Alibaba Cloud SLB', {
442
+ error: err.message,
443
+ stack: err.stack
444
+ });
445
+ throw err;
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Collect EIP (Elastic IP) addresses.
451
+ */
452
+ async *_collectEIP() {
453
+ try {
454
+ for (const region of this._regions) {
455
+ const client = await this._createRPCClient(`https://vpc.${region}.aliyuncs.com`, '2016-04-28');
456
+
457
+ const params = {
458
+ RegionId: region,
459
+ PageSize: 50
460
+ };
461
+
462
+ const response = await client.request('DescribeEipAddresses', params, { method: 'POST' });
463
+ const eips = response.EipAddresses?.EipAddress || [];
464
+
465
+ for (const eip of eips) {
466
+ yield {
467
+ provider: 'alibaba',
468
+ accountId: this._accountId,
469
+ region,
470
+ service: 'eip',
471
+ resourceType: 'alibaba.eip',
472
+ resourceId: eip.AllocationId,
473
+ name: eip.Name || eip.IpAddress,
474
+ tags: this._extractTags(eip.Tags?.Tag),
475
+ configuration: this._sanitize(eip)
476
+ };
477
+ }
478
+
479
+ this.logger('info', `Collected ${eips.length} EIPs in ${region}`);
480
+ }
481
+ } catch (err) {
482
+ this.logger('error', 'Failed to collect Alibaba Cloud EIP', {
483
+ error: err.message,
484
+ stack: err.stack
485
+ });
486
+ throw err;
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Collect CDN domains.
492
+ */
493
+ async *_collectCDN() {
494
+ try {
495
+ // CDN is global, not region-specific
496
+ const client = await this._createRPCClient('https://cdn.aliyuncs.com', '2018-05-10');
497
+
498
+ const params = {
499
+ PageSize: 50
500
+ };
501
+
502
+ const response = await client.request('DescribeUserDomains', params, { method: 'POST' });
503
+ const domains = response.Domains?.PageData || [];
504
+
505
+ for (const domain of domains) {
506
+ yield {
507
+ provider: 'alibaba',
508
+ accountId: this._accountId,
509
+ region: null, // CDN is global
510
+ service: 'cdn',
511
+ resourceType: 'alibaba.cdn.domain',
512
+ resourceId: domain.DomainName,
513
+ name: domain.DomainName,
514
+ tags: {},
515
+ configuration: this._sanitize(domain)
516
+ };
517
+ }
518
+
519
+ this.logger('info', `Collected ${domains.length} CDN domains`);
520
+ } catch (err) {
521
+ this.logger('error', 'Failed to collect Alibaba Cloud CDN', {
522
+ error: err.message,
523
+ stack: err.stack
524
+ });
525
+ throw err;
526
+ }
527
+ }
528
+
529
+ /**
530
+ * Collect DNS domains.
531
+ */
532
+ async *_collectDNS() {
533
+ try {
534
+ // DNS is global, not region-specific
535
+ const client = await this._createRPCClient('https://alidns.aliyuncs.com', '2015-01-09');
536
+
537
+ const params = {
538
+ PageSize: 100
539
+ };
540
+
541
+ const response = await client.request('DescribeDomains', params, { method: 'POST' });
542
+ const domains = response.Domains?.Domain || [];
543
+
544
+ for (const domain of domains) {
545
+ yield {
546
+ provider: 'alibaba',
547
+ accountId: this._accountId,
548
+ region: null, // DNS is global
549
+ service: 'dns',
550
+ resourceType: 'alibaba.dns.domain',
551
+ resourceId: domain.DomainId,
552
+ name: domain.DomainName,
553
+ tags: this._extractTags(domain.Tags?.Tag),
554
+ configuration: this._sanitize(domain)
555
+ };
556
+ }
557
+
558
+ this.logger('info', `Collected ${domains.length} DNS domains`);
559
+ } catch (err) {
560
+ this.logger('error', 'Failed to collect Alibaba Cloud DNS', {
561
+ error: err.message,
562
+ stack: err.stack
563
+ });
564
+ throw err;
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Collect Security Groups.
570
+ */
571
+ async *_collectSecurityGroups() {
572
+ try {
573
+ for (const region of this._regions) {
574
+ const client = await this._createRPCClient(`https://ecs.${region}.aliyuncs.com`, '2014-05-26');
575
+
576
+ const params = {
577
+ RegionId: region,
578
+ PageSize: 50
579
+ };
580
+
581
+ const response = await client.request('DescribeSecurityGroups', params, { method: 'POST' });
582
+ const securityGroups = response.SecurityGroups?.SecurityGroup || [];
583
+
584
+ for (const sg of securityGroups) {
585
+ yield {
586
+ provider: 'alibaba',
587
+ accountId: this._accountId,
588
+ region,
589
+ service: 'securitygroups',
590
+ resourceType: 'alibaba.ecs.securitygroup',
591
+ resourceId: sg.SecurityGroupId,
592
+ name: sg.SecurityGroupName,
593
+ tags: this._extractTags(sg.Tags?.Tag),
594
+ metadata: { vpcId: sg.VpcId },
595
+ configuration: this._sanitize(sg)
596
+ };
597
+ }
598
+
599
+ this.logger('info', `Collected ${securityGroups.length} security groups in ${region}`);
600
+ }
601
+ } catch (err) {
602
+ this.logger('error', 'Failed to collect Alibaba Cloud security groups', {
603
+ error: err.message,
604
+ stack: err.stack
605
+ });
606
+ throw err;
607
+ }
608
+ }
609
+
610
+ /**
611
+ * Collect Disk Snapshots.
612
+ */
613
+ async *_collectSnapshots() {
614
+ try {
615
+ for (const region of this._regions) {
616
+ const client = await this._createRPCClient(`https://ecs.${region}.aliyuncs.com`, '2014-05-26');
617
+
618
+ const params = {
619
+ RegionId: region,
620
+ PageSize: 50
621
+ };
622
+
623
+ const response = await client.request('DescribeSnapshots', params, { method: 'POST' });
624
+ const snapshots = response.Snapshots?.Snapshot || [];
625
+
626
+ for (const snapshot of snapshots) {
627
+ yield {
628
+ provider: 'alibaba',
629
+ accountId: this._accountId,
630
+ region,
631
+ service: 'snapshots',
632
+ resourceType: 'alibaba.ecs.snapshot',
633
+ resourceId: snapshot.SnapshotId,
634
+ name: snapshot.SnapshotName || snapshot.SnapshotId,
635
+ tags: this._extractTags(snapshot.Tags?.Tag),
636
+ metadata: { sourceDiskId: snapshot.SourceDiskId },
637
+ configuration: this._sanitize(snapshot)
638
+ };
639
+ }
640
+
641
+ this.logger('info', `Collected ${snapshots.length} snapshots in ${region}`);
642
+ }
643
+ } catch (err) {
644
+ this.logger('error', 'Failed to collect Alibaba Cloud snapshots', {
645
+ error: err.message,
646
+ stack: err.stack
647
+ });
648
+ throw err;
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Collect Auto Scaling Groups.
654
+ */
655
+ async *_collectAutoScaling() {
656
+ try {
657
+ for (const region of this._regions) {
658
+ const client = await this._createRPCClient(`https://ess.${region}.aliyuncs.com`, '2014-08-28');
659
+
660
+ const params = {
661
+ RegionId: region,
662
+ PageSize: 50
663
+ };
664
+
665
+ // Collect Scaling Groups
666
+ const response = await client.request('DescribeScalingGroups', params, { method: 'POST' });
667
+ const scalingGroups = response.ScalingGroups?.ScalingGroup || [];
668
+
669
+ for (const group of scalingGroups) {
670
+ yield {
671
+ provider: 'alibaba',
672
+ accountId: this._accountId,
673
+ region,
674
+ service: 'autoscaling',
675
+ resourceType: 'alibaba.ess.scalinggroup',
676
+ resourceId: group.ScalingGroupId,
677
+ name: group.ScalingGroupName,
678
+ tags: this._extractTags(group.Tags?.Tag),
679
+ configuration: this._sanitize(group)
680
+ };
681
+
682
+ // Collect Scaling Configurations for this group
683
+ try {
684
+ const configParams = {
685
+ ScalingGroupId: group.ScalingGroupId,
686
+ PageSize: 50
687
+ };
688
+ const configResponse = await client.request('DescribeScalingConfigurations', configParams, { method: 'POST' });
689
+ const configurations = configResponse.ScalingConfigurations?.ScalingConfiguration || [];
690
+
691
+ for (const config of configurations) {
692
+ yield {
693
+ provider: 'alibaba',
694
+ accountId: this._accountId,
695
+ region,
696
+ service: 'autoscaling',
697
+ resourceType: 'alibaba.ess.scalingconfiguration',
698
+ resourceId: config.ScalingConfigurationId,
699
+ name: config.ScalingConfigurationName,
700
+ tags: {},
701
+ metadata: { scalingGroupId: group.ScalingGroupId },
702
+ configuration: this._sanitize(config)
703
+ };
704
+ }
705
+ } catch (configErr) {
706
+ this.logger('warn', `Failed to collect scaling configurations for group ${group.ScalingGroupId}`, {
707
+ error: configErr.message
708
+ });
709
+ }
710
+ }
711
+
712
+ this.logger('info', `Collected ${scalingGroups.length} auto scaling groups in ${region}`);
713
+ }
714
+ } catch (err) {
715
+ this.logger('error', 'Failed to collect Alibaba Cloud auto scaling', {
716
+ error: err.message,
717
+ stack: err.stack
718
+ });
719
+ throw err;
720
+ }
721
+ }
722
+
723
+ /**
724
+ * Collect NAT Gateways.
725
+ */
726
+ async *_collectNATGateway() {
727
+ try {
728
+ for (const region of this._regions) {
729
+ const client = await this._createRPCClient(`https://vpc.${region}.aliyuncs.com`, '2016-04-28');
730
+
731
+ const params = {
732
+ RegionId: region,
733
+ PageSize: 50
734
+ };
735
+
736
+ const response = await client.request('DescribeNatGateways', params, { method: 'POST' });
737
+ const natGateways = response.NatGateways?.NatGateway || [];
738
+
739
+ for (const nat of natGateways) {
740
+ yield {
741
+ provider: 'alibaba',
742
+ accountId: this._accountId,
743
+ region,
744
+ service: 'natgateway',
745
+ resourceType: 'alibaba.vpc.natgateway',
746
+ resourceId: nat.NatGatewayId,
747
+ name: nat.Name || nat.NatGatewayId,
748
+ tags: this._extractTags(nat.Tags?.Tag),
749
+ metadata: { vpcId: nat.VpcId },
750
+ configuration: this._sanitize(nat)
751
+ };
752
+ }
753
+
754
+ this.logger('info', `Collected ${natGateways.length} NAT gateways in ${region}`);
755
+ }
756
+ } catch (err) {
757
+ this.logger('error', 'Failed to collect Alibaba Cloud NAT gateways', {
758
+ error: err.message,
759
+ stack: err.stack
760
+ });
761
+ throw err;
762
+ }
763
+ }
764
+
765
+ /**
766
+ * Collect Container Registry (ACR) repositories.
767
+ */
768
+ async *_collectACR() {
769
+ try {
770
+ // ACR API is region-specific but uses different endpoint pattern
771
+ for (const region of this._regions) {
772
+ const client = await this._createRPCClient(`https://cr.${region}.aliyuncs.com`, '2018-12-01');
773
+
774
+ const params = {
775
+ RegionId: region,
776
+ PageSize: 50
777
+ };
778
+
779
+ try {
780
+ const response = await client.request('ListRepository', params, { method: 'POST' });
781
+ const repositories = response.Repositories?.Repository || [];
782
+
783
+ for (const repo of repositories) {
784
+ yield {
785
+ provider: 'alibaba',
786
+ accountId: this._accountId,
787
+ region,
788
+ service: 'acr',
789
+ resourceType: 'alibaba.acr.repository',
790
+ resourceId: repo.RepoId || `${repo.RepoNamespace}/${repo.RepoName}`,
791
+ name: `${repo.RepoNamespace}/${repo.RepoName}`,
792
+ tags: {},
793
+ configuration: this._sanitize(repo)
794
+ };
795
+ }
796
+
797
+ this.logger('info', `Collected ${repositories.length} ACR repositories in ${region}`);
798
+ } catch (regionErr) {
799
+ // ACR might not be available in all regions
800
+ this.logger('debug', `ACR not available or no repositories in ${region}`, {
801
+ error: regionErr.message
802
+ });
803
+ }
804
+ }
805
+ } catch (err) {
806
+ this.logger('error', 'Failed to collect Alibaba Cloud ACR', {
807
+ error: err.message,
808
+ stack: err.stack
809
+ });
810
+ throw err;
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Extract tags from Alibaba Cloud tag format.
816
+ */
817
+ _extractTags(tags) {
818
+ if (!tags || !Array.isArray(tags)) return {};
819
+
820
+ const tagMap = {};
821
+ for (const tag of tags) {
822
+ if (tag.TagKey) {
823
+ tagMap[tag.TagKey] = tag.TagValue || '';
824
+ }
825
+ }
826
+ return tagMap;
827
+ }
828
+
829
+ /**
830
+ * Sanitize configuration by removing sensitive data.
831
+ */
832
+ _sanitize(config) {
833
+ if (!config || typeof config !== 'object') return config;
834
+
835
+ const sanitized = { ...config };
836
+ const sensitiveFields = [
837
+ 'Password',
838
+ 'MasterUserPassword',
839
+ 'AccessKeySecret',
840
+ 'SecretAccessKey',
841
+ 'PrivateKey',
842
+ 'Certificate'
843
+ ];
844
+
845
+ for (const field of sensitiveFields) {
846
+ if (field in sanitized) {
847
+ sanitized[field] = '***REDACTED***';
848
+ }
849
+ }
850
+
851
+ return sanitized;
852
+ }
853
+ }