s3db.js 13.5.1 → 13.6.1
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 +89 -19
- package/dist/{s3db.cjs.js → s3db.cjs} +29780 -24384
- package/dist/s3db.cjs.map +1 -0
- package/dist/s3db.es.js +24263 -18860
- package/dist/s3db.es.js.map +1 -1
- package/package.json +227 -21
- package/src/concerns/id.js +90 -6
- package/src/concerns/index.js +2 -1
- package/src/concerns/password-hashing.js +150 -0
- package/src/database.class.js +4 -0
- package/src/plugins/api/auth/basic-auth.js +23 -1
- package/src/plugins/api/auth/index.js +49 -3
- package/src/plugins/api/auth/oauth2-auth.js +171 -0
- package/src/plugins/api/auth/oidc-auth.js +789 -0
- package/src/plugins/api/auth/oidc-client.js +462 -0
- package/src/plugins/api/auth/path-auth-matcher.js +284 -0
- package/src/plugins/api/concerns/event-emitter.js +134 -0
- package/src/plugins/api/concerns/failban-manager.js +651 -0
- package/src/plugins/api/concerns/guards-helpers.js +402 -0
- package/src/plugins/api/concerns/metrics-collector.js +346 -0
- package/src/plugins/api/concerns/opengraph-helper.js +116 -0
- package/src/plugins/api/concerns/state-machine.js +288 -0
- package/src/plugins/api/index.js +514 -54
- package/src/plugins/api/middlewares/failban.js +305 -0
- package/src/plugins/api/middlewares/rate-limit.js +301 -0
- package/src/plugins/api/middlewares/request-id.js +74 -0
- package/src/plugins/api/middlewares/security-headers.js +120 -0
- package/src/plugins/api/middlewares/session-tracking.js +194 -0
- package/src/plugins/api/routes/auth-routes.js +23 -3
- package/src/plugins/api/routes/resource-routes.js +71 -29
- package/src/plugins/api/server.js +1017 -94
- package/src/plugins/api/utils/guards.js +213 -0
- package/src/plugins/api/utils/mime-types.js +154 -0
- package/src/plugins/api/utils/openapi-generator.js +44 -11
- package/src/plugins/api/utils/path-matcher.js +173 -0
- package/src/plugins/api/utils/static-filesystem.js +262 -0
- package/src/plugins/api/utils/static-s3.js +231 -0
- package/src/plugins/api/utils/template-engine.js +262 -0
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
- package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
- package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
- package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
- package/src/plugins/cloud-inventory/index.js +20 -0
- package/src/plugins/cloud-inventory/registry.js +146 -0
- package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
- package/src/plugins/cloud-inventory.plugin.js +1333 -0
- package/src/plugins/concerns/plugin-dependencies.js +61 -1
- package/src/plugins/eventual-consistency/analytics.js +1 -0
- package/src/plugins/identity/README.md +335 -0
- package/src/plugins/identity/concerns/mfa-manager.js +204 -0
- package/src/plugins/identity/concerns/password.js +138 -0
- package/src/plugins/identity/concerns/resource-schemas.js +273 -0
- package/src/plugins/identity/concerns/token-generator.js +172 -0
- package/src/plugins/identity/email-service.js +422 -0
- package/src/plugins/identity/index.js +1052 -0
- package/src/plugins/identity/oauth2-server.js +1033 -0
- package/src/plugins/identity/oidc-discovery.js +285 -0
- package/src/plugins/identity/rsa-keys.js +323 -0
- package/src/plugins/identity/server.js +500 -0
- package/src/plugins/identity/session-manager.js +453 -0
- package/src/plugins/identity/ui/layouts/base.js +251 -0
- package/src/plugins/identity/ui/middleware.js +135 -0
- package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
- package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
- package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
- package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
- package/src/plugins/identity/ui/pages/admin/users.js +263 -0
- package/src/plugins/identity/ui/pages/consent.js +262 -0
- package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
- package/src/plugins/identity/ui/pages/login.js +144 -0
- package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
- package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
- package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
- package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
- package/src/plugins/identity/ui/pages/profile.js +361 -0
- package/src/plugins/identity/ui/pages/register.js +226 -0
- package/src/plugins/identity/ui/pages/reset-password.js +128 -0
- package/src/plugins/identity/ui/pages/verify-email.js +172 -0
- package/src/plugins/identity/ui/routes.js +2541 -0
- package/src/plugins/identity/ui/styles/main.css +465 -0
- package/src/plugins/index.js +4 -1
- package/src/plugins/ml/base-model.class.js +32 -7
- package/src/plugins/ml/classification-model.class.js +1 -1
- package/src/plugins/ml/timeseries-model.class.js +3 -1
- package/src/plugins/ml.plugin.js +124 -32
- package/src/plugins/shared/error-handler.js +147 -0
- package/src/plugins/shared/index.js +9 -0
- package/src/plugins/shared/middlewares/compression.js +117 -0
- package/src/plugins/shared/middlewares/cors.js +49 -0
- package/src/plugins/shared/middlewares/index.js +11 -0
- package/src/plugins/shared/middlewares/logging.js +54 -0
- package/src/plugins/shared/middlewares/rate-limit.js +73 -0
- package/src/plugins/shared/middlewares/security.js +158 -0
- package/src/plugins/shared/response-formatter.js +264 -0
- package/src/plugins/tfstate/README.md +126 -126
- package/src/resource.class.js +140 -12
- package/src/schema.class.js +30 -1
- package/src/validator.class.js +57 -6
- package/dist/s3db.cjs.js.map +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { BaseCloudDriver } from './drivers/base-driver.js';
|
|
2
|
+
import { AwsInventoryDriver } from './drivers/aws-driver.js';
|
|
3
|
+
import { GcpInventoryDriver } from './drivers/gcp-driver.js';
|
|
4
|
+
import { VultrInventoryDriver } from './drivers/vultr-driver.js';
|
|
5
|
+
import { DigitalOceanInventoryDriver } from './drivers/digitalocean-driver.js';
|
|
6
|
+
import { OracleInventoryDriver } from './drivers/oracle-driver.js';
|
|
7
|
+
import { AzureInventoryDriver } from './drivers/azure-driver.js';
|
|
8
|
+
import { LinodeInventoryDriver } from './drivers/linode-driver.js';
|
|
9
|
+
import { HetznerInventoryDriver } from './drivers/hetzner-driver.js';
|
|
10
|
+
import { AlibabaInventoryDriver } from './drivers/alibaba-driver.js';
|
|
11
|
+
import { CloudflareInventoryDriver } from './drivers/cloudflare-driver.js';
|
|
12
|
+
import { MongoDBAtlasInventoryDriver } from './drivers/mongodb-atlas-driver.js';
|
|
13
|
+
import {
|
|
14
|
+
AwsMockDriver,
|
|
15
|
+
GcpMockDriver,
|
|
16
|
+
DigitalOceanMockDriver,
|
|
17
|
+
OracleMockDriver,
|
|
18
|
+
AzureMockDriver,
|
|
19
|
+
VultrMockDriver,
|
|
20
|
+
LinodeMockDriver,
|
|
21
|
+
HetznerMockDriver,
|
|
22
|
+
AlibabaMockDriver,
|
|
23
|
+
CloudflareMockDriver,
|
|
24
|
+
MongoDBAtlasMockDriver
|
|
25
|
+
} from './drivers/mock-drivers.js';
|
|
26
|
+
|
|
27
|
+
const CLOUD_DRIVERS = new Map();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register a cloud inventory driver.
|
|
31
|
+
* @param {string} name
|
|
32
|
+
* @param {Function} factory - (options) => BaseCloudDriver instance
|
|
33
|
+
*/
|
|
34
|
+
export function registerCloudDriver(name, factory) {
|
|
35
|
+
if (!name || typeof name !== 'string') {
|
|
36
|
+
throw new Error('registerCloudDriver: name must be a non-empty string');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof factory !== 'function') {
|
|
40
|
+
throw new Error(`registerCloudDriver("${name}") expects a factory function`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
CLOUD_DRIVERS.set(name, factory);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Instantiate a driver using the registry.
|
|
48
|
+
* @param {string} name
|
|
49
|
+
* @param {Object} options
|
|
50
|
+
* @returns {BaseCloudDriver}
|
|
51
|
+
*/
|
|
52
|
+
export function createCloudDriver(name, options) {
|
|
53
|
+
if (!CLOUD_DRIVERS.has(name)) {
|
|
54
|
+
throw new Error(`Cloud driver "${name}" is not registered. Registered drivers: ${[...CLOUD_DRIVERS.keys()].join(', ') || 'none'}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const driver = CLOUD_DRIVERS.get(name)(options);
|
|
58
|
+
if (!(driver instanceof BaseCloudDriver)) {
|
|
59
|
+
throw new Error(`Driver "${name}" factory must return an instance of BaseCloudDriver`);
|
|
60
|
+
}
|
|
61
|
+
return driver;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* List registered driver names.
|
|
66
|
+
* @returns {string[]}
|
|
67
|
+
*/
|
|
68
|
+
export function listCloudDrivers() {
|
|
69
|
+
return [...CLOUD_DRIVERS.keys()];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Utility for validating a cloud definition.
|
|
74
|
+
* Ensures driver exists and required fields are present.
|
|
75
|
+
* @param {Object} cloud
|
|
76
|
+
*/
|
|
77
|
+
export function validateCloudDefinition(cloud) {
|
|
78
|
+
if (!cloud || typeof cloud !== 'object') {
|
|
79
|
+
throw new Error('Each cloud configuration must be an object');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { driver, credentials } = cloud;
|
|
83
|
+
if (!driver || typeof driver !== 'string') {
|
|
84
|
+
throw new Error('Cloud configuration requires a "driver" string');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!CLOUD_DRIVERS.has(driver)) {
|
|
88
|
+
throw new Error(`Cloud driver "${driver}" is not registered`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!credentials || typeof credentials !== 'object') {
|
|
92
|
+
throw new Error(`Cloud "${driver}" requires a credentials object`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Register a no-op driver as a placeholder / mock implementation.
|
|
97
|
+
registerCloudDriver('noop', (options = {}) => {
|
|
98
|
+
class NoopDriver extends BaseCloudDriver {
|
|
99
|
+
async listResources() {
|
|
100
|
+
const { sampleResources = [] } = options.config || {};
|
|
101
|
+
return Array.isArray(sampleResources) ? sampleResources : [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return new NoopDriver({
|
|
105
|
+
...options,
|
|
106
|
+
driver: options.driver || 'noop'
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
function registerMockDriver(names, DriverClass) {
|
|
111
|
+
const list = Array.isArray(names) ? names : [names];
|
|
112
|
+
for (const name of list) {
|
|
113
|
+
registerCloudDriver(name, (options = {}) => new DriverClass(options));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
registerCloudDriver('aws', (options = {}) => new AwsInventoryDriver(options));
|
|
118
|
+
registerCloudDriver('gcp', (options = {}) => new GcpInventoryDriver(options));
|
|
119
|
+
registerCloudDriver('vultr', (options = {}) => new VultrInventoryDriver(options));
|
|
120
|
+
registerCloudDriver('digitalocean', (options = {}) => new DigitalOceanInventoryDriver(options));
|
|
121
|
+
registerCloudDriver('do', (options = {}) => new DigitalOceanInventoryDriver(options));
|
|
122
|
+
registerCloudDriver('oracle', (options = {}) => new OracleInventoryDriver(options));
|
|
123
|
+
registerCloudDriver('oci', (options = {}) => new OracleInventoryDriver(options));
|
|
124
|
+
registerCloudDriver('azure', (options = {}) => new AzureInventoryDriver(options));
|
|
125
|
+
registerCloudDriver('az', (options = {}) => new AzureInventoryDriver(options));
|
|
126
|
+
registerCloudDriver('linode', (options = {}) => new LinodeInventoryDriver(options));
|
|
127
|
+
registerCloudDriver('hetzner', (options = {}) => new HetznerInventoryDriver(options));
|
|
128
|
+
registerCloudDriver('alibaba', (options = {}) => new AlibabaInventoryDriver(options));
|
|
129
|
+
registerCloudDriver('aliyun', (options = {}) => new AlibabaInventoryDriver(options));
|
|
130
|
+
registerCloudDriver('cloudflare', (options = {}) => new CloudflareInventoryDriver(options));
|
|
131
|
+
registerCloudDriver('cf', (options = {}) => new CloudflareInventoryDriver(options));
|
|
132
|
+
registerCloudDriver('mongodb-atlas', (options = {}) => new MongoDBAtlasInventoryDriver(options));
|
|
133
|
+
registerCloudDriver('atlas', (options = {}) => new MongoDBAtlasInventoryDriver(options));
|
|
134
|
+
registerMockDriver('aws-mock', AwsMockDriver);
|
|
135
|
+
registerMockDriver('gcp-mock', GcpMockDriver);
|
|
136
|
+
registerMockDriver('vultr-mock', VultrMockDriver);
|
|
137
|
+
registerMockDriver(['digitalocean-mock', 'do-mock'], DigitalOceanMockDriver);
|
|
138
|
+
registerMockDriver(['oracle-mock', 'oci-mock'], OracleMockDriver);
|
|
139
|
+
registerMockDriver(['azure-mock', 'az-mock'], AzureMockDriver);
|
|
140
|
+
registerMockDriver('linode-mock', LinodeMockDriver);
|
|
141
|
+
registerMockDriver('hetzner-mock', HetznerMockDriver);
|
|
142
|
+
registerMockDriver(['alibaba-mock', 'aliyun-mock'], AlibabaMockDriver);
|
|
143
|
+
registerMockDriver(['cloudflare-mock', 'cf-mock'], CloudflareMockDriver);
|
|
144
|
+
registerMockDriver(['mongodb-atlas-mock', 'atlas-mock'], MongoDBAtlasMockDriver);
|
|
145
|
+
|
|
146
|
+
export { BaseCloudDriver } from './drivers/base-driver.js';
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terraform/OpenTofu State Exporter for Cloud Inventory
|
|
3
|
+
*
|
|
4
|
+
* Converts discovered cloud resources into Terraform/OpenTofu state format (.tfstate)
|
|
5
|
+
* Enables "brownfield to IaC" workflows - import existing infrastructure into Terraform management
|
|
6
|
+
*
|
|
7
|
+
* @module terraform-exporter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Map cloud resource types to Terraform resource types
|
|
12
|
+
* Format: 'provider.service.resourceType' → 'terraform_resource_type'
|
|
13
|
+
*/
|
|
14
|
+
const RESOURCE_TYPE_MAP = {
|
|
15
|
+
// AWS
|
|
16
|
+
'aws.ec2.instance': 'aws_instance',
|
|
17
|
+
'aws.s3.bucket': 'aws_s3_bucket',
|
|
18
|
+
'aws.rds.instance': 'aws_db_instance',
|
|
19
|
+
'aws.dynamodb.table': 'aws_dynamodb_table',
|
|
20
|
+
'aws.lambda.function': 'aws_lambda_function',
|
|
21
|
+
'aws.vpc.vpc': 'aws_vpc',
|
|
22
|
+
'aws.vpc.subnet': 'aws_subnet',
|
|
23
|
+
'aws.vpc.securitygroup': 'aws_security_group',
|
|
24
|
+
'aws.vpc.routetable': 'aws_route_table',
|
|
25
|
+
'aws.vpc.internetgateway': 'aws_internet_gateway',
|
|
26
|
+
'aws.vpc.natgateway': 'aws_nat_gateway',
|
|
27
|
+
'aws.elb.loadbalancer': 'aws_elb',
|
|
28
|
+
'aws.elbv2.loadbalancer': 'aws_lb',
|
|
29
|
+
'aws.elbv2.targetgroup': 'aws_lb_target_group',
|
|
30
|
+
'aws.iam.user': 'aws_iam_user',
|
|
31
|
+
'aws.iam.role': 'aws_iam_role',
|
|
32
|
+
'aws.iam.policy': 'aws_iam_policy',
|
|
33
|
+
'aws.kms.key': 'aws_kms_key',
|
|
34
|
+
'aws.secretsmanager.secret': 'aws_secretsmanager_secret',
|
|
35
|
+
'aws.ecs.cluster': 'aws_ecs_cluster',
|
|
36
|
+
'aws.ecs.service': 'aws_ecs_service',
|
|
37
|
+
'aws.ecs.taskdefinition': 'aws_ecs_task_definition',
|
|
38
|
+
'aws.eks.cluster': 'aws_eks_cluster',
|
|
39
|
+
'aws.eks.nodegroup': 'aws_eks_node_group',
|
|
40
|
+
'aws.ecr.repository': 'aws_ecr_repository',
|
|
41
|
+
'aws.route53.hostedzone': 'aws_route53_zone',
|
|
42
|
+
'aws.cloudfront.distribution': 'aws_cloudfront_distribution',
|
|
43
|
+
'aws.sqs.queue': 'aws_sqs_queue',
|
|
44
|
+
'aws.sns.topic': 'aws_sns_topic',
|
|
45
|
+
'aws.kinesis.stream': 'aws_kinesis_stream',
|
|
46
|
+
'aws.ebs.volume': 'aws_ebs_volume',
|
|
47
|
+
'aws.ebs.snapshot': 'aws_ebs_snapshot',
|
|
48
|
+
'aws.efs.filesystem': 'aws_efs_file_system',
|
|
49
|
+
'aws.elasticache.cluster': 'aws_elasticache_cluster',
|
|
50
|
+
|
|
51
|
+
// GCP
|
|
52
|
+
'gcp.compute.instance': 'google_compute_instance',
|
|
53
|
+
'gcp.storage.bucket': 'google_storage_bucket',
|
|
54
|
+
'gcp.sql.instance': 'google_sql_database_instance',
|
|
55
|
+
'gcp.gke.cluster': 'google_container_cluster',
|
|
56
|
+
'gcp.compute.network': 'google_compute_network',
|
|
57
|
+
'gcp.compute.subnetwork': 'google_compute_subnetwork',
|
|
58
|
+
'gcp.compute.firewall': 'google_compute_firewall',
|
|
59
|
+
'gcp.bigquery.dataset': 'google_bigquery_dataset',
|
|
60
|
+
'gcp.pubsub.topic': 'google_pubsub_topic',
|
|
61
|
+
'gcp.pubsub.subscription': 'google_pubsub_subscription',
|
|
62
|
+
'gcp.functions.function': 'google_cloudfunctions_function',
|
|
63
|
+
'gcp.run.service': 'google_cloud_run_service',
|
|
64
|
+
'gcp.iam.serviceaccount': 'google_service_account',
|
|
65
|
+
'gcp.kms.keyring': 'google_kms_key_ring',
|
|
66
|
+
'gcp.secretmanager.secret': 'google_secret_manager_secret',
|
|
67
|
+
|
|
68
|
+
// Azure
|
|
69
|
+
'azure.vm': 'azurerm_virtual_machine',
|
|
70
|
+
'azure.vm.vmss': 'azurerm_virtual_machine_scale_set',
|
|
71
|
+
'azure.storage.account': 'azurerm_storage_account',
|
|
72
|
+
'azure.sql.server': 'azurerm_sql_server',
|
|
73
|
+
'azure.sql.database': 'azurerm_sql_database',
|
|
74
|
+
'azure.aks.cluster': 'azurerm_kubernetes_cluster',
|
|
75
|
+
'azure.network.vnet': 'azurerm_virtual_network',
|
|
76
|
+
'azure.network.subnet': 'azurerm_subnet',
|
|
77
|
+
'azure.network.nsg': 'azurerm_network_security_group',
|
|
78
|
+
'azure.network.loadbalancer': 'azurerm_lb',
|
|
79
|
+
'azure.network.publicip': 'azurerm_public_ip',
|
|
80
|
+
'azure.containerregistry': 'azurerm_container_registry',
|
|
81
|
+
'azure.cosmosdb.account': 'azurerm_cosmosdb_account',
|
|
82
|
+
'azure.identity.userassigned': 'azurerm_user_assigned_identity',
|
|
83
|
+
'azure.dns.zone': 'azurerm_dns_zone',
|
|
84
|
+
|
|
85
|
+
// DigitalOcean
|
|
86
|
+
'do.droplet': 'digitalocean_droplet',
|
|
87
|
+
'do.kubernetes.cluster': 'digitalocean_kubernetes_cluster',
|
|
88
|
+
'do.volume': 'digitalocean_volume',
|
|
89
|
+
'do.loadbalancer': 'digitalocean_loadbalancer',
|
|
90
|
+
'do.firewall': 'digitalocean_firewall',
|
|
91
|
+
'do.vpc': 'digitalocean_vpc',
|
|
92
|
+
'do.spaces.bucket': 'digitalocean_spaces_bucket',
|
|
93
|
+
'do.database.cluster': 'digitalocean_database_cluster',
|
|
94
|
+
'do.domain': 'digitalocean_domain',
|
|
95
|
+
'do.cdn': 'digitalocean_cdn',
|
|
96
|
+
'do.registry': 'digitalocean_container_registry',
|
|
97
|
+
|
|
98
|
+
// Alibaba Cloud
|
|
99
|
+
'alibaba.ecs.instance': 'alicloud_instance',
|
|
100
|
+
'alibaba.oss.bucket': 'alicloud_oss_bucket',
|
|
101
|
+
'alibaba.rds.instance': 'alicloud_db_instance',
|
|
102
|
+
'alibaba.ack.cluster': 'alicloud_cs_kubernetes',
|
|
103
|
+
'alibaba.vpc.vpc': 'alicloud_vpc',
|
|
104
|
+
'alibaba.vpc.vswitch': 'alicloud_vswitch',
|
|
105
|
+
'alibaba.slb.loadbalancer': 'alicloud_slb',
|
|
106
|
+
'alibaba.redis.instance': 'alicloud_kvstore_instance',
|
|
107
|
+
'alibaba.cdn.distribution': 'alicloud_cdn_domain',
|
|
108
|
+
'alibaba.dns.domain': 'alicloud_dns',
|
|
109
|
+
'alibaba.ecs.securitygroup': 'alicloud_security_group',
|
|
110
|
+
'alibaba.ecs.snapshot': 'alicloud_snapshot',
|
|
111
|
+
'alibaba.ess.scalinggroup': 'alicloud_ess_scaling_group',
|
|
112
|
+
'alibaba.ess.scalingconfiguration': 'alicloud_ess_scaling_configuration',
|
|
113
|
+
'alibaba.natgateway': 'alicloud_nat_gateway',
|
|
114
|
+
'alibaba.acr.repository': 'alicloud_cr_repo',
|
|
115
|
+
|
|
116
|
+
// Linode
|
|
117
|
+
'linode.compute.instance': 'linode_instance',
|
|
118
|
+
'linode.lke.cluster': 'linode_lke_cluster',
|
|
119
|
+
'linode.volume': 'linode_volume',
|
|
120
|
+
'linode.nodebalancer': 'linode_nodebalancer',
|
|
121
|
+
'linode.firewall': 'linode_firewall',
|
|
122
|
+
'linode.domain': 'linode_domain',
|
|
123
|
+
'linode.objectstorage.bucket': 'linode_object_storage_bucket',
|
|
124
|
+
'linode.database': 'linode_database_mysql', // or postgresql, mongodb
|
|
125
|
+
|
|
126
|
+
// Hetzner
|
|
127
|
+
'hetzner.server': 'hetzner_server',
|
|
128
|
+
'hetzner.volume': 'hetzner_volume',
|
|
129
|
+
'hetzner.network': 'hetzner_network',
|
|
130
|
+
'hetzner.loadbalancer': 'hetzner_load_balancer',
|
|
131
|
+
'hetzner.firewall': 'hetzner_firewall',
|
|
132
|
+
'hetzner.floatingip': 'hetzner_floating_ip',
|
|
133
|
+
'hetzner.primaryip': 'hetzner_primary_ip',
|
|
134
|
+
'hetzner.sshkey': 'hetzner_ssh_key',
|
|
135
|
+
'hetzner.placementgroup': 'hetzner_placement_group',
|
|
136
|
+
|
|
137
|
+
// Vultr
|
|
138
|
+
'vultr.instance': 'vultr_instance',
|
|
139
|
+
'vultr.kubernetes.cluster': 'vultr_kubernetes',
|
|
140
|
+
'vultr.blockstorage': 'vultr_block_storage',
|
|
141
|
+
'vultr.loadbalancer': 'vultr_load_balancer',
|
|
142
|
+
'vultr.firewall.group': 'vultr_firewall_group',
|
|
143
|
+
'vultr.vpc': 'vultr_vpc',
|
|
144
|
+
'vultr.objectstorage': 'vultr_object_storage',
|
|
145
|
+
'vultr.database': 'vultr_database',
|
|
146
|
+
|
|
147
|
+
// Oracle Cloud
|
|
148
|
+
'oci.compute.instance': 'oci_core_instance',
|
|
149
|
+
'oci.objectstorage.bucket': 'oci_objectstorage_bucket',
|
|
150
|
+
'oci.database.autonomousdatabase': 'oci_database_autonomous_database',
|
|
151
|
+
'oci.database.dbsystem': 'oci_database_db_system',
|
|
152
|
+
'oci.kubernetes.cluster': 'oci_containerengine_cluster',
|
|
153
|
+
'oci.vcn': 'oci_core_vcn',
|
|
154
|
+
'oci.vcn.subnet': 'oci_core_subnet',
|
|
155
|
+
'oci.compute.volume': 'oci_core_volume',
|
|
156
|
+
'oci.filestorage.filesystem': 'oci_file_storage_file_system',
|
|
157
|
+
'oci.loadbalancer': 'oci_load_balancer',
|
|
158
|
+
'oci.dns.zone': 'oci_dns_zone',
|
|
159
|
+
|
|
160
|
+
// Cloudflare
|
|
161
|
+
'cloudflare.workers.script': 'cloudflare_worker_script',
|
|
162
|
+
'cloudflare.r2.bucket': 'cloudflare_r2_bucket',
|
|
163
|
+
'cloudflare.pages.project': 'cloudflare_pages_project',
|
|
164
|
+
'cloudflare.d1.database': 'cloudflare_d1_database',
|
|
165
|
+
'cloudflare.kv.namespace': 'cloudflare_workers_kv_namespace',
|
|
166
|
+
'cloudflare.durable-objects.namespace': 'cloudflare_workers_durable_object_namespace',
|
|
167
|
+
'cloudflare.zone': 'cloudflare_zone',
|
|
168
|
+
'cloudflare.dns.record': 'cloudflare_record',
|
|
169
|
+
'cloudflare.loadbalancer': 'cloudflare_load_balancer',
|
|
170
|
+
'cloudflare.ssl.certificate': 'cloudflare_origin_ca_certificate',
|
|
171
|
+
'cloudflare.waf.ruleset': 'cloudflare_ruleset',
|
|
172
|
+
'cloudflare.access.application': 'cloudflare_access_application',
|
|
173
|
+
'cloudflare.access.policy': 'cloudflare_access_policy',
|
|
174
|
+
|
|
175
|
+
// MongoDB Atlas
|
|
176
|
+
'mongodb-atlas.project': 'mongodbatlas_project',
|
|
177
|
+
'mongodb-atlas.cluster': 'mongodbatlas_cluster',
|
|
178
|
+
'mongodb-atlas.serverless': 'mongodbatlas_serverless_instance',
|
|
179
|
+
'mongodb-atlas.user': 'mongodbatlas_database_user',
|
|
180
|
+
'mongodb-atlas.accesslist': 'mongodbatlas_project_ip_access_list',
|
|
181
|
+
'mongodb-atlas.backup': 'mongodbatlas_cloud_backup_snapshot',
|
|
182
|
+
'mongodb-atlas.alert': 'mongodbatlas_alert_configuration',
|
|
183
|
+
'mongodb-atlas.datalake': 'mongodbatlas_data_lake',
|
|
184
|
+
'mongodb-atlas.search.index': 'mongodbatlas_search_index',
|
|
185
|
+
'mongodb-atlas.customrole': 'mongodbatlas_custom_db_role',
|
|
186
|
+
'mongodb-atlas.event': 'mongodbatlas_event_trigger'
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get Terraform provider name from cloud resource type
|
|
191
|
+
* @param {string} resourceType - Cloud inventory resource type (e.g., 'aws.ec2.instance')
|
|
192
|
+
* @returns {string} - Provider name (e.g., 'registry.terraform.io/hashicorp/aws')
|
|
193
|
+
*/
|
|
194
|
+
function getProviderConfig(resourceType) {
|
|
195
|
+
const provider = resourceType.split('.')[0];
|
|
196
|
+
|
|
197
|
+
const providerMap = {
|
|
198
|
+
'aws': 'registry.terraform.io/hashicorp/aws',
|
|
199
|
+
'gcp': 'registry.terraform.io/hashicorp/google',
|
|
200
|
+
'azure': 'registry.terraform.io/hashicorp/azurerm',
|
|
201
|
+
'do': 'registry.terraform.io/digitalocean/digitalocean',
|
|
202
|
+
'alibaba': 'registry.terraform.io/aliyun/alicloud',
|
|
203
|
+
'linode': 'registry.terraform.io/linode/linode',
|
|
204
|
+
'hetzner': 'registry.terraform.io/hetznercloud/hetzner',
|
|
205
|
+
'vultr': 'registry.terraform.io/vultr/vultr',
|
|
206
|
+
'oci': 'registry.terraform.io/hashicorp/oci',
|
|
207
|
+
'oracle': 'registry.terraform.io/hashicorp/oci',
|
|
208
|
+
'cloudflare': 'registry.terraform.io/cloudflare/cloudflare',
|
|
209
|
+
'mongodb-atlas': 'registry.terraform.io/mongodb/mongodbatlas'
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return providerMap[provider] || `registry.terraform.io/hashicorp/${provider}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Convert cloud inventory resource to Terraform resource format
|
|
217
|
+
* @param {Object} resource - Cloud inventory resource
|
|
218
|
+
* @returns {Object|null} - Terraform resource or null if not mappable
|
|
219
|
+
*/
|
|
220
|
+
export function convertToTerraformResource(resource) {
|
|
221
|
+
const { resourceType, resourceId, configuration } = resource;
|
|
222
|
+
|
|
223
|
+
// Map resource type
|
|
224
|
+
const tfType = RESOURCE_TYPE_MAP[resourceType];
|
|
225
|
+
if (!tfType) {
|
|
226
|
+
return null; // Skip unmapped resource types
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Generate resource name from resourceId (sanitize for Terraform)
|
|
230
|
+
const resourceName = sanitizeResourceName(resourceId);
|
|
231
|
+
|
|
232
|
+
// Get provider configuration
|
|
233
|
+
const providerFqn = getProviderConfig(resourceType);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
mode: 'managed',
|
|
237
|
+
type: tfType,
|
|
238
|
+
name: resourceName,
|
|
239
|
+
provider: providerFqn,
|
|
240
|
+
instances: [
|
|
241
|
+
{
|
|
242
|
+
schema_version: 0,
|
|
243
|
+
attributes: configuration || {},
|
|
244
|
+
private: 'bnVsbA==', // base64("null")
|
|
245
|
+
dependencies: []
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Sanitize resource ID for Terraform resource name
|
|
253
|
+
* Terraform names must: start with letter, contain only letters/numbers/underscores/hyphens
|
|
254
|
+
* @param {string} resourceId
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
function sanitizeResourceName(resourceId) {
|
|
258
|
+
let name = String(resourceId)
|
|
259
|
+
.toLowerCase()
|
|
260
|
+
.replace(/[^a-z0-9_-]/g, '_')
|
|
261
|
+
.replace(/^[^a-z]+/, '') // Remove leading non-letters
|
|
262
|
+
.replace(/_+/g, '_')
|
|
263
|
+
.replace(/^_+|_+$/g, ''); // Trim underscores
|
|
264
|
+
|
|
265
|
+
// Ensure it starts with a letter
|
|
266
|
+
if (!name || !/^[a-z]/.test(name)) {
|
|
267
|
+
name = `resource_${name}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Limit length
|
|
271
|
+
return name.slice(0, 64) || 'resource';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Export cloud inventory snapshots to Terraform state format
|
|
276
|
+
* @param {Array} snapshots - Array of cloud inventory snapshot records
|
|
277
|
+
* @param {Object} options - Export options
|
|
278
|
+
* @param {string} options.terraformVersion - Terraform version (default: '1.5.0')
|
|
279
|
+
* @param {string} options.lineage - State lineage UUID (default: auto-generated)
|
|
280
|
+
* @param {number} options.serial - State serial number (default: 1)
|
|
281
|
+
* @param {Object} options.outputs - Terraform outputs (default: {})
|
|
282
|
+
* @param {Array<string>} options.resourceTypes - Filter by cloud resource types
|
|
283
|
+
* @param {Array<string>} options.providers - Filter by provider
|
|
284
|
+
* @returns {Object} - Terraform state object
|
|
285
|
+
*/
|
|
286
|
+
export function exportToTerraformState(snapshots, options = {}) {
|
|
287
|
+
const {
|
|
288
|
+
terraformVersion = '1.5.0',
|
|
289
|
+
lineage = generateLineage(),
|
|
290
|
+
serial = 1,
|
|
291
|
+
outputs = {},
|
|
292
|
+
resourceTypes = [],
|
|
293
|
+
providers = []
|
|
294
|
+
} = options;
|
|
295
|
+
|
|
296
|
+
// Filter snapshots
|
|
297
|
+
let filteredSnapshots = snapshots;
|
|
298
|
+
|
|
299
|
+
if (resourceTypes.length > 0) {
|
|
300
|
+
filteredSnapshots = filteredSnapshots.filter(s =>
|
|
301
|
+
resourceTypes.includes(s.resourceType)
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (providers.length > 0) {
|
|
306
|
+
filteredSnapshots = filteredSnapshots.filter(s => {
|
|
307
|
+
const provider = s.resourceType.split('.')[0];
|
|
308
|
+
return providers.includes(provider);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Convert to Terraform resources
|
|
313
|
+
const tfResources = [];
|
|
314
|
+
const skipped = [];
|
|
315
|
+
|
|
316
|
+
for (const snapshot of filteredSnapshots) {
|
|
317
|
+
const tfResource = convertToTerraformResource(snapshot);
|
|
318
|
+
if (tfResource) {
|
|
319
|
+
tfResources.push(tfResource);
|
|
320
|
+
} else {
|
|
321
|
+
skipped.push(snapshot.resourceType);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Construct Terraform state
|
|
326
|
+
const state = {
|
|
327
|
+
version: 4,
|
|
328
|
+
terraform_version: terraformVersion,
|
|
329
|
+
serial,
|
|
330
|
+
lineage,
|
|
331
|
+
outputs,
|
|
332
|
+
resources: tfResources
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
state,
|
|
337
|
+
stats: {
|
|
338
|
+
total: snapshots.length,
|
|
339
|
+
converted: tfResources.length,
|
|
340
|
+
skipped: skipped.length,
|
|
341
|
+
skippedTypes: [...new Set(skipped)]
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Generate a UUID for Terraform state lineage
|
|
348
|
+
* @returns {string}
|
|
349
|
+
*/
|
|
350
|
+
function generateLineage() {
|
|
351
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
352
|
+
const r = Math.random() * 16 | 0;
|
|
353
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
354
|
+
return v.toString(16);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export default {
|
|
359
|
+
convertToTerraformResource,
|
|
360
|
+
exportToTerraformState,
|
|
361
|
+
RESOURCE_TYPE_MAP
|
|
362
|
+
};
|