@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39

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 (149) hide show
  1. package/README.md +662 -658
  2. package/package.json +10 -3
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/index.js +26 -0
  100. package/src/services/lambda/server.js +12 -1
  101. package/src/services/lambda/simulator.js +4 -0
  102. package/src/services/opensearch/index.js +105 -0
  103. package/src/services/opensearch/models/document.js +115 -0
  104. package/src/services/opensearch/models/index.js +73 -0
  105. package/src/services/opensearch/server.js +310 -0
  106. package/src/services/opensearch/simulator.js +605 -0
  107. package/src/services/opensearch/store.js +162 -0
  108. package/src/services/organizations/control-tower-simulator.js +321 -0
  109. package/src/services/organizations/control-tower-store.js +182 -0
  110. package/src/services/organizations/iam-integration.js +413 -0
  111. package/src/services/organizations/index.js +172 -0
  112. package/src/services/organizations/models/index.js +13 -0
  113. package/src/services/organizations/models/invitation.js +48 -0
  114. package/src/services/organizations/models/organization.js +566 -0
  115. package/src/services/organizations/models/role.js +244 -0
  116. package/src/services/organizations/models/scp.js +131 -0
  117. package/src/services/organizations/server.js +736 -0
  118. package/src/services/organizations/simulator.js +593 -0
  119. package/src/services/organizations/store.js +235 -0
  120. package/src/services/organizations/vpc-integration.js +453 -0
  121. package/src/services/route53/index.js +67 -0
  122. package/src/services/route53/models/healthCheck.js +101 -0
  123. package/src/services/route53/models/hostedZone.js +37 -0
  124. package/src/services/route53/models/resourceRecord.js +106 -0
  125. package/src/services/route53/server.js +841 -0
  126. package/src/services/route53/simulator.js +350 -0
  127. package/src/services/s3/index.js +62 -0
  128. package/src/services/s3/presigned-url-generator.js +337 -0
  129. package/src/services/s3/server.js +231 -0
  130. package/src/services/s3/simulator.js +551 -4
  131. package/src/services/sns/index.js +31 -0
  132. package/src/services/sns/server.js +9 -0
  133. package/src/services/sqs/index.js +38 -0
  134. package/src/services/sqs/server.js +57 -4
  135. package/src/services/sqs/simulator.js +423 -33
  136. package/src/services/sts/simulator.js +209 -17
  137. package/src/services/vpc/README.md +459 -0
  138. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  139. package/src/services/vpc/index.js +90 -0
  140. package/src/services/vpc/models/internetGateway.js +71 -0
  141. package/src/services/vpc/models/routeTable.js +160 -0
  142. package/src/services/vpc/models/subnet.js +48 -0
  143. package/src/services/vpc/models/vpc.js +37 -0
  144. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  145. package/src/services/vpc/models/vpcGateway.js +134 -0
  146. package/src/services/vpc/server.js +836 -0
  147. package/src/services/vpc/simulator.js +613 -0
  148. package/src/utils/policy-cache.js +215 -0
  149. package/aws-config +0 -154
