@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,162 @@
1
+ /**
2
+ * OpenSearch Store - Persistência de índices e documentos em JSON
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class OpenSearchStore {
10
+ constructor(dataDir) {
11
+ this.dataDir = dataDir;
12
+ this.indicesDir = path.join(dataDir, 'opensearch');
13
+ this.metadataFile = path.join(this.indicesDir, '__indices__.json');
14
+
15
+ if (!fs.existsSync(this.indicesDir)) {
16
+ fs.mkdirSync(this.indicesDir, { recursive: true });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Carrega metadados de todos os índices
22
+ */
23
+ loadMetadata() {
24
+ if (fs.existsSync(this.metadataFile)) {
25
+ try {
26
+ const data = fs.readFileSync(this.metadataFile, 'utf8');
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
30
+ return {};
31
+ }
32
+ }
33
+ return {};
34
+ }
35
+
36
+ /**
37
+ * Salva metadados de todos os índices
38
+ */
39
+ saveMetadata(metadata) {
40
+ try {
41
+ fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
42
+ logger.debug(`Metadados salvos em ${this.metadataFile}`);
43
+ } catch (error) {
44
+ logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Obtém caminho do arquivo de índice
50
+ */
51
+ getIndexPath(indexName) {
52
+ return path.join(this.indicesDir, `${indexName}.json`);
53
+ }
54
+
55
+ /**
56
+ * Carrega documentos de um índice
57
+ */
58
+ loadDocuments(indexName) {
59
+ const indexPath = this.getIndexPath(indexName);
60
+
61
+ if (!fs.existsSync(indexPath)) {
62
+ return [];
63
+ }
64
+
65
+ try {
66
+ const data = fs.readFileSync(indexPath, 'utf8');
67
+ return JSON.parse(data);
68
+ } catch (error) {
69
+ logger.warn(`Erro ao ler índice ${indexName}: ${error.message}`);
70
+ return [];
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Salva documentos de um índice
76
+ */
77
+ saveDocuments(indexName, documents) {
78
+ try {
79
+ const indexPath = this.getIndexPath(indexName);
80
+ fs.writeFileSync(indexPath, JSON.stringify(documents, null, 2), 'utf8');
81
+ logger.debug(`${documents.length} documentos salvos no índice ${indexName}`);
82
+ } catch (error) {
83
+ logger.error(`Erro ao salvar índice ${indexName}: ${error.message}`);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Adiciona documento ao índice
89
+ */
90
+ addDocument(indexName, document) {
91
+ const documents = this.loadDocuments(indexName);
92
+
93
+ // Remove se já existe (atualização)
94
+ const index = documents.findIndex(doc => doc._id === document._id);
95
+ if (index !== -1) {
96
+ documents[index] = document;
97
+ } else {
98
+ documents.push(document);
99
+ }
100
+
101
+ this.saveDocuments(indexName, documents);
102
+ }
103
+
104
+ /**
105
+ * Remove documento do índice
106
+ */
107
+ removeDocument(indexName, documentId) {
108
+ const documents = this.loadDocuments(indexName);
109
+ const filtered = documents.filter(doc => doc._id !== documentId);
110
+
111
+ if (filtered.length < documents.length) {
112
+ this.saveDocuments(indexName, filtered);
113
+ return true;
114
+ }
115
+
116
+ return false;
117
+ }
118
+
119
+ /**
120
+ * Deleta índice completamente
121
+ */
122
+ deleteIndex(indexName) {
123
+ try {
124
+ const indexPath = this.getIndexPath(indexName);
125
+ if (fs.existsSync(indexPath)) {
126
+ fs.unlinkSync(indexPath);
127
+ logger.debug(`Índice ${indexName} deletado`);
128
+ }
129
+ } catch (error) {
130
+ logger.error(`Erro ao deletar índice ${indexName}: ${error.message}`);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Calcula tamanho total do índice em bytes
136
+ */
137
+ getIndexSize(indexName) {
138
+ const documents = this.loadDocuments(indexName);
139
+ return documents.reduce((sum, doc) => sum + JSON.stringify(doc._source).length, 0);
140
+ }
141
+
142
+ /**
143
+ * Limpa todos os índices
144
+ */
145
+ clear() {
146
+ try {
147
+ if (fs.existsSync(this.indicesDir)) {
148
+ const files = fs.readdirSync(this.indicesDir);
149
+ files.forEach(file => {
150
+ if (file.endsWith('.json')) {
151
+ fs.unlinkSync(path.join(this.indicesDir, file));
152
+ }
153
+ });
154
+ logger.debug('Todos os índices foram limpos');
155
+ }
156
+ } catch (error) {
157
+ logger.error(`Erro ao limpar índices: ${error.message}`);
158
+ }
159
+ }
160
+ }
161
+
162
+ module.exports = OpenSearchStore;
@@ -0,0 +1,321 @@
1
+ /**
2
+ * AWS Control Tower Simulator
3
+ * Gerencia landing zones, guardrails e conformidade
4
+ */
5
+
6
+ const LocalStore = require('../../utils/local-store');
7
+ const logger = require('../../utils/logger');
8
+ const path = require('path');
9
+ const { v4: uuidv4 } = require('uuid');
10
+
11
+ class ControlTowerSimulator {
12
+ constructor(config, organizationsSimulator) {
13
+ this.config = config;
14
+ this.organizationsSimulator = organizationsSimulator;
15
+
16
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
17
+ this.dataDir = path.join(dataDir, 'control-tower');
18
+
19
+ this.store = new LocalStore(this.dataDir);
20
+
21
+ // Landing zones
22
+ this.landingZones = new Map();
23
+
24
+ // Guardrails (Preventive e Detective)
25
+ this.guardrails = new Map();
26
+ this.guardrailStatus = new Map(); // Mapear OU -> guardrails ativos
27
+
28
+ // Compliance checks
29
+ this.complianceChecks = new Map();
30
+ }
31
+
32
+ async initialize() {
33
+ logger.debug('Inicializando Control Tower Simulator...');
34
+
35
+ this.createDefaultGuardrails();
36
+ logger.debug(`✅ Control Tower Simulator inicializado`);
37
+ }
38
+
39
+ /**
40
+ * Cria guardrails padrão
41
+ */
42
+ createDefaultGuardrails() {
43
+ // Preventive Guardrails
44
+ this.createGuardrail('DisablePublicS3Buckets', 'PREVENTIVE', {
45
+ description: 'Bloqueia criação de buckets S3 públicos',
46
+ service: 's3',
47
+ action: 'PutBucketPublicAccessBlock'
48
+ });
49
+
50
+ this.createGuardrail('RequireMFA', 'PREVENTIVE', {
51
+ description: 'Requer MFA para operações sensíveis',
52
+ service: 'iam',
53
+ action: 'iam:*'
54
+ });
55
+
56
+ this.createGuardrail('RestrictEC2InstanceTypes', 'PREVENTIVE', {
57
+ description: 'Restringe tipos de instância EC2 a tipos aprovados',
58
+ service: 'ec2',
59
+ action: 'ec2:RunInstances'
60
+ });
61
+
62
+ // Detective Guardrails
63
+ this.createGuardrail('LogAllAPIActivity', 'DETECTIVE', {
64
+ description: 'Detecta falta de CloudTrail logging',
65
+ service: 'cloudtrail',
66
+ action: 'cloudtrail:StartLogging'
67
+ });
68
+
69
+ this.createGuardrail('DetectRootAccountUsage', 'DETECTIVE', {
70
+ description: 'Detecta uso de conta root',
71
+ service: 'iam',
72
+ action: 'root-account-usage'
73
+ });
74
+
75
+ this.createGuardrail('DetectUnencryptedData', 'DETECTIVE', {
76
+ description: 'Detecta dados não criptografados',
77
+ service: 'kms',
78
+ action: 'kms:Encrypt'
79
+ });
80
+ }
81
+
82
+ /**
83
+ * Cria um guardrail
84
+ */
85
+ createGuardrail(name, type, config = {}) {
86
+ const guardrailId = uuidv4().substring(0, 12);
87
+
88
+ const guardrail = {
89
+ id: guardrailId,
90
+ name: name,
91
+ type: type, // PREVENTIVE ou DETECTIVE
92
+ arn: `arn:aws:controltower::123456789012:guardrail/${guardrailId}`,
93
+ description: config.description || '',
94
+ service: config.service,
95
+ action: config.action,
96
+ createdAt: new Date().toISOString(),
97
+ status: 'ACTIVE'
98
+ };
99
+
100
+ this.guardrails.set(guardrailId, guardrail);
101
+ return guardrail;
102
+ }
103
+
104
+ /**
105
+ * Estabelece landing zone
106
+ */
107
+ setupLandingZone(organizationRootId, config = {}) {
108
+ const landingZoneId = uuidv4().substring(0, 12);
109
+
110
+ const landingZone = {
111
+ id: landingZoneId,
112
+ arn: `arn:aws:controltower::123456789012:landingzone/${landingZoneId}`,
113
+ organizationRootId: organizationRootId,
114
+ status: 'ACTIVE',
115
+ createdAt: new Date().toISOString(),
116
+ lastUpdatedAt: new Date().toISOString(),
117
+ version: '2.0',
118
+ // OUs criadas pela Landing Zone
119
+ ous: {
120
+ managementOu: null,
121
+ securityOu: null,
122
+ sandboxOu: null
123
+ },
124
+ enabledGuardrails: [],
125
+ complianceStatus: 'COMPLIANT'
126
+ };
127
+
128
+ this.landingZones.set(landingZoneId, landingZone);
129
+
130
+ logger.info(`✅ Landing Zone estabelecida: ${landingZoneId}`);
131
+
132
+ return {
133
+ LandingZone: landingZone
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Habilita guardrail em uma OU
139
+ */
140
+ enableGuardrail(guardrailId, targetOUId) {
141
+ const guardrail = this.guardrails.get(guardrailId);
142
+ if (!guardrail) {
143
+ throw new Error(`Guardrail '${guardrailId}' not found`);
144
+ }
145
+
146
+ // Valida OU
147
+ const organization = this.organizationsSimulator.organization;
148
+ const ou = organization.getOrganizationalUnit(targetOUId);
149
+ if (!ou) {
150
+ throw new Error(`OU '${targetOUId}' not found`);
151
+ }
152
+
153
+ if (!this.guardrailStatus.has(targetOUId)) {
154
+ this.guardrailStatus.set(targetOUId, []);
155
+ }
156
+
157
+ const enabledGuardrails = this.guardrailStatus.get(targetOUId);
158
+ if (!enabledGuardrails.includes(guardrailId)) {
159
+ enabledGuardrails.push(guardrailId);
160
+ }
161
+
162
+ logger.info(`✅ Guardrail habilitado: ${guardrail.name} em OU ${targetOUId}`);
163
+
164
+ return {
165
+ GuardrailStatus: {
166
+ GuardrailId: guardrailId,
167
+ TargetOUId: targetOUId,
168
+ Status: 'ENABLED'
169
+ }
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Desabilita guardrail em uma OU
175
+ */
176
+ disableGuardrail(guardrailId, targetOUId) {
177
+ const guardrails = this.guardrailStatus.get(targetOUId);
178
+ if (!guardrails) {
179
+ throw new Error(`No guardrails enabled for OU '${targetOUId}'`);
180
+ }
181
+
182
+ const index = guardrails.indexOf(guardrailId);
183
+ if (index > -1) {
184
+ guardrails.splice(index, 1);
185
+ }
186
+
187
+ logger.info(`✅ Guardrail desabilitado: ${guardrailId} em OU ${targetOUId}`);
188
+
189
+ return {};
190
+ }
191
+
192
+ /**
193
+ * Lista guardrails disponíveis
194
+ */
195
+ listGuardrails() {
196
+ return {
197
+ Guardrails: Array.from(this.guardrails.values())
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Lista guardrails habilitados em uma OU
203
+ */
204
+ getGuardrailsForOU(ouId) {
205
+ const enabledIds = this.guardrailStatus.get(ouId) || [];
206
+ const enabledGuardrails = enabledIds.map(id => this.guardrails.get(id));
207
+
208
+ return {
209
+ Guardrails: enabledGuardrails.filter(g => g)
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Verifica conformidade de um account
215
+ */
216
+ checkCompliance(accountId) {
217
+ const organization = this.organizationsSimulator.organization;
218
+ const account = organization.getAccount(accountId);
219
+
220
+ if (!account) {
221
+ throw new Error(`Account '${accountId}' not found`);
222
+ }
223
+
224
+ // Coleta guardrails aplicáveis
225
+ const applicableGuardrails = [];
226
+
227
+ let currentOUId = account.parentId;
228
+ while (currentOUId) {
229
+ const guardrails = this.guardrailStatus.get(currentOUId) || [];
230
+ applicableGuardrails.push(...guardrails);
231
+
232
+ const ou = organization.getOrganizationalUnit(currentOUId);
233
+ currentOUId = ou?.parentId;
234
+ }
235
+
236
+ // Simula verificação de conformidade
237
+ const results = {
238
+ AccountId: accountId,
239
+ ComplianceStatus: 'COMPLIANT',
240
+ Guardrails: applicableGuardrails.map(guardrailId => {
241
+ const guardrail = this.guardrails.get(guardrailId);
242
+ return {
243
+ GuardrailId: guardrailId,
244
+ GuardrailName: guardrail?.name,
245
+ Status: 'COMPLIANT', // Em produção seria baseado em verificações reais
246
+ LastCheckedAt: new Date().toISOString()
247
+ };
248
+ })
249
+ };
250
+
251
+ return results;
252
+ }
253
+
254
+ /**
255
+ * Gera relatório de conformidade
256
+ */
257
+ generateComplianceReport() {
258
+ const organization = this.organizationsSimulator.organization;
259
+ const accounts = organization.listAccounts();
260
+
261
+ const report = {
262
+ ReportId: uuidv4().substring(0, 12),
263
+ GeneratedAt: new Date().toISOString(),
264
+ AccountsChecked: accounts.length,
265
+ CompliantAccounts: 0,
266
+ NonCompliantAccounts: 0,
267
+ AccountResults: accounts.map(account => {
268
+ const compliance = this.checkCompliance(account.id);
269
+ if (compliance.ComplianceStatus === 'COMPLIANT') {
270
+ report.CompliantAccounts++;
271
+ } else {
272
+ report.NonCompliantAccounts++;
273
+ }
274
+ return compliance;
275
+ })
276
+ };
277
+
278
+ return report;
279
+ }
280
+
281
+ /**
282
+ * Obtém status da landing zone
283
+ */
284
+ getLandingZoneStatus(landingZoneId) {
285
+ const landingZone = this.landingZones.get(landingZoneId);
286
+ if (!landingZone) {
287
+ throw new Error(`Landing Zone '${landingZoneId}' not found`);
288
+ }
289
+
290
+ return {
291
+ LandingZone: landingZone
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Obtém estatísticas
297
+ */
298
+ getStats() {
299
+ return {
300
+ landingZonesCount: this.landingZones.size,
301
+ guardrailsCount: this.guardrails.size,
302
+ preventiveGuardrailsCount: Array.from(this.guardrails.values()).filter(g => g.type === 'PREVENTIVE').length,
303
+ detectiveGuardrailsCount: Array.from(this.guardrails.values()).filter(g => g.type === 'DETECTIVE').length,
304
+ enabledGuardrailsCount: Array.from(this.guardrailStatus.values()).reduce((sum, arr) => sum + arr.length, 0)
305
+ };
306
+ }
307
+
308
+ /**
309
+ * Reset
310
+ */
311
+ async reset() {
312
+ this.landingZones.clear();
313
+ this.guardrailStatus.clear();
314
+ this.guardrails.clear();
315
+
316
+ this.createDefaultGuardrails();
317
+ logger.info('✅ Control Tower Simulator resetado');
318
+ }
319
+ }
320
+
321
+ module.exports = ControlTowerSimulator;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Control Tower Store - Persistência de Landing Zones e Guardrails
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class ControlTowerStore {
10
+ constructor(dataDir) {
11
+ this.dataDir = dataDir;
12
+ this.controlTowerDir = path.join(dataDir, 'control-tower');
13
+ this.metadataFile = path.join(this.controlTowerDir, '__metadata__.json');
14
+
15
+ if (!fs.existsSync(this.controlTowerDir)) {
16
+ fs.mkdirSync(this.controlTowerDir, { recursive: true });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Carrega landing zones
22
+ */
23
+ loadLandingZones() {
24
+ const landingZonesFile = path.join(this.controlTowerDir, '__landing_zones__.json');
25
+
26
+ if (fs.existsSync(landingZonesFile)) {
27
+ try {
28
+ const data = fs.readFileSync(landingZonesFile, 'utf8');
29
+ return JSON.parse(data);
30
+ } catch (error) {
31
+ logger.warn(`Erro ao ler landing zones: ${error.message}`);
32
+ return {};
33
+ }
34
+ }
35
+ return {};
36
+ }
37
+
38
+ /**
39
+ * Salva landing zones
40
+ */
41
+ saveLandingZones(landingZones) {
42
+ try {
43
+ const landingZonesFile = path.join(this.controlTowerDir, '__landing_zones__.json');
44
+ const data = {};
45
+
46
+ for (const [lzId, lz] of landingZones) {
47
+ data[lzId] = lz;
48
+ }
49
+
50
+ fs.writeFileSync(landingZonesFile, JSON.stringify(data, null, 2), 'utf8');
51
+ logger.debug(`${Object.keys(data).length} landing zones salvas`);
52
+ } catch (error) {
53
+ logger.error(`Erro ao salvar landing zones: ${error.message}`);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Carrega guardrails
59
+ */
60
+ loadGuardrails() {
61
+ const guardrailsFile = path.join(this.controlTowerDir, '__guardrails__.json');
62
+
63
+ if (fs.existsSync(guardrailsFile)) {
64
+ try {
65
+ const data = fs.readFileSync(guardrailsFile, 'utf8');
66
+ return JSON.parse(data);
67
+ } catch (error) {
68
+ logger.warn(`Erro ao ler guardrails: ${error.message}`);
69
+ return {};
70
+ }
71
+ }
72
+ return {};
73
+ }
74
+
75
+ /**
76
+ * Salva guardrails
77
+ */
78
+ saveGuardrails(guardrails) {
79
+ try {
80
+ const guardrailsFile = path.join(this.controlTowerDir, '__guardrails__.json');
81
+ const data = {};
82
+
83
+ for (const [guardrailId, guardrail] of guardrails) {
84
+ data[guardrailId] = guardrail;
85
+ }
86
+
87
+ fs.writeFileSync(guardrailsFile, JSON.stringify(data, null, 2), 'utf8');
88
+ logger.debug(`${Object.keys(data).length} guardrails salvos`);
89
+ } catch (error) {
90
+ logger.error(`Erro ao salvar guardrails: ${error.message}`);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Carrega status de guardrails por OU
96
+ */
97
+ loadGuardrailStatus() {
98
+ const statusFile = path.join(this.controlTowerDir, '__guardrail_status__.json');
99
+
100
+ if (fs.existsSync(statusFile)) {
101
+ try {
102
+ const data = fs.readFileSync(statusFile, 'utf8');
103
+ return JSON.parse(data);
104
+ } catch (error) {
105
+ logger.warn(`Erro ao ler status de guardrails: ${error.message}`);
106
+ return {};
107
+ }
108
+ }
109
+ return {};
110
+ }
111
+
112
+ /**
113
+ * Salva status de guardrails
114
+ */
115
+ saveGuardrailStatus(guardrailStatus) {
116
+ try {
117
+ const statusFile = path.join(this.controlTowerDir, '__guardrail_status__.json');
118
+ const data = {};
119
+
120
+ for (const [ouId, guardrails] of guardrailStatus) {
121
+ data[ouId] = guardrails;
122
+ }
123
+
124
+ fs.writeFileSync(statusFile, JSON.stringify(data, null, 2), 'utf8');
125
+ logger.debug(`Status de guardrails salvo`);
126
+ } catch (error) {
127
+ logger.error(`Erro ao salvar status de guardrails: ${error.message}`);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Carrega compliance checks
133
+ */
134
+ loadComplianceChecks() {
135
+ const checksFile = path.join(this.controlTowerDir, '__compliance_checks__.json');
136
+
137
+ if (fs.existsSync(checksFile)) {
138
+ try {
139
+ const data = fs.readFileSync(checksFile, 'utf8');
140
+ return JSON.parse(data);
141
+ } catch (error) {
142
+ logger.warn(`Erro ao ler compliance checks: ${error.message}`);
143
+ return {};
144
+ }
145
+ }
146
+ return {};
147
+ }
148
+
149
+ /**
150
+ * Salva compliance checks
151
+ */
152
+ saveComplianceChecks(checks) {
153
+ try {
154
+ const checksFile = path.join(this.controlTowerDir, '__compliance_checks__.json');
155
+ fs.writeFileSync(checksFile, JSON.stringify(checks, null, 2), 'utf8');
156
+ logger.debug(`Compliance checks salvos`);
157
+ } catch (error) {
158
+ logger.error(`Erro ao salvar compliance checks: ${error.message}`);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Limpa tudo
164
+ */
165
+ clear() {
166
+ try {
167
+ if (fs.existsSync(this.controlTowerDir)) {
168
+ const files = fs.readdirSync(this.controlTowerDir);
169
+ files.forEach(file => {
170
+ if (file.endsWith('.json')) {
171
+ fs.unlinkSync(path.join(this.controlTowerDir, file));
172
+ }
173
+ });
174
+ logger.debug('Todos os dados de Control Tower foram limpos');
175
+ }
176
+ } catch (error) {
177
+ logger.error(`Erro ao limpar Control Tower: ${error.message}`);
178
+ }
179
+ }
180
+ }
181
+
182
+ module.exports = ControlTowerStore;