@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.
- package/README.md +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- 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;
|