@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,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudFront Cache Engine
|
|
3
|
+
* Gerencia caching de objetos com TTL
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const logger = require('../../utils/logger');
|
|
7
|
+
|
|
8
|
+
class CacheEngine {
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
this.cache = new Map();
|
|
11
|
+
this.stats = {
|
|
12
|
+
hits: 0,
|
|
13
|
+
misses: 0,
|
|
14
|
+
evictions: 0,
|
|
15
|
+
bytesStored: 0
|
|
16
|
+
};
|
|
17
|
+
this.maxSize = config.maxSize || 100 * 1024 * 1024; // 100MB default
|
|
18
|
+
this.cleanupInterval = setInterval(() => this.cleanup(), 60000); // Cleanup every minute
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Gerar chave de cache
|
|
23
|
+
*/
|
|
24
|
+
generateKey(distributionId, path, queryString = '') {
|
|
25
|
+
return `${distributionId}:${path}${queryString ? '?' + queryString : ''}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get com validação de TTL
|
|
30
|
+
*/
|
|
31
|
+
get(key) {
|
|
32
|
+
const entry = this.cache.get(key);
|
|
33
|
+
|
|
34
|
+
if (!entry) {
|
|
35
|
+
this.stats.misses++;
|
|
36
|
+
logger.debug(`Cache MISS: ${key}`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Verificar se expirou
|
|
41
|
+
if (entry.expiresAt < Date.now()) {
|
|
42
|
+
this.cache.delete(key);
|
|
43
|
+
this.stats.misses++;
|
|
44
|
+
logger.debug(`Cache EXPIRED: ${key}`);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Hit
|
|
49
|
+
this.stats.hits++;
|
|
50
|
+
entry.hits++;
|
|
51
|
+
entry.lastAccessed = Date.now();
|
|
52
|
+
logger.debug(`Cache HIT: ${key} (hits: ${entry.hits})`);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
data: entry.data,
|
|
56
|
+
headers: entry.headers,
|
|
57
|
+
statusCode: entry.statusCode,
|
|
58
|
+
cacheKey: key,
|
|
59
|
+
xCache: 'Hit from cloudfront'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Set com TTL
|
|
65
|
+
*/
|
|
66
|
+
set(key, data, headers = {}, statusCode = 200, ttl = 86400) {
|
|
67
|
+
// Calcular tamanho aproximado
|
|
68
|
+
const size = JSON.stringify({ data, headers }).length;
|
|
69
|
+
|
|
70
|
+
// Verificar espaço disponível
|
|
71
|
+
if (this.stats.bytesStored + size > this.maxSize) {
|
|
72
|
+
this.evictLRU();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
const entry = {
|
|
77
|
+
data,
|
|
78
|
+
headers,
|
|
79
|
+
statusCode,
|
|
80
|
+
createdAt: now,
|
|
81
|
+
expiresAt: now + (ttl * 1000),
|
|
82
|
+
lastAccessed: now,
|
|
83
|
+
hits: 0,
|
|
84
|
+
size
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
this.cache.set(key, entry);
|
|
88
|
+
this.stats.bytesStored += size;
|
|
89
|
+
|
|
90
|
+
logger.debug(`Cache SET: ${key} (TTL: ${ttl}s, size: ${size} bytes)`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Invalidar cache por padrão
|
|
95
|
+
*/
|
|
96
|
+
invalidate(pattern) {
|
|
97
|
+
let invalidated = 0;
|
|
98
|
+
|
|
99
|
+
for (const key of this.cache.keys()) {
|
|
100
|
+
if (this.pathMatches(key.split(':')[1], pattern)) {
|
|
101
|
+
const entry = this.cache.get(key);
|
|
102
|
+
this.stats.bytesStored -= entry.size;
|
|
103
|
+
this.cache.delete(key);
|
|
104
|
+
invalidated++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
logger.info(`Cache INVALIDATE: ${pattern} (${invalidated} entries)`);
|
|
109
|
+
return invalidated;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Invalidar tudo de uma distribuição
|
|
114
|
+
*/
|
|
115
|
+
invalidateDistribution(distributionId) {
|
|
116
|
+
let invalidated = 0;
|
|
117
|
+
|
|
118
|
+
for (const key of this.cache.keys()) {
|
|
119
|
+
if (key.startsWith(distributionId + ':')) {
|
|
120
|
+
const entry = this.cache.get(key);
|
|
121
|
+
this.stats.bytesStored -= entry.size;
|
|
122
|
+
this.cache.delete(key);
|
|
123
|
+
invalidated++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
logger.info(`Cache INVALIDATE DISTRIBUTION: ${distributionId} (${invalidated} entries)`);
|
|
128
|
+
return invalidated;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Verificar se path corresponde ao padrão
|
|
133
|
+
*/
|
|
134
|
+
pathMatches(path, pattern) {
|
|
135
|
+
if (pattern === '*') return true;
|
|
136
|
+
|
|
137
|
+
const regex = new RegExp(
|
|
138
|
+
'^' + pattern
|
|
139
|
+
.replace(/\./g, '\\.')
|
|
140
|
+
.replace(/\*/g, '.*')
|
|
141
|
+
.replace(/\?/g, '.') +
|
|
142
|
+
'$'
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return regex.test(path);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Remover item menos recentemente acessado (LRU)
|
|
150
|
+
*/
|
|
151
|
+
evictLRU() {
|
|
152
|
+
let lruKey = null;
|
|
153
|
+
let lruTime = Date.now();
|
|
154
|
+
|
|
155
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
156
|
+
if (entry.lastAccessed < lruTime) {
|
|
157
|
+
lruTime = entry.lastAccessed;
|
|
158
|
+
lruKey = key;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (lruKey) {
|
|
163
|
+
const entry = this.cache.get(lruKey);
|
|
164
|
+
this.stats.bytesStored -= entry.size;
|
|
165
|
+
this.cache.delete(lruKey);
|
|
166
|
+
this.stats.evictions++;
|
|
167
|
+
logger.debug(`Cache LRU EVICTION: ${lruKey}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Cleanup de entries expiradas
|
|
173
|
+
*/
|
|
174
|
+
cleanup() {
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
let expired = 0;
|
|
177
|
+
|
|
178
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
179
|
+
if (entry.expiresAt < now) {
|
|
180
|
+
this.stats.bytesStored -= entry.size;
|
|
181
|
+
this.cache.delete(key);
|
|
182
|
+
expired++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (expired > 0) {
|
|
187
|
+
logger.debug(`Cache CLEANUP: ${expired} expired entries removed`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Obter estatísticas do cache
|
|
193
|
+
*/
|
|
194
|
+
getStats() {
|
|
195
|
+
const total = this.stats.hits + this.stats.misses;
|
|
196
|
+
const hitRate = total > 0 ? ((this.stats.hits / total) * 100).toFixed(2) : 0;
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
...this.stats,
|
|
200
|
+
hitRate: `${hitRate}%`,
|
|
201
|
+
cacheSize: this.cache.size,
|
|
202
|
+
bytesStored: this.stats.bytesStored,
|
|
203
|
+
maxSize: this.maxSize
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Limpar cache completamente
|
|
209
|
+
*/
|
|
210
|
+
clear() {
|
|
211
|
+
this.cache.clear();
|
|
212
|
+
this.stats.bytesStored = 0;
|
|
213
|
+
logger.debug('Cache CLEARED');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Destruir e parar cleanup
|
|
218
|
+
*/
|
|
219
|
+
destroy() {
|
|
220
|
+
clearInterval(this.cleanupInterval);
|
|
221
|
+
this.clear();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = CacheEngine;
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Geo-Restrictions Handler for CloudFront
|
|
5
|
+
* Manages geographic restrictions for distributions based on country codes
|
|
6
|
+
*/
|
|
7
|
+
class GeoRestrictions {
|
|
8
|
+
constructor() {
|
|
9
|
+
// ISO 3166-1 alpha-2 country code validation
|
|
10
|
+
this.validCountryCodes = this._initializeCountryCodes();
|
|
11
|
+
this.requestCache = new Map();
|
|
12
|
+
this.cacheExpiry = 10 * 60 * 1000; // 10 minutes
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if a request should be allowed based on geo-restrictions
|
|
17
|
+
* @param {object} geoRestriction - GeoRestriction object from Distribution
|
|
18
|
+
* @param {string} clientCountry - Country code of the client (ISO 3166-1 alpha-2)
|
|
19
|
+
* @param {object} [requestContext] - Additional context for logging/debugging
|
|
20
|
+
* @returns {object} Geo-restriction check result
|
|
21
|
+
*/
|
|
22
|
+
checkGeoRestriction(geoRestriction, clientCountry, requestContext = {}) {
|
|
23
|
+
if (!geoRestriction || !geoRestriction.restrictionType) {
|
|
24
|
+
return {
|
|
25
|
+
isAllowed: true,
|
|
26
|
+
reason: 'NO_RESTRICTIONS'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const normalizedCountry = clientCountry?.toUpperCase();
|
|
31
|
+
const { restrictionType, locations = [] } = geoRestriction;
|
|
32
|
+
|
|
33
|
+
// Validate country code
|
|
34
|
+
if (!this._isValidCountryCode(normalizedCountry)) {
|
|
35
|
+
return {
|
|
36
|
+
isAllowed: false,
|
|
37
|
+
reason: 'INVALID_COUNTRY_CODE',
|
|
38
|
+
country: clientCountry,
|
|
39
|
+
error: `Invalid country code: ${clientCountry}`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let isAllowed = false;
|
|
44
|
+
let reason = '';
|
|
45
|
+
|
|
46
|
+
if (restrictionType === 'whitelist') {
|
|
47
|
+
isAllowed = locations.includes(normalizedCountry);
|
|
48
|
+
reason = isAllowed ? 'WHITELISTED_COUNTRY' : 'COUNTRY_NOT_WHITELISTED';
|
|
49
|
+
} else if (restrictionType === 'blacklist') {
|
|
50
|
+
isAllowed = !locations.includes(normalizedCountry);
|
|
51
|
+
reason = isAllowed ? 'COUNTRY_NOT_BLACKLISTED' : 'BLACKLISTED_COUNTRY';
|
|
52
|
+
} else {
|
|
53
|
+
return {
|
|
54
|
+
isAllowed: true,
|
|
55
|
+
reason: 'UNKNOWN_RESTRICTION_TYPE'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
isAllowed: isAllowed,
|
|
61
|
+
reason: reason,
|
|
62
|
+
country: normalizedCountry,
|
|
63
|
+
restrictionType: restrictionType,
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
requestContext: requestContext
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate geo-restriction configuration
|
|
71
|
+
* @param {object} geoRestriction - GeoRestriction object
|
|
72
|
+
* @returns {object} Validation result
|
|
73
|
+
*/
|
|
74
|
+
validateGeoRestriction(geoRestriction) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
const warnings = [];
|
|
77
|
+
|
|
78
|
+
if (!geoRestriction) {
|
|
79
|
+
return {
|
|
80
|
+
isValid: true,
|
|
81
|
+
message: 'No geo-restrictions configured'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Validate restriction type
|
|
86
|
+
const validTypes = ['whitelist', 'blacklist'];
|
|
87
|
+
if (!validTypes.includes(geoRestriction.restrictionType)) {
|
|
88
|
+
errors.push(
|
|
89
|
+
`Invalid restriction type: ${geoRestriction.restrictionType}. Must be 'whitelist' or 'blacklist'`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate locations
|
|
94
|
+
if (!Array.isArray(geoRestriction.locations)) {
|
|
95
|
+
errors.push('Locations must be an array');
|
|
96
|
+
} else {
|
|
97
|
+
const invalidCodes = [];
|
|
98
|
+
for (const code of geoRestriction.locations) {
|
|
99
|
+
if (!this._isValidCountryCode(code)) {
|
|
100
|
+
invalidCodes.push(code);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (invalidCodes.length > 0) {
|
|
105
|
+
errors.push(
|
|
106
|
+
`Invalid country codes: ${invalidCodes.join(', ')}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (geoRestriction.locations.length === 0) {
|
|
111
|
+
warnings.push('No locations specified in geo-restriction');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Warn if quantity doesn't match
|
|
115
|
+
if (
|
|
116
|
+
geoRestriction.quantity !== geoRestriction.locations.length
|
|
117
|
+
) {
|
|
118
|
+
warnings.push(
|
|
119
|
+
`Quantity mismatch: specified ${geoRestriction.quantity} but has ${geoRestriction.locations.length} locations`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
isValid: errors.length === 0,
|
|
126
|
+
errors: errors,
|
|
127
|
+
warnings: warnings
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get country information
|
|
133
|
+
* @param {string} countryCode - ISO 3166-1 alpha-2 country code
|
|
134
|
+
* @returns {object} Country information
|
|
135
|
+
*/
|
|
136
|
+
getCountryInfo(countryCode) {
|
|
137
|
+
const code = countryCode?.toUpperCase();
|
|
138
|
+
|
|
139
|
+
if (!this._isValidCountryCode(code)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
code: code,
|
|
145
|
+
name: this._getCountryName(code),
|
|
146
|
+
region: this._getCountryRegion(code)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get list of all valid country codes
|
|
152
|
+
* @returns {string[]} Array of ISO 3166-1 alpha-2 country codes
|
|
153
|
+
*/
|
|
154
|
+
getAllCountryCodes() {
|
|
155
|
+
return Array.from(this.validCountryCodes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get countries by region
|
|
160
|
+
* @param {string} region - Region name (e.g., 'EU', 'ASIA', 'AMERICAS')
|
|
161
|
+
* @returns {string[]} Array of country codes
|
|
162
|
+
*/
|
|
163
|
+
getCountriesByRegion(region) {
|
|
164
|
+
const regionCode = region?.toUpperCase();
|
|
165
|
+
const countries = [];
|
|
166
|
+
|
|
167
|
+
const regionMap = {
|
|
168
|
+
'EU': ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'],
|
|
169
|
+
'ASIA': ['CN', 'JP', 'KR', 'IN', 'TH', 'VN', 'SG', 'MY', 'PH', 'ID', 'HK', 'TW', 'BD', 'PK', 'LK', 'KZ', 'UZ', 'TM', 'KG'],
|
|
170
|
+
'AMERICAS': ['US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'PE', 'VE', 'EC', 'CU', 'DO', 'JM', 'TT'],
|
|
171
|
+
'AFRICA': ['NG', 'EG', 'ZA', 'KE', 'ET', 'GH', 'TZ', 'UG', 'DZ', 'MA', 'TN', 'SD', 'AO', 'ZM', 'ZW'],
|
|
172
|
+
'OCEANIA': ['AU', 'NZ', 'FJ', 'PG', 'SB', 'VU']
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return regionMap[regionCode] || [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Generate a geo-restriction report
|
|
180
|
+
* @param {object} geoRestriction - GeoRestriction object
|
|
181
|
+
* @returns {object} Detailed report
|
|
182
|
+
*/
|
|
183
|
+
generateReport(geoRestriction) {
|
|
184
|
+
if (!geoRestriction) {
|
|
185
|
+
return {
|
|
186
|
+
status: 'NO_RESTRICTIONS',
|
|
187
|
+
report: 'No geo-restrictions configured'
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const validation = this.validateGeoRestriction(geoRestriction);
|
|
192
|
+
const locations = geoRestriction.locations || [];
|
|
193
|
+
const countryDetails = locations.map(code => ({
|
|
194
|
+
code: code,
|
|
195
|
+
name: this._getCountryName(code),
|
|
196
|
+
region: this._getCountryRegion(code)
|
|
197
|
+
}));
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
status: 'ACTIVE',
|
|
201
|
+
restrictionType: geoRestriction.restrictionType,
|
|
202
|
+
totalCountries: locations.length,
|
|
203
|
+
countries: countryDetails,
|
|
204
|
+
validation: validation,
|
|
205
|
+
timestamp: new Date().toISOString()
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Estimate impact of geo-restrictions (rough statistics)
|
|
211
|
+
* @param {object} geoRestriction - GeoRestriction object
|
|
212
|
+
* @returns {object} Impact estimate
|
|
213
|
+
*/
|
|
214
|
+
estimateRestrictionImpact(geoRestriction) {
|
|
215
|
+
if (!geoRestriction || !geoRestriction.locations) {
|
|
216
|
+
return {
|
|
217
|
+
estimatedAffected: 0,
|
|
218
|
+
estimatedAccessible: 100
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Rough estimates for demonstration (these would come from real data in production)
|
|
223
|
+
const globalPopulation = 100; // Represents 100%
|
|
224
|
+
const locationCount = geoRestriction.locations.length;
|
|
225
|
+
const countryCount = this.getAllCountryCodes().length;
|
|
226
|
+
|
|
227
|
+
let estimatedAffected = (locationCount / countryCount) * globalPopulation;
|
|
228
|
+
|
|
229
|
+
if (geoRestriction.restrictionType === 'whitelist') {
|
|
230
|
+
// Whitelisting specific countries
|
|
231
|
+
estimatedAffected = (locationCount / countryCount) * globalPopulation;
|
|
232
|
+
} else if (geoRestriction.restrictionType === 'blacklist') {
|
|
233
|
+
// Blacklisting specific countries
|
|
234
|
+
estimatedAffected = (locationCount / countryCount) * globalPopulation;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
estimatedAffected: Math.round(estimatedAffected * 100) / 100,
|
|
239
|
+
estimatedAccessible: Math.round((globalPopulation - estimatedAffected) * 100) / 100,
|
|
240
|
+
locationCount: locationCount,
|
|
241
|
+
restrictionType: geoRestriction.restrictionType
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create a geo-restriction from country names (convenience method)
|
|
247
|
+
* @param {string} type - 'whitelist' or 'blacklist'
|
|
248
|
+
* @param {string[]} countryNames - Array of country names
|
|
249
|
+
* @returns {object} Geo-restriction object
|
|
250
|
+
*/
|
|
251
|
+
createFromCountryNames(type, countryNames) {
|
|
252
|
+
const countryCodes = countryNames
|
|
253
|
+
.map(name => this._getCountryCodeByName(name))
|
|
254
|
+
.filter(code => code !== null);
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
restrictionType: type,
|
|
258
|
+
quantity: countryCodes.length,
|
|
259
|
+
locations: countryCodes
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Clear cache
|
|
265
|
+
*/
|
|
266
|
+
clearCache() {
|
|
267
|
+
this.requestCache.clear();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Private Methods
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
_initializeCountryCodes() {
|
|
275
|
+
// ISO 3166-1 alpha-2 country codes
|
|
276
|
+
return new Set([
|
|
277
|
+
'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ',
|
|
278
|
+
'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ',
|
|
279
|
+
'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ',
|
|
280
|
+
'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ',
|
|
281
|
+
'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET',
|
|
282
|
+
'FI', 'FJ', 'FK', 'FM', 'FO', 'FR',
|
|
283
|
+
'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY',
|
|
284
|
+
'HK', 'HM', 'HN', 'HR', 'HT', 'HU',
|
|
285
|
+
'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT',
|
|
286
|
+
'JE', 'JM', 'JO', 'JP',
|
|
287
|
+
'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ',
|
|
288
|
+
'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY',
|
|
289
|
+
'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ',
|
|
290
|
+
'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ',
|
|
291
|
+
'OM',
|
|
292
|
+
'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY',
|
|
293
|
+
'QA',
|
|
294
|
+
'RE', 'RO', 'RS', 'RU', 'RW',
|
|
295
|
+
'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ',
|
|
296
|
+
'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ',
|
|
297
|
+
'UA', 'UG', 'UM', 'US', 'UY', 'UZ',
|
|
298
|
+
'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU',
|
|
299
|
+
'WF', 'WS',
|
|
300
|
+
'YE', 'YT',
|
|
301
|
+
'ZA', 'ZM', 'ZW'
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
_isValidCountryCode(code) {
|
|
306
|
+
if (!code || typeof code !== 'string') return false;
|
|
307
|
+
return this.validCountryCodes.has(code.toUpperCase());
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_getCountryName(code) {
|
|
311
|
+
const countryNames = {
|
|
312
|
+
'US': 'United States',
|
|
313
|
+
'GB': 'United Kingdom',
|
|
314
|
+
'CA': 'Canada',
|
|
315
|
+
'AU': 'Australia',
|
|
316
|
+
'JP': 'Japan',
|
|
317
|
+
'DE': 'Germany',
|
|
318
|
+
'FR': 'France',
|
|
319
|
+
'BR': 'Brazil',
|
|
320
|
+
'IN': 'India',
|
|
321
|
+
'CN': 'China',
|
|
322
|
+
'MX': 'Mexico',
|
|
323
|
+
'ZA': 'South Africa',
|
|
324
|
+
'SG': 'Singapore',
|
|
325
|
+
'NZ': 'New Zealand',
|
|
326
|
+
'NL': 'Netherlands',
|
|
327
|
+
'SE': 'Sweden',
|
|
328
|
+
'NO': 'Norway',
|
|
329
|
+
'ES': 'Spain',
|
|
330
|
+
'IT': 'Italy',
|
|
331
|
+
'KR': 'South Korea',
|
|
332
|
+
// Add more as needed
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return countryNames[code] || code;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_getCountryRegion(code) {
|
|
339
|
+
const regionMap = {
|
|
340
|
+
'US': 'AMERICAS', 'CA': 'AMERICAS', 'MX': 'AMERICAS', 'BR': 'AMERICAS', 'AR': 'AMERICAS',
|
|
341
|
+
'GB': 'EU', 'DE': 'EU', 'FR': 'EU', 'IT': 'EU', 'ES': 'EU', 'NL': 'EU', 'SE': 'EU', 'NO': 'EU',
|
|
342
|
+
'JP': 'ASIA', 'CN': 'ASIA', 'IN': 'ASIA', 'SG': 'ASIA', 'KR': 'ASIA', 'AU': 'OCEANIA', 'NZ': 'OCEANIA',
|
|
343
|
+
'ZA': 'AFRICA', 'EG': 'AFRICA',
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return regionMap[code] || 'UNKNOWN';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
_getCountryCodeByName(name) {
|
|
350
|
+
const nameToCode = {
|
|
351
|
+
'United States': 'US',
|
|
352
|
+
'United Kingdom': 'GB',
|
|
353
|
+
'Canada': 'CA',
|
|
354
|
+
'Australia': 'AU',
|
|
355
|
+
'Japan': 'JP',
|
|
356
|
+
'Germany': 'DE',
|
|
357
|
+
'France': 'FR',
|
|
358
|
+
'Brazil': 'BR',
|
|
359
|
+
'India': 'IN',
|
|
360
|
+
'China': 'CN',
|
|
361
|
+
'Mexico': 'MX',
|
|
362
|
+
'South Africa': 'ZA',
|
|
363
|
+
'Singapore': 'SG',
|
|
364
|
+
'New Zealand': 'NZ',
|
|
365
|
+
'Netherlands': 'NL',
|
|
366
|
+
'Sweden': 'SE',
|
|
367
|
+
'Norway': 'NO',
|
|
368
|
+
'Spain': 'ES',
|
|
369
|
+
'Italy': 'IT',
|
|
370
|
+
'South Korea': 'KR',
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return nameToCode[name] || null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = GeoRestrictions;
|