@@ -0,0 +1,220 @@
1
+ /**
2
+ * CloudFront Service Index
3
+ * Main entry point for CloudFront simulator
4
+ */
5
+
6
+ const CloudFrontServer = require('./server');
7
+ const CloudFrontSimulator = require('./simulator');
8
+ const CloudFrontStore = require('./store');
9
+ const SignedUrlGenerator = require('./signed-url-generator');
10
+ const OriginValidator = require('./origin-validator');
11
+ const GeoRestrictions = require('./geo-restrictions');
12
+ const OriginRouter = require('./origin-router');
13
+ const CacheEngine = require('./cache-engine');
14
+
15
+ const {
16
+ Distribution,
17
+ Origin,
18
+ Behavior,
19
+ Invalidation,
20
+ validate
21
+ } = require('./models');
22
+
23
+ /**
24
+ * CloudFront Service Class
25
+ */
26
+ class CloudFrontService {
27
+ constructor(config) {
28
+ this.config = config;
29
+ this.name = 'cloudfront';
30
+ this.port = config?.ports?.cloudfront || config?.services?.cloudfront?.port || 4017;
31
+ this.server = null;
32
+ this.simulator = null;
33
+ this.store = null;
34
+ this.isRunning = false;
35
+ }
36
+
37
+ async initialize() {
38
+ const logger = require('../../utils/logger');
39
+ logger?.debug?.(`Initializing CloudFront Service on port ${this.port}...`);
40
+
41
+ this.simulator = new CloudFrontSimulator(this.config);
42
+ this.store = new CloudFrontStore(this.config?.storagePath || './data/cloudfront');
43
+ this.server = new CloudFrontServer(this.config);
44
+
45
+ // Inject simulator into server
46
+ this.server.simulator = this.simulator;
47
+
48
+ logger?.debug?.('CloudFront Service initialized');
49
+ }
50
+
51
+ injectDependencies(server) {
52
+ // CloudFront may depend on other services like S3, Lambda, etc.
53
+ // Injections would happen here
54
+ const s3Service = server.getService('s3');
55
+ const apigatewayService = server.getService('apigateway');
56
+ const ec2Service = server.getService('ec2');
57
+ const lambdaService = server.getService('lambda');
58
+
59
+ if (this.simulator) {
60
+ if (s3Service) this.simulator.registerService('s3', s3Service);
61
+ if (apigatewayService) this.simulator.registerService('apigateway', apigatewayService);
62
+ if (ec2Service) this.simulator.registerService('ec2', ec2Service);
63
+ if (lambdaService) this.simulator.registerService('lambda', lambdaService);
64
+ }
65
+ }
66
+
67
+ async start() {
68
+ if (this.isRunning) return;
69
+
70
+ return new Promise((resolve, reject) => {
71
+ try {
72
+ const httpServer = this.server.listen(this.port);
73
+
74
+ // Handle listen errors
75
+ httpServer.on('error', (error) => {
76
+ if (error.code === 'EADDRINUSE') {
77
+ reject(new Error(`Port ${this.port} is already in use`));
78
+ } else {
79
+ reject(error);
80
+ }
81
+ });
82
+
83
+ // Server is listening
84
+ this.isRunning = true;
85
+ resolve();
86
+ } catch (error) {
87
+ reject(error);
88
+ }
89
+ });
90
+ }
91
+
92
+ async stop() {
93
+ if (!this.isRunning) return;
94
+
95
+ return new Promise((resolve) => {
96
+ if (this.server?.server) {
97
+ this.server.server.close(() => {
98
+ this.isRunning = false;
99
+ resolve();
100
+ });
101
+ } else {
102
+ this.isRunning = false;
103
+ resolve();
104
+ }
105
+ });
106
+ }
107
+
108
+ async reset() {
109
+ if (this.simulator) {
110
+ this.simulator.reset?.();
111
+ }
112
+ if (this.store) {
113
+ this.store.clear?.();
114
+ }
115
+ }
116
+
117
+ getStatus() {
118
+ const stats = this.simulator?.getStats?.() || {
119
+ distributionsCount: 0,
120
+ cacheHits: 0,
121
+ cacheMisses: 0
122
+ };
123
+
124
+ return {
125
+ name: 'cloudfront',
126
+ running: this.isRunning,
127
+ port: this.port,
128
+ endpoint: `http://localhost:${this.port}`,
129
+ ...stats
130
+ };
131
+ }
132
+
133
+ getSimulator() {
134
+ return this.simulator;
135
+ }
136
+
137
+ getServer() {
138
+ return this.server;
139
+ }
140
+
141
+ getStore() {
142
+ return this.store;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Create a CloudFront service instance (backward compatibility)
148
+ * @param {object} options - Configuration options
149
+ * @returns {object} CloudFront service object
150
+ */
151
+ function createCloudFrontService(options = {}) {
152
+ const simulator = new CloudFrontSimulator(options);
153
+ const store = new CloudFrontStore(options.storePath);
154
+ const server = new CloudFrontServer(options);
155
+
156
+ return {
157
+ // Simulator methods
158
+ simulator,
159
+ createDistribution: (config) => simulator.createDistribution(config),
160
+ getDistribution: (id) => simulator.getDistribution(id),
161
+ listDistributions: () => simulator.listDistributions(),
162
+ deleteDistribution: (id) => simulator.deleteDistribution(id),
163
+ processRequest: (request) => simulator.processRequest(request),
164
+ invalidateDistribution: (id, paths) => simulator.invalidateDistribution(id, paths),
165
+ generateSignedUrl: (id, path, key, keyPair, expiration) =>
166
+ simulator.generateSignedUrl(id, path, key, keyPair, expiration),
167
+ getCacheStats: () => simulator.getCacheStats(),
168
+ getDistributionStats: (id) => simulator.getDistributionStats(id),
169
+ getRequestStats: () => simulator.getRequestStats(),
170
+
171
+ // Store methods
172
+ store,
173
+ saveDistribution: (distribution) => store.saveDistribution(distribution),
174
+ loadDistribution: (id) => store.getDistribution(id),
175
+ loadDistributions: () => store.getAllDistributions(),
176
+ saveInvalidation: (invalidation) => store.saveInvalidation(invalidation),
177
+ loadInvalidation: (id) => store.getInvalidation(id),
178
+ clearStore: () => store.clear(),
179
+ exportData: () => store.export(),
180
+ importData: (data) => store.import(data),
181
+ createBackup: (path) => store.createBackup(path),
182
+
183
+ // Server methods
184
+ server,
185
+ listen: (port) => server.listen(port),
186
+ getApp: () => server.getApp(),
187
+
188
+ // Helper classes
189
+ SignedUrlGenerator,
190
+ OriginValidator,
191
+ GeoRestrictions,
192
+ CacheEngine,
193
+
194
+ // Models
195
+ Distribution,
196
+ Origin,
197
+ Behavior,
198
+ Invalidation,
199
+ validate
200
+ };
201
+ }
202
+
203
+ module.exports = CloudFrontService;
204
+
205
+ // Also export for backward compatibility
206
+ module.exports.CloudFrontService = CloudFrontService;
207
+ module.exports.createCloudFrontService = createCloudFrontService;
208
+ module.exports.CloudFrontServer = CloudFrontServer;
209
+ module.exports.CloudFrontSimulator = CloudFrontSimulator;
210
+ module.exports.CloudFrontStore = CloudFrontStore;
211
+ module.exports.SignedUrlGenerator = SignedUrlGenerator;
212
+ module.exports.OriginValidator = OriginValidator;
213
+ module.exports.OriginRouter = OriginRouter;
214
+ module.exports.GeoRestrictions = GeoRestrictions;
215
+ module.exports.CacheEngine = CacheEngine;
216
+ module.exports.Distribution = Distribution;
217
+ module.exports.Origin = Origin;
218
+ module.exports.Behavior = Behavior;
219
+ module.exports.Invalidation = Invalidation;
220
+ module.exports.validate = validate;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * CloudFront Behavior Model
3
+ * Define como CloudFront comporta-se para diferentes paths
4
+ */
5
+
6
+ class Behavior {
7
+ constructor(params = {}) {
8
+ this.PathPattern = params.PathPattern || '*'; // null for default
9
+ this.TargetOriginId = params.TargetOriginId;
10
+ this.ViewerProtocolPolicy = params.ViewerProtocolPolicy || 'allow-all';
11
+ this.AllowedMethods = params.AllowedMethods || {
12
+ Quantity: 2,
13
+ Items: ['GET', 'HEAD']
14
+ };
15
+ this.CachedMethods = params.CachedMethods || {
16
+ Quantity: 2,
17
+ Items: ['GET', 'HEAD']
18
+ };
19
+
20
+ // Caching
21
+ this.CachePolicyId = params.CachePolicyId || '658327ea-f89d-4fab-a63d-7e88639e58f6';
22
+ this.OriginRequestPolicyId = params.OriginRequestPolicyId || null;
23
+ this.ResponseHeadersPolicyId = params.ResponseHeadersPolicyId || null;
24
+
25
+ // TTL (deprecated, for backwards compatibility)
26
+ this.MinTTL = params.MinTTL || 0;
27
+ this.DefaultTTL = params.DefaultTTL || 86400;
28
+ this.MaxTTL = params.MaxTTL || 31536000;
29
+
30
+ // Compression
31
+ this.Compress = params.Compress !== false;
32
+
33
+ // Forwarded values
34
+ this.ForwardedValues = params.ForwardedValues || {
35
+ QueryString: true,
36
+ Cookies: { Forward: 'all' },
37
+ Headers: { Quantity: 0, Items: [] }
38
+ };
39
+
40
+ // Trusted signers (for signed URLs/cookies)
41
+ this.TrustedSigners = params.TrustedSigners || {
42
+ Enabled: false,
43
+ Quantity: 0,
44
+ Items: []
45
+ };
46
+
47
+ // Field-level encryption
48
+ this.FieldLevelEncryptionId = params.FieldLevelEncryptionId || null;
49
+
50
+ // Lambda@Edge
51
+ this.LambdaFunctionAssociations = params.LambdaFunctionAssociations || {
52
+ Quantity: 0,
53
+ Items: []
54
+ };
55
+
56
+ // Realtime logs
57
+ this.RealtimeLogConfigArn = params.RealtimeLogConfigArn || null;
58
+ }
59
+
60
+ /**
61
+ * Validar behavior
62
+ */
63
+ validate() {
64
+ const errors = [];
65
+
66
+ if (!this.TargetOriginId) {
67
+ errors.push('TargetOriginId is required');
68
+ }
69
+
70
+ if (!['allow-all', 'https-only', 'redirect-to-https'].includes(this.ViewerProtocolPolicy)) {
71
+ errors.push('Invalid ViewerProtocolPolicy');
72
+ }
73
+
74
+ return {
75
+ valid: errors.length === 0,
76
+ errors
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Obter política de cache baseada no CachePolicyId
82
+ */
83
+ getCachePolicy() {
84
+ const managedPolicies = {
85
+ '658327ea-f89d-4fab-a63d-7e88639e58f6': {
86
+ name: 'CachingOptimized',
87
+ ttl: { min: 0, default: 86400, max: 31536000 }
88
+ },
89
+ '4135ea3d-c35d-4c5f-bc95-6f532eef9d81': {
90
+ name: 'CachingDisabled',
91
+ ttl: { min: 0, default: 0, max: 0 }
92
+ },
93
+ '08627262-05a9-4f76-9ded-b50ec2b5fbf0': {
94
+ name: 'Elemental',
95
+ ttl: { min: 0, default: 0, max: 0 }
96
+ }
97
+ };
98
+
99
+ return managedPolicies[this.CachePolicyId] || {
100
+ name: 'Custom',
101
+ ttl: { min: this.MinTTL, default: this.DefaultTTL, max: this.MaxTTL }
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Verificar se request é cacheável
107
+ */
108
+ isCacheable(method = 'GET', statusCode = 200) {
109
+ // GET e HEAD são cacheáveis por padrão
110
+ if (!['GET', 'HEAD'].includes(method)) {
111
+ return false;
112
+ }
113
+
114
+ // Apenas respostas 200 são cacheadas (simplificado)
115
+ if (statusCode !== 200) {
116
+ return false;
117
+ }
118
+
119
+ const cachePolicy = this.getCachePolicy();
120
+ if (cachePolicy.name === 'CachingDisabled') {
121
+ return false;
122
+ }
123
+
124
+ return true;
125
+ }
126
+
127
+ /**
128
+ * Obter TTL baseado na política
129
+ */
130
+ getTTL() {
131
+ const policy = this.getCachePolicy();
132
+ return policy.ttl.default;
133
+ }
134
+
135
+ /**
136
+ * Retornar behavior em formato AWS
137
+ */
138
+ toAWSFormat() {
139
+ return {
140
+ PathPattern: this.PathPattern,
141
+ TargetOriginId: this.TargetOriginId,
142
+ ViewerProtocolPolicy: this.ViewerProtocolPolicy,
143
+ AllowedMethods: this.AllowedMethods,
144
+ CachedMethods: this.CachedMethods,
145
+ Compress: this.Compress,
146
+ CachePolicyId: this.CachePolicyId,
147
+ OriginRequestPolicyId: this.OriginRequestPolicyId,
148
+ ResponseHeadersPolicyId: this.ResponseHeadersPolicyId,
149
+ MinTTL: this.MinTTL,
150
+ DefaultTTL: this.DefaultTTL,
151
+ MaxTTL: this.MaxTTL,
152
+ ForwardedValues: this.ForwardedValues,
153
+ TrustedSigners: this.TrustedSigners
154
+ };
155
+ }
156
+ }
157
+
158
+ module.exports = Behavior;
@@ -0,0 +1,284 @@
1
+ /**
2
+ * CloudFront Distribution Model
3
+ * Representa uma distribuição CloudFront completa
4
+ */
5
+
6
+ const crypto = require('crypto');
7
+
8
+ class Distribution {
9
+ constructor(params = {}) {
10
+ this.Id = params.Id || this.generateId();
11
+ this.ARN = `arn:aws:cloudfront::123456789012:distribution/${this.Id}`;
12
+ this.DomainName = params.DomainName || `${this.Id}.cloudfront.net`;
13
+ this.CallerReference = params.CallerReference || crypto.randomUUID();
14
+ this.Comment = params.Comment || '';
15
+ this.DefaultRootObject = params.DefaultRootObject || 'index.html';
16
+ this.Enabled = params.Enabled !== false;
17
+ this.Status = 'Deployed';
18
+ this.CreatedTime = new Date();
19
+ this.LastModifiedTime = new Date();
20
+ this.LastModifiedBy = params.LastModifiedBy || 'simulator';
21
+ this.ETag = this.generateETag();
22
+
23
+ // Origins
24
+ this.Origins = params.Origins || {
25
+ Quantity: 0,
26
+ Items: []
27
+ };
28
+
29
+ // Default Cache Behavior
30
+ this.DefaultCacheBehavior = params.DefaultCacheBehavior || {
31
+ TargetOriginId: this.Origins.Items?.[0]?.Id || 'default-origin',
32
+ ViewerProtocolPolicy: 'allow-all',
33
+ TrustedSigners: { Enabled: false, Quantity: 0 },
34
+ ForwardedValues: {
35
+ QueryString: false,
36
+ Cookies: { Forward: 'none' },
37
+ Headers: { Quantity: 0 }
38
+ },
39
+ MinTTL: 0,
40
+ DefaultTTL: 86400,
41
+ MaxTTL: 31536000,
42
+ Compress: false,
43
+ CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6'
44
+ };
45
+
46
+ // Cache Behaviors (path patterns)
47
+ this.CacheBehaviors = params.CacheBehaviors || {
48
+ Quantity: 0,
49
+ Items: []
50
+ };
51
+
52
+ // Geo Restrictions
53
+ this.GeoRestriction = params.GeoRestriction || {
54
+ RestrictionType: 'none',
55
+ Quantity: 0,
56
+ Items: []
57
+ };
58
+
59
+ // WebACL
60
+ this.WebACLId = params.WebACLId || '';
61
+
62
+ // HTTP Version
63
+ this.HttpVersion = params.HttpVersion || 'http2';
64
+
65
+ // IPv6 Enabled
66
+ this.IsIPV6Enabled = params.IsIPV6Enabled !== false;
67
+
68
+ // Invalidations in progress
69
+ this.InvalidationsInProgress = 0;
70
+
71
+ // Cache statistics
72
+ this.CacheStats = {
73
+ Hits: 0,
74
+ Misses: 0,
75
+ BytesSent: 0,
76
+ BytesUploaded: 0
77
+ };
78
+
79
+ // Origin Shield
80
+ this.OriginShield = params.OriginShield || {
81
+ Enabled: false,
82
+ OriginShieldRegion: null
83
+ };
84
+
85
+ // Custom domain (CNAME)
86
+ this.Aliases = params.Aliases || {
87
+ Quantity: 0,
88
+ Items: []
89
+ };
90
+
91
+ // SSL/TLS
92
+ this.ViewerCertificate = params.ViewerCertificate || {
93
+ CloudFrontDefaultCertificate: true,
94
+ ACMCertificateArn: null,
95
+ SSLSupportMethod: null,
96
+ MinimumProtocolVersion: 'TLSv1'
97
+ };
98
+
99
+ // Logging
100
+ this.Logging = params.Logging || {
101
+ Enabled: false,
102
+ IncludeCookies: false,
103
+ Bucket: null,
104
+ Prefix: ''
105
+ };
106
+
107
+ // Price Class
108
+ this.PriceClass = params.PriceClass || 'PriceClass_All';
109
+ }
110
+
111
+ /**
112
+ * Getter para origins (compatibilidade com simulador)
113
+ * Converte Origins com Items para array com propriedades em minúsculas
114
+ */
115
+ get origins() {
116
+ if (!this.Origins?.Items) return [];
117
+ return this.Origins.Items.map(item => ({
118
+ id: item.Id || item.id,
119
+ domainName: item.DomainName || item.domainName,
120
+ originPath: item.OriginPath || item.originPath || '',
121
+ customOriginConfig: item.CustomOriginConfig || item.customOriginConfig,
122
+ s3OriginConfig: item.S3OriginConfig || item.s3OriginConfig,
123
+ ...item // Manter todas as outras propriedades
124
+ }));
125
+ }
126
+
127
+ /**
128
+ * Getter para cache behaviors
129
+ */
130
+ get cacheBehaviors() {
131
+ if (!this.CacheBehaviors?.Items) return [];
132
+ return this.CacheBehaviors.Items.map(item => ({
133
+ pathPattern: item.PathPattern || item.pathPattern,
134
+ targetOriginId: item.TargetOriginId || item.targetOriginId,
135
+ viewerProtocolPolicy: item.ViewerProtocolPolicy || item.viewerProtocolPolicy,
136
+ ...item
137
+ }));
138
+ }
139
+
140
+ generateId() {
141
+ return crypto.randomBytes(7).toString('hex').toUpperCase();
142
+ }
143
+
144
+ generateETag() {
145
+ const data = JSON.stringify({
146
+ Origins: this.Origins,
147
+ DefaultCacheBehavior: this.DefaultCacheBehavior,
148
+ CacheBehaviors: this.CacheBehaviors,
149
+ Enabled: this.Enabled,
150
+ LastModifiedTime: this.LastModifiedTime
151
+ });
152
+ return crypto.createHash('md5').update(data).digest('hex');
153
+ }
154
+
155
+ /**
156
+ * Encontrar behavior que corresponde ao path
157
+ */
158
+ matchBehavior(path) {
159
+ // Procurar em Cache Behaviors por path pattern (mais específico primeiro)
160
+ if (this.CacheBehaviors?.Items) {
161
+ const sorted = [...this.CacheBehaviors.Items].sort((a, b) => {
162
+ // Path patterns mais específicos primeiro
163
+ const aSpecificity = this.calculatePathSpecificity(a.PathPattern);
164
+ const bSpecificity = this.calculatePathSpecificity(b.PathPattern);
165
+ return bSpecificity - aSpecificity;
166
+ });
167
+
168
+ for (const behavior of sorted) {
169
+ if (this.pathMatches(path, behavior.PathPattern)) {
170
+ return behavior;
171
+ }
172
+ }
173
+ }
174
+
175
+ // Retornar default behavior
176
+ return this.DefaultCacheBehavior;
177
+ }
178
+
179
+ /**
180
+ * Verificar se path corresponde ao pattern
181
+ * Suporta wildcards: *.jpg, /api/*, etc
182
+ */
183
+ pathMatches(path, pattern) {
184
+ if (!pattern || pattern === '*') {
185
+ return true;
186
+ }
187
+
188
+ // Converter glob pattern para regex
189
+ const regex = new RegExp(
190
+ '^' + pattern
191
+ .replace(/\./g, '\\.')
192
+ .replace(/\*/g, '.*')
193
+ .replace(/\?/g, '.') +
194
+ '$'
195
+ );
196
+
197
+ return regex.test(path);
198
+ }
199
+
200
+ /**
201
+ * Calcular especificidade do path pattern
202
+ * Mais específico = maior número
203
+ */
204
+ calculatePathSpecificity(pattern) {
205
+ if (!pattern) return 0;
206
+ if (pattern === '*') return 1;
207
+
208
+ // Contar caracteres não-wildcard
209
+ const nonWildcard = pattern.replace(/[*?]/g, '').length;
210
+ return nonWildcard;
211
+ }
212
+
213
+ /**
214
+ * Atualizar ETag após modificação
215
+ */
216
+ updateETag() {
217
+ this.ETag = this.generateETag();
218
+ this.LastModifiedTime = new Date();
219
+ }
220
+
221
+ /**
222
+ * Retornar distribuição em formato AWS
223
+ */
224
+ toAWSFormat() {
225
+ return {
226
+ Id: this.Id,
227
+ ARN: this.ARN,
228
+ Status: this.Status,
229
+ LastModifiedTime: this.LastModifiedTime,
230
+ DomainName: this.DomainName,
231
+ Aliases: this.Aliases,
232
+ DefaultRootObject: this.DefaultRootObject,
233
+ Origins: this.Origins,
234
+ DefaultCacheBehavior: this.DefaultCacheBehavior,
235
+ CacheBehaviors: this.CacheBehaviors,
236
+ CustomErrorResponses: {
237
+ Quantity: 0,
238
+ Items: []
239
+ },
240
+ Logging: this.Logging,
241
+ Enabled: this.Enabled,
242
+ Comment: this.Comment,
243
+ Restrictions: {
244
+ GeoRestriction: this.GeoRestriction
245
+ },
246
+ WebACLId: this.WebACLId,
247
+ HttpVersion: this.HttpVersion,
248
+ IsIPV6Enabled: this.IsIPV6Enabled,
249
+ PriceClass: this.PriceClass
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Retornar config da distribuição
255
+ */
256
+ getConfig() {
257
+ return {
258
+ CallerReference: this.CallerReference,
259
+ ...this.toAWSFormat()
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Validar distribuição
265
+ */
266
+ validate() {
267
+ const errors = [];
268
+
269
+ if (!this.Origins?.Items || this.Origins.Items.length === 0) {
270
+ errors.push('Distribution must have at least one origin');
271
+ }
272
+
273
+ if (!this.DefaultCacheBehavior?.TargetOriginId) {
274
+ errors.push('DefaultCacheBehavior must have a TargetOriginId');
275
+ }
276
+
277
+ return {
278
+ valid: errors.length === 0,
279
+ errors
280
+ };
281
+ }
282
+ }
283
+
284
+ module.exports = Distribution;