@gugananuvem/aws-local-simulator 1.0.33 → 1.0.34

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 (79) hide show
  1. package/README.md +834 -834
  2. package/aws-config +153 -153
  3. package/bin/aws-local-simulator.js +63 -63
  4. package/package.json +3 -2
  5. package/src/config/config-loader.js +114 -114
  6. package/src/config/default-config.js +79 -79
  7. package/src/config/env-loader.js +68 -68
  8. package/src/index.js +146 -146
  9. package/src/index.mjs +123 -123
  10. package/src/server.js +463 -463
  11. package/src/services/apigateway/index.js +75 -75
  12. package/src/services/apigateway/server.js +607 -607
  13. package/src/services/apigateway/simulator.js +1405 -1405
  14. package/src/services/athena/index.js +75 -75
  15. package/src/services/athena/server.js +101 -101
  16. package/src/services/athena/simulador.js +998 -998
  17. package/src/services/athena/simulator.js +346 -346
  18. package/src/services/cloudformation/index.js +106 -106
  19. package/src/services/cloudformation/server.js +417 -417
  20. package/src/services/cloudformation/simulador.js +1020 -1020
  21. package/src/services/cloudtrail/index.js +84 -84
  22. package/src/services/cloudtrail/server.js +235 -235
  23. package/src/services/cloudtrail/simulador.js +719 -719
  24. package/src/services/cloudwatch/index.js +84 -84
  25. package/src/services/cloudwatch/server.js +366 -366
  26. package/src/services/cloudwatch/simulador.js +1173 -1173
  27. package/src/services/cognito/index.js +79 -79
  28. package/src/services/cognito/server.js +297 -297
  29. package/src/services/cognito/simulator.js +1992 -1761
  30. package/src/services/config/index.js +96 -96
  31. package/src/services/config/server.js +215 -215
  32. package/src/services/config/simulador.js +1260 -1260
  33. package/src/services/dynamodb/index.js +74 -74
  34. package/src/services/dynamodb/server.js +139 -139
  35. package/src/services/dynamodb/simulator.js +1005 -994
  36. package/src/services/dynamodb/sqlite-store.js +722 -0
  37. package/src/services/ecs/index.js +65 -65
  38. package/src/services/ecs/server.js +235 -235
  39. package/src/services/ecs/simulator.js +844 -844
  40. package/src/services/eventbridge/index.js +89 -89
  41. package/src/services/eventbridge/server.js +209 -209
  42. package/src/services/eventbridge/simulator.js +684 -684
  43. package/src/services/index.js +45 -45
  44. package/src/services/kms/index.js +75 -75
  45. package/src/services/kms/server.js +81 -81
  46. package/src/services/kms/simulator.js +344 -344
  47. package/src/services/lambda/handler-loader.js +183 -183
  48. package/src/services/lambda/index.js +81 -81
  49. package/src/services/lambda/route-registry.js +274 -274
  50. package/src/services/lambda/server.js +191 -191
  51. package/src/services/lambda/simulator.js +364 -364
  52. package/src/services/parameter-store/index.js +80 -80
  53. package/src/services/parameter-store/server.js +50 -50
  54. package/src/services/parameter-store/simulator.js +201 -201
  55. package/src/services/s3/index.js +73 -73
  56. package/src/services/s3/server.js +350 -350
  57. package/src/services/s3/simulator.js +568 -568
  58. package/src/services/secret-manager/index.js +80 -80
  59. package/src/services/secret-manager/server.js +51 -51
  60. package/src/services/secret-manager/simulator.js +182 -182
  61. package/src/services/sns/index.js +89 -89
  62. package/src/services/sns/server.js +607 -607
  63. package/src/services/sns/simulator.js +1482 -1482
  64. package/src/services/sqs/index.js +98 -98
  65. package/src/services/sqs/server.js +360 -360
  66. package/src/services/sqs/simulator.js +509 -509
  67. package/src/services/sts/index.js +37 -37
  68. package/src/services/sts/server.js +144 -144
  69. package/src/services/sts/simulator.js +69 -69
  70. package/src/services/xray/index.js +83 -83
  71. package/src/services/xray/server.js +308 -308
  72. package/src/services/xray/simulador.js +994 -994
  73. package/src/template/aws-config-template.js +87 -87
  74. package/src/template/aws-config-template.mjs +90 -90
  75. package/src/template/config-template.json +203 -203
  76. package/src/utils/aws-config.js +91 -91
  77. package/src/utils/cloudtrail-audit.js +129 -129
  78. package/src/utils/local-store.js +83 -83
  79. package/src/utils/logger.js +59 -59
package/src/server.js CHANGED
@@ -1,463 +1,463 @@
1
- /**
2
- * Servidor Principal - Orquestra todos os serviços
3
- */
4
-
5
- const path = require("path");
6
- const fs = require("fs");
7
- const mkdirp = require("mkdirp");
8
- const express = require("express");
9
- const cors = require("cors");
10
- const logger = require("./utils/logger");
11
-
12
- // Importa serviços
13
- const DynamoDBService = require("./services/dynamodb");
14
- const S3Service = require("./services/s3");
15
- const SQSService = require("./services/sqs");
16
- const LambdaService = require("./services/lambda");
17
- const { SNSService } = require("./services/sns");
18
- const { EventBridgeService } = require("./services/eventbridge");
19
- const CognitoService = require("./services/cognito");
20
- const APIGatewayService = require("./services/apigateway");
21
- const ECSService = require("./services/ecs");
22
- const STSService = require("./services/sts");
23
- const { CloudWatchService } = require("./services/cloudwatch");
24
- const CloudTrailService = require("./services/cloudtrail");
25
- const { KMSService } = require("./services/kms");
26
- const CloudFormationService = require("./services/cloudformation");
27
- const { XRayService } = require("./services/xray");
28
- const { SecretManagerService } = require("./services/secret-manager");
29
- const { ParameterStoreService } = require("./services/parameter-store");
30
- const { ConfigService } = require("./services/config");
31
- const { AthenaService } = require("./services/athena");
32
-
33
- class Server {
34
- constructor(config) {
35
- this.config = config;
36
- this.services = [];
37
- this.servicesMap = new Map();
38
- this.running = false;
39
- this.managementApp = express();
40
- this.managementApp.use(cors());
41
- this.managementApp.use(express.json());
42
- this.managementServer = null;
43
- this.setupDataDir();
44
- this.setupLogLevel();
45
- }
46
-
47
- setupDataDir() {
48
- const dataDir = path.resolve(process.cwd(), this.config.dataDir);
49
- if (!fs.existsSync(dataDir)) {
50
- mkdirp.sync(dataDir);
51
- logger.debug(`📁 Diretório de dados criado: ${dataDir}`);
52
- }
53
- process.env.AWS_LOCAL_SIMULATOR_DATA_DIR = dataDir;
54
- }
55
-
56
- setupLogLevel() {
57
- logger.setLevel(this.config.logLevel);
58
- logger.info(`📝 Nível de log: ${this.config.logLevel}`);
59
- }
60
-
61
- async start() {
62
- if (this.running) {
63
- logger.warn("Servidor já está rodando");
64
- return;
65
- }
66
-
67
- logger.info("\n🚀 Iniciando AWS Local Simulator...\n");
68
-
69
- try {
70
- await this.initializeServices();
71
- await this.startServices();
72
- this.setupManagementRoutes();
73
- await this.startManagementServer();
74
-
75
- this.running = true;
76
- this.printStatus();
77
- } catch (error) {
78
- logger.error("❌ Erro ao iniciar servidor:", error);
79
- throw error;
80
- }
81
- }
82
-
83
- setupManagementRoutes() {
84
- const app = this.managementApp;
85
-
86
- // GET /__admin/services — returns all services status
87
- app.get("/__admin/services", (req, res) => {
88
- const registry = this.buildServiceRegistry();
89
- const services = registry.map((def) => {
90
- const svc = this.servicesMap.get(def.name);
91
- if (svc) {
92
- const status = typeof svc.getStatus === "function"
93
- ? svc.getStatus()
94
- : { name: def.name, running: true, port: svc.port };
95
- // Compute canDisable: no running service should depend on this one
96
- const canDisable = !registry.some(
97
- (other) =>
98
- other.depends.includes(def.name) &&
99
- this.servicesMap.has(other.name)
100
- );
101
- return {
102
- name: def.name,
103
- running: true,
104
- enabled: true,
105
- port: status.port || this.config.ports?.[def.name],
106
- endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[def.name]}`,
107
- dependencies: def.depends,
108
- category: def.category,
109
- canDisable,
110
- ...status,
111
- };
112
- }
113
- return {
114
- name: def.name,
115
- running: false,
116
- enabled: false,
117
- port: this.config.ports?.[def.name],
118
- endpoint: `http://localhost:${this.config.ports?.[def.name]}`,
119
- dependencies: def.depends,
120
- canDisable: true,
121
- };
122
- });
123
- res.json({ services });
124
- });
125
-
126
- // GET /__admin/services/:name — returns single service status
127
- app.get("/__admin/services/:name", (req, res) => {
128
- const { name } = req.params;
129
- const registry = this.buildServiceRegistry();
130
- const def = registry.find((d) => d.name === name);
131
- if (!def) {
132
- return res.status(404).json({ error: `Unknown service: ${name}` });
133
- }
134
- const svc = this.servicesMap.get(name);
135
- const canDisable = !registry.some(
136
- (other) =>
137
- other.depends.includes(name) &&
138
- this.servicesMap.has(other.name)
139
- );
140
- if (svc) {
141
- const status = typeof svc.getStatus === "function"
142
- ? svc.getStatus()
143
- : { name, running: true, port: svc.port };
144
- return res.json({
145
- name,
146
- running: true,
147
- enabled: true,
148
- port: status.port || this.config.ports?.[name],
149
- endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[name]}`,
150
- dependencies: def.depends,
151
- canDisable,
152
- ...status,
153
- });
154
- }
155
- return res.json({
156
- name,
157
- running: false,
158
- enabled: false,
159
- port: this.config.ports?.[name],
160
- endpoint: `http://localhost:${this.config.ports?.[name]}`,
161
- dependencies: def.depends,
162
- canDisable: true,
163
- });
164
- });
165
-
166
- // POST /__admin/services/:name/enable — enables a service
167
- app.post("/__admin/services/:name/enable", async (req, res) => {
168
- const result = await this.enableService(req.params.name);
169
- res.status(result.success ? 200 : 400).json(result);
170
- });
171
-
172
- // POST /__admin/services/:name/disable — disables a service
173
- app.post("/__admin/services/:name/disable", async (req, res) => {
174
- const result = await this.disableService(req.params.name);
175
- res.status(result.success ? 200 : 400).json(result);
176
- });
177
- }
178
-
179
- startManagementServer() {
180
- return new Promise((resolve, reject) => {
181
- const port = this.config.adminPort || 9999;
182
- this.managementServer = this.managementApp.listen(port, () => {
183
- logger.info(`🔧 Management API rodando em http://localhost:${port}`);
184
- resolve();
185
- });
186
- this.managementServer.on("error", reject);
187
- });
188
- }
189
-
190
- buildServiceRegistry() {
191
- return [
192
- //Armazenamento & BD
193
- { name: "dynamodb", class: DynamoDBService, depends: [], category: 'Armazenamento & Banco de Dados' },
194
- { name: "s3", class: S3Service, depends: [], category: 'Armazenamento & Banco de Dados' },
195
- { name: "athena", class: AthenaService, depends: [], category: 'Armazenamento & Banco de Dados' },
196
-
197
- //Computação
198
- { name: "lambda", class: LambdaService, depends: [], category: 'Computação' },
199
- /* { name: "ecs", class: ECSService, depends: [] , category:'Computação'},*/
200
-
201
- { name: "cognito", class: CognitoService, depends: ["lambda"], category: 'Segurança & Identidade' },
202
- { name: "sts", class: STSService, depends: [], category: 'Segurança & Identidade' },
203
- { name: "kms", class: KMSService, depends: [], category: 'Segurança & Identidade' },
204
- { name: "secret-manager", class: SecretManagerService, depends: [], category: 'Segurança & Identidade' },
205
- { name: "parameter-store", class: ParameterStoreService, depends: [], category: 'Segurança & Identidade' },
206
-
207
- //Mensageria
208
- { name: "sqs", class: SQSService, depends: ["lambda"], category: 'Mensageria & Integração' },
209
- { name: "sns", class: SNSService, depends: [], category: 'Mensageria & Integração' },
210
- { name: "eventbridge", class: EventBridgeService, depends: [], category: 'Mensageria & Integração' },
211
-
212
- //Networking
213
- { name: "apigateway", class: APIGatewayService, depends: ["lambda", "cognito"], category: 'Networking' },
214
-
215
- //Observabilidade & Conformidade
216
- { name: "cloudwatch", class: CloudWatchService, depends: [], category: 'Observabilidade & Conformidade' },
217
- { name: "cloudtrail", class: CloudTrailService, depends: [], category: 'Observabilidade & Conformidade' },
218
- { name: "xray", class: XRayService, depends: [], category: 'Observabilidade & Conformidade' },
219
- { name: "config", class: ConfigService, depends: [], category: 'Observabilidade & Conformidade' },
220
- { name: "cloudformation", class: CloudFormationService, depends: [], category: 'Observabilidade & Conformidade' },
221
-
222
- ];
223
- }
224
-
225
- async initializeServices() {
226
- const serviceOrder = this.buildServiceRegistry();
227
-
228
- for (const serviceDef of serviceOrder) {
229
- if (this.config.services[serviceDef.name]) {
230
- try {
231
- const dependencies = {};
232
- for (const dep of serviceDef.depends) {
233
- dependencies[dep] = this.servicesMap.get(dep);
234
- }
235
-
236
- const service = new serviceDef.class(this.config, dependencies);
237
- await service.initialize();
238
-
239
- this.services.push(service);
240
- this.servicesMap.set(serviceDef.name, service);
241
-
242
- logger.success(`✅ ${serviceDef.name.toUpperCase()} Service inicializado`);
243
- } catch (error) {
244
- logger.error(`❌ Erro ao inicializar ${serviceDef.name}:`, error);
245
- if (serviceDef.name === "lambda") throw error;
246
- }
247
- }
248
- }
249
-
250
- // Injeta dependências cross-service nos serviços que suportam
251
- for (const service of this.services) {
252
- if (typeof service.injectDependencies === "function") {
253
- service.injectDependencies(this);
254
- }
255
- }
256
- }
257
-
258
- async startServices() {
259
- const startPromises = this.services.map((service) => service.start());
260
- await Promise.all(startPromises);
261
- logger.success("\n🎉 Todos os serviços foram iniciados com sucesso!");
262
- }
263
-
264
- async stop() {
265
- if (!this.running) {
266
- logger.warn("Servidor não está rodando");
267
- return;
268
- }
269
-
270
- logger.info("\n🛑 Parando AWS Local Simulator...\n");
271
-
272
- try {
273
- const stopPromises = [...this.services].reverse().map((service) => service.stop());
274
- await Promise.all(stopPromises);
275
-
276
- if (this.managementServer) {
277
- await new Promise((resolve) => this.managementServer.close(resolve));
278
- this.managementServer = null;
279
- }
280
-
281
- this.running = false;
282
- logger.success("✅ Todos os serviços foram parados");
283
- } catch (error) {
284
- logger.error("❌ Erro ao parar servidor:", error);
285
- throw error;
286
- }
287
- }
288
-
289
- async reset() {
290
- logger.info("\n🗑️ Resetando todos os dados...\n");
291
-
292
- const resetPromises = this.services.map((service) => service.reset());
293
- await Promise.all(resetPromises);
294
-
295
- logger.success("✅ Todos os dados foram resetados");
296
- }
297
-
298
- getStatus() {
299
- const status = {
300
- running: this.running,
301
- services: {},
302
- };
303
-
304
- for (const service of this.services) {
305
- status.services[service.name] = service.getStatus();
306
- }
307
-
308
- return status;
309
- }
310
-
311
- getService(name) {
312
- return this.servicesMap.get(name);
313
- }
314
-
315
- async enableService(name) {
316
- // Check if already running
317
- if (this.servicesMap.has(name)) {
318
- return { success: false, error: "Service already running" };
319
- }
320
-
321
- // Look up in registry
322
- const registry = this.buildServiceRegistry();
323
- const serviceDef = registry.find((s) => s.name === name);
324
- if (!serviceDef) {
325
- return { success: false, error: "Unknown service" };
326
- }
327
-
328
- // Validate all dependencies are running
329
- for (const dep of serviceDef.depends) {
330
- if (!this.servicesMap.has(dep)) {
331
- return { success: false, error: `Dependency not running: ${dep}` };
332
- }
333
- }
334
-
335
- try {
336
- const service = new serviceDef.class(this.config);
337
- await service.initialize();
338
- await service.start();
339
-
340
- this.services.push(service);
341
- this.servicesMap.set(name, service);
342
-
343
- if (typeof service.injectDependencies === "function") {
344
- service.injectDependencies(this);
345
- }
346
-
347
- // Build ServiceStatus for the response
348
- const canDisable = !registry.some(
349
- (other) =>
350
- other.depends.includes(name) && this.servicesMap.has(other.name)
351
- );
352
- const rawStatus =
353
- typeof service.getStatus === "function"
354
- ? service.getStatus()
355
- : { name, running: true, port: service.port };
356
-
357
- const serviceStatus = {
358
- name,
359
- running: true,
360
- enabled: true,
361
- port: rawStatus.port || this.config.ports?.[name],
362
- endpoint:
363
- rawStatus.endpoint ||
364
- `http://localhost:${rawStatus.port || this.config.ports?.[name]}`,
365
- dependencies: serviceDef.depends,
366
- canDisable,
367
- ...rawStatus,
368
- };
369
-
370
- return { success: true, service: serviceStatus };
371
- } catch (error) {
372
- return { success: false, error: error.message };
373
- }
374
- }
375
-
376
- async disableService(name) {
377
- // Check if running
378
- const service = this.servicesMap.get(name);
379
- if (!service) {
380
- return { success: false, error: "Service not running" };
381
- }
382
-
383
- // Check reverse dependencies
384
- const registry = this.buildServiceRegistry();
385
- for (const other of registry) {
386
- if (other.depends.includes(name) && this.servicesMap.has(other.name)) {
387
- return {
388
- success: false,
389
- error: `Cannot disable: ${other.name} depends on it`,
390
- };
391
- }
392
- }
393
-
394
- try {
395
- await service.stop();
396
- this.services = this.services.filter((s) => s !== service);
397
- this.servicesMap.delete(name);
398
-
399
- return {
400
- success: true,
401
- service: {
402
- name,
403
- running: false,
404
- enabled: false,
405
- port: this.config.ports?.[name],
406
- endpoint: `http://localhost:${this.config.ports?.[name]}`,
407
- dependencies: registry.find((d) => d.name === name)?.depends || [],
408
- canDisable: true,
409
- },
410
- };
411
- } catch (error) {
412
- return { success: false, error: error.message };
413
- }
414
- }
415
-
416
- printStatus() {
417
- logger.info("\n" + "=".repeat(60));
418
- logger.info("📊 Status dos Serviços:");
419
- logger.info("=".repeat(60));
420
-
421
- for (const service of this.services) {
422
- const status = service.getStatus();
423
- logger.info(`\n${service.name.toUpperCase()}:`);
424
- logger.info(` Status: ${status.running ? "✅ Rodando" : "❌ Parado"}`);
425
- logger.info(` Porta: ${status.port}`);
426
- logger.info(` Endpoint: ${status.endpoint}`);
427
-
428
- if (status.tablesCount !== undefined) {
429
- logger.info(` Tabelas: ${status.tablesCount}`);
430
- }
431
- if (status.bucketsCount !== undefined) {
432
- logger.info(` Buckets: ${status.bucketsCount}`);
433
- }
434
- if (status.queuesCount !== undefined) {
435
- logger.info(` Filas: ${status.queuesCount}`);
436
- }
437
- if (status.lambdasCount !== undefined) {
438
- logger.info(` Lambdas: ${status.lambdasCount}`);
439
- }
440
- if (status.objectsCount !== undefined) {
441
- logger.info(` Objetos: ${status.objectsCount}`);
442
- }
443
- if (status.messagesCount !== undefined) {
444
- logger.info(` Mensagens: ${status.messagesCount}`);
445
- }
446
- }
447
-
448
- logger.info("\n" + "=".repeat(60));
449
- logger.info("\n📝 Comandos úteis:");
450
- logger.info(" AWS CLI:");
451
- logger.info(" aws dynamodb list-tables --endpoint-url http://localhost:8000");
452
- logger.info(" aws s3 ls --endpoint-url http://localhost:4566");
453
- logger.info(" aws sqs list-queues --endpoint-url http://localhost:9324");
454
- logger.info("\n Testar APIs:");
455
- logger.info(" curl http://localhost:3001/health");
456
- logger.info(" curl http://localhost:8000/__admin/tables");
457
- logger.info(" curl http://localhost:4566/__admin/buckets");
458
- logger.info(" curl http://localhost:9324/__admin/queues");
459
- logger.info("=".repeat(60) + "\n");
460
- }
461
- }
462
-
463
- module.exports = Server;
1
+ /**
2
+ * Servidor Principal - Orquestra todos os serviços
3
+ */
4
+
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+ const mkdirp = require("mkdirp");
8
+ const express = require("express");
9
+ const cors = require("cors");
10
+ const logger = require("./utils/logger");
11
+
12
+ // Importa serviços
13
+ const DynamoDBService = require("./services/dynamodb");
14
+ const S3Service = require("./services/s3");
15
+ const SQSService = require("./services/sqs");
16
+ const LambdaService = require("./services/lambda");
17
+ const { SNSService } = require("./services/sns");
18
+ const { EventBridgeService } = require("./services/eventbridge");
19
+ const CognitoService = require("./services/cognito");
20
+ const APIGatewayService = require("./services/apigateway");
21
+ const ECSService = require("./services/ecs");
22
+ const STSService = require("./services/sts");
23
+ const { CloudWatchService } = require("./services/cloudwatch");
24
+ const CloudTrailService = require("./services/cloudtrail");
25
+ const { KMSService } = require("./services/kms");
26
+ const CloudFormationService = require("./services/cloudformation");
27
+ const { XRayService } = require("./services/xray");
28
+ const { SecretManagerService } = require("./services/secret-manager");
29
+ const { ParameterStoreService } = require("./services/parameter-store");
30
+ const { ConfigService } = require("./services/config");
31
+ const { AthenaService } = require("./services/athena");
32
+
33
+ class Server {
34
+ constructor(config) {
35
+ this.config = config;
36
+ this.services = [];
37
+ this.servicesMap = new Map();
38
+ this.running = false;
39
+ this.managementApp = express();
40
+ this.managementApp.use(cors());
41
+ this.managementApp.use(express.json());
42
+ this.managementServer = null;
43
+ this.setupDataDir();
44
+ this.setupLogLevel();
45
+ }
46
+
47
+ setupDataDir() {
48
+ const dataDir = path.resolve(process.cwd(), this.config.dataDir);
49
+ if (!fs.existsSync(dataDir)) {
50
+ mkdirp.sync(dataDir);
51
+ logger.debug(`📁 Diretório de dados criado: ${dataDir}`);
52
+ }
53
+ process.env.AWS_LOCAL_SIMULATOR_DATA_DIR = dataDir;
54
+ }
55
+
56
+ setupLogLevel() {
57
+ logger.setLevel(this.config.logLevel);
58
+ logger.info(`📝 Nível de log: ${this.config.logLevel}`);
59
+ }
60
+
61
+ async start() {
62
+ if (this.running) {
63
+ logger.warn("Servidor já está rodando");
64
+ return;
65
+ }
66
+
67
+ logger.info("\n🚀 Iniciando AWS Local Simulator...\n");
68
+
69
+ try {
70
+ await this.initializeServices();
71
+ await this.startServices();
72
+ this.setupManagementRoutes();
73
+ await this.startManagementServer();
74
+
75
+ this.running = true;
76
+ this.printStatus();
77
+ } catch (error) {
78
+ logger.error("❌ Erro ao iniciar servidor:", error);
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ setupManagementRoutes() {
84
+ const app = this.managementApp;
85
+
86
+ // GET /__admin/services — returns all services status
87
+ app.get("/__admin/services", (req, res) => {
88
+ const registry = this.buildServiceRegistry();
89
+ const services = registry.map((def) => {
90
+ const svc = this.servicesMap.get(def.name);
91
+ if (svc) {
92
+ const status = typeof svc.getStatus === "function"
93
+ ? svc.getStatus()
94
+ : { name: def.name, running: true, port: svc.port };
95
+ // Compute canDisable: no running service should depend on this one
96
+ const canDisable = !registry.some(
97
+ (other) =>
98
+ other.depends.includes(def.name) &&
99
+ this.servicesMap.has(other.name)
100
+ );
101
+ return {
102
+ name: def.name,
103
+ running: true,
104
+ enabled: true,
105
+ port: status.port || this.config.ports?.[def.name],
106
+ endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[def.name]}`,
107
+ dependencies: def.depends,
108
+ category: def.category,
109
+ canDisable,
110
+ ...status,
111
+ };
112
+ }
113
+ return {
114
+ name: def.name,
115
+ running: false,
116
+ enabled: false,
117
+ port: this.config.ports?.[def.name],
118
+ endpoint: `http://localhost:${this.config.ports?.[def.name]}`,
119
+ dependencies: def.depends,
120
+ canDisable: true,
121
+ };
122
+ });
123
+ res.json({ services });
124
+ });
125
+
126
+ // GET /__admin/services/:name — returns single service status
127
+ app.get("/__admin/services/:name", (req, res) => {
128
+ const { name } = req.params;
129
+ const registry = this.buildServiceRegistry();
130
+ const def = registry.find((d) => d.name === name);
131
+ if (!def) {
132
+ return res.status(404).json({ error: `Unknown service: ${name}` });
133
+ }
134
+ const svc = this.servicesMap.get(name);
135
+ const canDisable = !registry.some(
136
+ (other) =>
137
+ other.depends.includes(name) &&
138
+ this.servicesMap.has(other.name)
139
+ );
140
+ if (svc) {
141
+ const status = typeof svc.getStatus === "function"
142
+ ? svc.getStatus()
143
+ : { name, running: true, port: svc.port };
144
+ return res.json({
145
+ name,
146
+ running: true,
147
+ enabled: true,
148
+ port: status.port || this.config.ports?.[name],
149
+ endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[name]}`,
150
+ dependencies: def.depends,
151
+ canDisable,
152
+ ...status,
153
+ });
154
+ }
155
+ return res.json({
156
+ name,
157
+ running: false,
158
+ enabled: false,
159
+ port: this.config.ports?.[name],
160
+ endpoint: `http://localhost:${this.config.ports?.[name]}`,
161
+ dependencies: def.depends,
162
+ canDisable: true,
163
+ });
164
+ });
165
+
166
+ // POST /__admin/services/:name/enable — enables a service
167
+ app.post("/__admin/services/:name/enable", async (req, res) => {
168
+ const result = await this.enableService(req.params.name);
169
+ res.status(result.success ? 200 : 400).json(result);
170
+ });
171
+
172
+ // POST /__admin/services/:name/disable — disables a service
173
+ app.post("/__admin/services/:name/disable", async (req, res) => {
174
+ const result = await this.disableService(req.params.name);
175
+ res.status(result.success ? 200 : 400).json(result);
176
+ });
177
+ }
178
+
179
+ startManagementServer() {
180
+ return new Promise((resolve, reject) => {
181
+ const port = this.config.adminPort || 9999;
182
+ this.managementServer = this.managementApp.listen(port, () => {
183
+ logger.info(`🔧 Management API rodando em http://localhost:${port}`);
184
+ resolve();
185
+ });
186
+ this.managementServer.on("error", reject);
187
+ });
188
+ }
189
+
190
+ buildServiceRegistry() {
191
+ return [
192
+ //Armazenamento & BD
193
+ { name: "dynamodb", class: DynamoDBService, depends: [], category: 'Armazenamento & Banco de Dados' },
194
+ { name: "s3", class: S3Service, depends: [], category: 'Armazenamento & Banco de Dados' },
195
+ { name: "athena", class: AthenaService, depends: [], category: 'Armazenamento & Banco de Dados' },
196
+
197
+ //Computação
198
+ { name: "lambda", class: LambdaService, depends: [], category: 'Computação' },
199
+ /* { name: "ecs", class: ECSService, depends: [] , category:'Computação'},*/
200
+
201
+ { name: "cognito", class: CognitoService, depends: ["lambda"], category: 'Segurança & Identidade' },
202
+ { name: "sts", class: STSService, depends: [], category: 'Segurança & Identidade' },
203
+ { name: "kms", class: KMSService, depends: [], category: 'Segurança & Identidade' },
204
+ { name: "secret-manager", class: SecretManagerService, depends: [], category: 'Segurança & Identidade' },
205
+ { name: "parameter-store", class: ParameterStoreService, depends: [], category: 'Segurança & Identidade' },
206
+
207
+ //Mensageria
208
+ { name: "sqs", class: SQSService, depends: ["lambda"], category: 'Mensageria & Integração' },
209
+ { name: "sns", class: SNSService, depends: [], category: 'Mensageria & Integração' },
210
+ { name: "eventbridge", class: EventBridgeService, depends: [], category: 'Mensageria & Integração' },
211
+
212
+ //Networking
213
+ { name: "apigateway", class: APIGatewayService, depends: ["lambda", "cognito"], category: 'Networking' },
214
+
215
+ //Observabilidade & Conformidade
216
+ { name: "cloudwatch", class: CloudWatchService, depends: [], category: 'Observabilidade & Conformidade' },
217
+ { name: "cloudtrail", class: CloudTrailService, depends: [], category: 'Observabilidade & Conformidade' },
218
+ { name: "xray", class: XRayService, depends: [], category: 'Observabilidade & Conformidade' },
219
+ { name: "config", class: ConfigService, depends: [], category: 'Observabilidade & Conformidade' },
220
+ { name: "cloudformation", class: CloudFormationService, depends: [], category: 'Observabilidade & Conformidade' },
221
+
222
+ ];
223
+ }
224
+
225
+ async initializeServices() {
226
+ const serviceOrder = this.buildServiceRegistry();
227
+
228
+ for (const serviceDef of serviceOrder) {
229
+ if (this.config.services[serviceDef.name]) {
230
+ try {
231
+ const dependencies = {};
232
+ for (const dep of serviceDef.depends) {
233
+ dependencies[dep] = this.servicesMap.get(dep);
234
+ }
235
+
236
+ const service = new serviceDef.class(this.config, dependencies);
237
+ await service.initialize();
238
+
239
+ this.services.push(service);
240
+ this.servicesMap.set(serviceDef.name, service);
241
+
242
+ logger.success(`✅ ${serviceDef.name.toUpperCase()} Service inicializado`);
243
+ } catch (error) {
244
+ logger.error(`❌ Erro ao inicializar ${serviceDef.name}:`, error);
245
+ if (serviceDef.name === "lambda") throw error;
246
+ }
247
+ }
248
+ }
249
+
250
+ // Injeta dependências cross-service nos serviços que suportam
251
+ for (const service of this.services) {
252
+ if (typeof service.injectDependencies === "function") {
253
+ service.injectDependencies(this);
254
+ }
255
+ }
256
+ }
257
+
258
+ async startServices() {
259
+ const startPromises = this.services.map((service) => service.start());
260
+ await Promise.all(startPromises);
261
+ logger.success("\n🎉 Todos os serviços foram iniciados com sucesso!");
262
+ }
263
+
264
+ async stop() {
265
+ if (!this.running) {
266
+ logger.warn("Servidor não está rodando");
267
+ return;
268
+ }
269
+
270
+ logger.info("\n🛑 Parando AWS Local Simulator...\n");
271
+
272
+ try {
273
+ const stopPromises = [...this.services].reverse().map((service) => service.stop());
274
+ await Promise.all(stopPromises);
275
+
276
+ if (this.managementServer) {
277
+ await new Promise((resolve) => this.managementServer.close(resolve));
278
+ this.managementServer = null;
279
+ }
280
+
281
+ this.running = false;
282
+ logger.success("✅ Todos os serviços foram parados");
283
+ } catch (error) {
284
+ logger.error("❌ Erro ao parar servidor:", error);
285
+ throw error;
286
+ }
287
+ }
288
+
289
+ async reset() {
290
+ logger.info("\n🗑️ Resetando todos os dados...\n");
291
+
292
+ const resetPromises = this.services.map((service) => service.reset());
293
+ await Promise.all(resetPromises);
294
+
295
+ logger.success("✅ Todos os dados foram resetados");
296
+ }
297
+
298
+ getStatus() {
299
+ const status = {
300
+ running: this.running,
301
+ services: {},
302
+ };
303
+
304
+ for (const service of this.services) {
305
+ status.services[service.name] = service.getStatus();
306
+ }
307
+
308
+ return status;
309
+ }
310
+
311
+ getService(name) {
312
+ return this.servicesMap.get(name);
313
+ }
314
+
315
+ async enableService(name) {
316
+ // Check if already running
317
+ if (this.servicesMap.has(name)) {
318
+ return { success: false, error: "Service already running" };
319
+ }
320
+
321
+ // Look up in registry
322
+ const registry = this.buildServiceRegistry();
323
+ const serviceDef = registry.find((s) => s.name === name);
324
+ if (!serviceDef) {
325
+ return { success: false, error: "Unknown service" };
326
+ }
327
+
328
+ // Validate all dependencies are running
329
+ for (const dep of serviceDef.depends) {
330
+ if (!this.servicesMap.has(dep)) {
331
+ return { success: false, error: `Dependency not running: ${dep}` };
332
+ }
333
+ }
334
+
335
+ try {
336
+ const service = new serviceDef.class(this.config);
337
+ await service.initialize();
338
+ await service.start();
339
+
340
+ this.services.push(service);
341
+ this.servicesMap.set(name, service);
342
+
343
+ if (typeof service.injectDependencies === "function") {
344
+ service.injectDependencies(this);
345
+ }
346
+
347
+ // Build ServiceStatus for the response
348
+ const canDisable = !registry.some(
349
+ (other) =>
350
+ other.depends.includes(name) && this.servicesMap.has(other.name)
351
+ );
352
+ const rawStatus =
353
+ typeof service.getStatus === "function"
354
+ ? service.getStatus()
355
+ : { name, running: true, port: service.port };
356
+
357
+ const serviceStatus = {
358
+ name,
359
+ running: true,
360
+ enabled: true,
361
+ port: rawStatus.port || this.config.ports?.[name],
362
+ endpoint:
363
+ rawStatus.endpoint ||
364
+ `http://localhost:${rawStatus.port || this.config.ports?.[name]}`,
365
+ dependencies: serviceDef.depends,
366
+ canDisable,
367
+ ...rawStatus,
368
+ };
369
+
370
+ return { success: true, service: serviceStatus };
371
+ } catch (error) {
372
+ return { success: false, error: error.message };
373
+ }
374
+ }
375
+
376
+ async disableService(name) {
377
+ // Check if running
378
+ const service = this.servicesMap.get(name);
379
+ if (!service) {
380
+ return { success: false, error: "Service not running" };
381
+ }
382
+
383
+ // Check reverse dependencies
384
+ const registry = this.buildServiceRegistry();
385
+ for (const other of registry) {
386
+ if (other.depends.includes(name) && this.servicesMap.has(other.name)) {
387
+ return {
388
+ success: false,
389
+ error: `Cannot disable: ${other.name} depends on it`,
390
+ };
391
+ }
392
+ }
393
+
394
+ try {
395
+ await service.stop();
396
+ this.services = this.services.filter((s) => s !== service);
397
+ this.servicesMap.delete(name);
398
+
399
+ return {
400
+ success: true,
401
+ service: {
402
+ name,
403
+ running: false,
404
+ enabled: false,
405
+ port: this.config.ports?.[name],
406
+ endpoint: `http://localhost:${this.config.ports?.[name]}`,
407
+ dependencies: registry.find((d) => d.name === name)?.depends || [],
408
+ canDisable: true,
409
+ },
410
+ };
411
+ } catch (error) {
412
+ return { success: false, error: error.message };
413
+ }
414
+ }
415
+
416
+ printStatus() {
417
+ logger.info("\n" + "=".repeat(60));
418
+ logger.info("📊 Status dos Serviços:");
419
+ logger.info("=".repeat(60));
420
+
421
+ for (const service of this.services) {
422
+ const status = service.getStatus();
423
+ logger.info(`\n${service.name.toUpperCase()}:`);
424
+ logger.info(` Status: ${status.running ? "✅ Rodando" : "❌ Parado"}`);
425
+ logger.info(` Porta: ${status.port}`);
426
+ logger.info(` Endpoint: ${status.endpoint}`);
427
+
428
+ if (status.tablesCount !== undefined) {
429
+ logger.info(` Tabelas: ${status.tablesCount}`);
430
+ }
431
+ if (status.bucketsCount !== undefined) {
432
+ logger.info(` Buckets: ${status.bucketsCount}`);
433
+ }
434
+ if (status.queuesCount !== undefined) {
435
+ logger.info(` Filas: ${status.queuesCount}`);
436
+ }
437
+ if (status.lambdasCount !== undefined) {
438
+ logger.info(` Lambdas: ${status.lambdasCount}`);
439
+ }
440
+ if (status.objectsCount !== undefined) {
441
+ logger.info(` Objetos: ${status.objectsCount}`);
442
+ }
443
+ if (status.messagesCount !== undefined) {
444
+ logger.info(` Mensagens: ${status.messagesCount}`);
445
+ }
446
+ }
447
+
448
+ logger.info("\n" + "=".repeat(60));
449
+ logger.info("\n📝 Comandos úteis:");
450
+ logger.info(" AWS CLI:");
451
+ logger.info(" aws dynamodb list-tables --endpoint-url http://localhost:8000");
452
+ logger.info(" aws s3 ls --endpoint-url http://localhost:4566");
453
+ logger.info(" aws sqs list-queues --endpoint-url http://localhost:9324");
454
+ logger.info("\n Testar APIs:");
455
+ logger.info(" curl http://localhost:3001/health");
456
+ logger.info(" curl http://localhost:8000/__admin/tables");
457
+ logger.info(" curl http://localhost:4566/__admin/buckets");
458
+ logger.info(" curl http://localhost:9324/__admin/queues");
459
+ logger.info("=".repeat(60) + "\n");
460
+ }
461
+ }
462
+
463
+ module.exports = Server;