@gugananuvem/aws-local-simulator 1.0.11 → 1.0.14

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 (65) hide show
  1. package/README.md +349 -72
  2. package/package.json +12 -2
  3. package/src/config/config-loader.js +2 -0
  4. package/src/config/default-config.js +3 -0
  5. package/src/index.js +18 -2
  6. package/src/server.js +37 -31
  7. package/src/services/apigateway/index.js +10 -3
  8. package/src/services/apigateway/server.js +73 -0
  9. package/src/services/apigateway/simulator.js +13 -3
  10. package/src/services/athena/index.js +75 -0
  11. package/src/services/athena/server.js +101 -0
  12. package/src/services/athena/simulador.js +998 -0
  13. package/src/services/athena/simulator.js +346 -0
  14. package/src/services/cloudformation/index.js +106 -0
  15. package/src/services/cloudformation/server.js +417 -0
  16. package/src/services/cloudformation/simulador.js +1045 -0
  17. package/src/services/cloudtrail/index.js +84 -0
  18. package/src/services/cloudtrail/server.js +235 -0
  19. package/src/services/cloudtrail/simulador.js +719 -0
  20. package/src/services/cloudwatch/index.js +84 -0
  21. package/src/services/cloudwatch/server.js +366 -0
  22. package/src/services/cloudwatch/simulador.js +1173 -0
  23. package/src/services/cognito/index.js +5 -0
  24. package/src/services/cognito/server.js +54 -3
  25. package/src/services/cognito/simulator.js +273 -2
  26. package/src/services/config/index.js +96 -0
  27. package/src/services/config/server.js +215 -0
  28. package/src/services/config/simulador.js +1260 -0
  29. package/src/services/dynamodb/index.js +7 -3
  30. package/src/services/dynamodb/server.js +4 -2
  31. package/src/services/dynamodb/simulator.js +39 -29
  32. package/src/services/eventbridge/index.js +55 -51
  33. package/src/services/eventbridge/server.js +209 -0
  34. package/src/services/eventbridge/simulator.js +684 -0
  35. package/src/services/index.js +30 -4
  36. package/src/services/kms/index.js +75 -0
  37. package/src/services/kms/server.js +67 -0
  38. package/src/services/kms/simulator.js +324 -0
  39. package/src/services/lambda/handler-loader.js +13 -2
  40. package/src/services/lambda/index.js +7 -1
  41. package/src/services/lambda/server.js +32 -39
  42. package/src/services/lambda/simulator.js +78 -181
  43. package/src/services/parameter-store/index.js +80 -0
  44. package/src/services/parameter-store/server.js +50 -0
  45. package/src/services/parameter-store/simulator.js +201 -0
  46. package/src/services/s3/index.js +7 -3
  47. package/src/services/s3/server.js +20 -13
  48. package/src/services/s3/simulator.js +163 -407
  49. package/src/services/secret-manager/index.js +80 -0
  50. package/src/services/secret-manager/server.js +50 -0
  51. package/src/services/secret-manager/simulator.js +171 -0
  52. package/src/services/sns/index.js +55 -42
  53. package/src/services/sns/server.js +580 -0
  54. package/src/services/sns/simulator.js +1482 -0
  55. package/src/services/sqs/index.js +2 -4
  56. package/src/services/sqs/server.js +92 -18
  57. package/src/services/sqs/simulator.js +79 -298
  58. package/src/services/sts/index.js +37 -0
  59. package/src/services/sts/server.js +142 -0
  60. package/src/services/sts/simulator.js +69 -0
  61. package/src/services/xray/index.js +83 -0
  62. package/src/services/xray/server.js +308 -0
  63. package/src/services/xray/simulador.js +994 -0
  64. package/src/utils/cloudtrail-audit.js +129 -0
  65. package/src/utils/local-store.js +18 -2
@@ -31,6 +31,11 @@ class CognitoService {
31
31
  logger.debug('Cognito Service inicializado');
32
32
  }
33
33
 
34
+ injectDependencies(server) {
35
+ const ct = server.getService('cloudtrail');
36
+ if (ct?.simulator) this.simulator.audit.setTrail(ct.simulator);
37
+ }
38
+
34
39
  async start() {
35
40
  if (this.isRunning) return;
36
41
  await this.server.start();
@@ -16,7 +16,19 @@ class CognitoServer {
16
16
  }
17
17
 
18
18
  setupMiddlewares() {
19
- this.app.use(express.json());
19
+ this.app.use(express.raw({ type: '*/*', limit: '10mb' }));
20
+ this.app.use((req, res, next) => {
21
+ if (req.body && Buffer.isBuffer(req.body)) {
22
+ try {
23
+ req.body = JSON.parse(req.body.toString('utf8'));
24
+ } catch (e) {
25
+ req.body = {};
26
+ }
27
+ } else if (!req.body) {
28
+ req.body = {};
29
+ }
30
+ next();
31
+ });
20
32
 
21
33
  if (logger.currentLogLevel === 'verboso') {
22
34
  this.app.use((req, res, next) => {
@@ -48,12 +60,13 @@ class CognitoServer {
48
60
  // User Pool operations
49
61
  this.app.post('/', async (req, res) => {
50
62
  const target = req.headers['x-amz-target'];
63
+ logger.info(`Cognito incoming: target=${target} body=${JSON.stringify(req.body)}`);
51
64
  if (!target) {
52
65
  return res.status(400).json({ error: 'Missing X-Amz-Target header' });
53
66
  }
54
67
 
55
68
  try {
56
- const result = await this.handleRequest(target, req.body);
69
+ const result = await this.handleRequest(target, req.body || {});
57
70
  res.json(result);
58
71
  } catch (error) {
59
72
  logger.error('Cognito Error:', error);
@@ -84,20 +97,46 @@ class CognitoServer {
84
97
  case 'DeleteUserPool':
85
98
  return this.simulator.deleteUserPool(params);
86
99
 
100
+ case 'ListUsers':
101
+ return this.simulator.listUsers(params);
87
102
  // User Pool Client Management
88
103
  case 'CreateUserPoolClient':
89
104
  return this.simulator.createUserPoolClient(params);
105
+ case 'ListUserPoolClients':
106
+ return this.simulator.listUserPoolClients(params);
107
+ case 'DescribeUserPoolClient':
108
+ return this.simulator.describeUserPoolClient(params);
109
+ case 'DeleteUserPoolClient':
110
+ return this.simulator.deleteUserPoolClient(params);
90
111
 
91
112
  // User Operations
92
113
  case 'SignUp':
93
114
  return this.simulator.signUp(params);
94
115
  case 'ConfirmSignUp':
95
116
  return this.simulator.confirmSignUp(params);
117
+ case 'ForgotPassword':
118
+ return this.simulator.forgotPassword(params);
119
+ case 'ConfirmForgotPassword':
120
+ return this.simulator.confirmForgotPassword(params);
121
+ case 'ChangePassword':
122
+ return this.simulator.changePassword(params);
96
123
  case 'InitiateAuth':
97
124
  return this.simulator.initiateAuth(params);
125
+ case 'RespondToAuthChallenge':
126
+ return this.simulator.respondToAuthChallenge(params);
98
127
  case 'GetToken':
99
128
  return this.simulator.getToken(params);
100
-
129
+ case 'GlobalSignOut':
130
+ return this.simulator.globalSignOut(params);
131
+ case 'RevokeToken':
132
+ return this.simulator.revokeToken(params);
133
+ case 'GetUser':
134
+ return this.simulator.getUser(params);
135
+ case 'UpdateUserAttributes':
136
+ return this.simulator.updateUserAttributes(params);
137
+ case 'DeleteUser':
138
+ return this.simulator.deleteUser(params);
139
+
101
140
  // Admin Operations
102
141
  case 'AdminGetUser':
103
142
  return this.simulator.adminGetUser(params);
@@ -107,6 +146,18 @@ class CognitoServer {
107
146
  return this.simulator.adminSetUserPassword(params);
108
147
  case 'AdminDeleteUser':
109
148
  return this.simulator.adminDeleteUser(params);
149
+ case 'AdminDisableUser':
150
+ return this.simulator.adminDisableUser(params);
151
+ case 'AdminEnableUser':
152
+ return this.simulator.adminEnableUser(params);
153
+ case 'AdminResetUserPassword':
154
+ return this.simulator.adminResetUserPassword(params);
155
+ case 'AdminInitiateAuth':
156
+ return this.simulator.initiateAuth(params);
157
+ case 'AdminListGroupsForUser':
158
+ return this.simulator.adminListGroupsForUser(params);
159
+ case 'AdminUserGlobalSignOut':
160
+ return this.simulator.adminUserGlobalSignOut(params);
110
161
 
111
162
  // Identity Pool Operations
112
163
  case 'CreateIdentityPool':
@@ -9,6 +9,7 @@ const { v4: uuidv4 } = require('uuid');
9
9
  const logger = require('../../utils/logger');
10
10
  const LocalStore = require('../../utils/local-store');
11
11
  const path = require('path');
12
+ const { CloudTrailAudit } = require('../../utils/cloudtrail-audit');
12
13
 
13
14
  class CognitoSimulator {
14
15
  constructor(config) {
@@ -22,6 +23,7 @@ class CognitoSimulator {
22
23
  this.refreshTokens = new Map();
23
24
  this.accessTokens = new Map();
24
25
  this.jwtSecret = crypto.randomBytes(64).toString('hex');
26
+ this.audit = new CloudTrailAudit('cognito-idp.amazonaws.com');
25
27
  }
26
28
 
27
29
  async initialize() {
@@ -73,6 +75,7 @@ class CognitoSimulator {
73
75
  this.persistUserPools();
74
76
 
75
77
  logger.debug(`✅ User Pool criado: ${PoolName} (${poolId})`);
78
+ this.audit.record({ eventName: 'CreateUserPool', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { poolName: PoolName } });
76
79
 
77
80
  return {
78
81
  UserPool: {
@@ -140,6 +143,215 @@ class CognitoSimulator {
140
143
  };
141
144
  }
142
145
 
146
+ listUsers(params = {}) {
147
+ const { UserPoolId, Filter, Limit = 60, PaginationToken } = params;
148
+ const userPool = this.userPools.get(UserPoolId);
149
+
150
+ if (!userPool) {
151
+ throw new Error(`User pool ${UserPoolId} not found`);
152
+ }
153
+
154
+ let users = Array.from(this.users.values()).filter(u => u.UserPoolId === UserPoolId);
155
+
156
+ if (PaginationToken) {
157
+ const startIndex = parseInt(PaginationToken);
158
+ users = users.slice(startIndex);
159
+ }
160
+
161
+ const results = users.slice(0, Limit);
162
+ const nextToken = results.length === Limit && users.length > Limit ? String(Limit) : null;
163
+
164
+ return {
165
+ Users: results.map(u => ({
166
+ Username: u.Username,
167
+ UserStatus: u.UserStatus,
168
+ Enabled: u.Enabled,
169
+ UserCreateDate: u.CreatedDate,
170
+ UserLastModifiedDate: u.LastModifiedDate,
171
+ Attributes: this.formatUserAttributes(u.Attributes)
172
+ })),
173
+ PaginationToken: nextToken
174
+ };
175
+ }
176
+
177
+ listUserPoolClients(params = {}) {
178
+ const { UserPoolId, MaxResults = 60, NextToken } = params;
179
+ const userPool = this.userPools.get(UserPoolId);
180
+ if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
181
+
182
+ let clients = Array.from(userPool.Clients.values());
183
+ if (NextToken) clients = clients.slice(parseInt(NextToken));
184
+ const results = clients.slice(0, MaxResults);
185
+
186
+ return {
187
+ UserPoolClients: results.map(c => ({
188
+ ClientId: c.ClientId,
189
+ ClientName: c.ClientName,
190
+ UserPoolId: c.UserPoolId
191
+ })),
192
+ NextToken: results.length === MaxResults && clients.length > MaxResults ? String(MaxResults) : null
193
+ };
194
+ }
195
+
196
+ describeUserPoolClient(params = {}) {
197
+ const { UserPoolId, ClientId } = params;
198
+ const userPool = this.userPools.get(UserPoolId);
199
+ if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
200
+ const client = userPool.Clients.get(ClientId);
201
+ if (!client) throw new Error(`Client ${ClientId} not found`);
202
+ return { UserPoolClient: client };
203
+ }
204
+
205
+ deleteUserPoolClient(params = {}) {
206
+ const { UserPoolId, ClientId } = params;
207
+ const userPool = this.userPools.get(UserPoolId);
208
+ if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
209
+ userPool.Clients.delete(ClientId);
210
+ this.persistUserPools();
211
+ return {};
212
+ }
213
+
214
+ forgotPassword(params = {}) {
215
+ const { ClientId, Username } = params;
216
+ const userPool = this.findUserPoolByClientId(ClientId);
217
+ if (!userPool) throw new Error(`Client ${ClientId} not found`);
218
+ return { CodeDeliveryDetails: { Destination: 'test@example.com', DeliveryMedium: 'EMAIL', AttributeName: 'email' } };
219
+ }
220
+
221
+ confirmForgotPassword(params = {}) {
222
+ const { ClientId, Username, ConfirmationCode, Password } = params;
223
+ const user = this.findUserByUsername(Username, ClientId);
224
+ if (!user) throw new Error(`User not found: ${Username}`);
225
+ user.Password = this.hashPassword(Password);
226
+ user.UserStatus = 'CONFIRMED';
227
+ this.persistUsers();
228
+ return {};
229
+ }
230
+
231
+ changePassword(params = {}) {
232
+ const { AccessToken, PreviousPassword, ProposedPassword } = params;
233
+ const session = this.accessTokens.get(AccessToken);
234
+ if (!session) throw new Error('Invalid access token');
235
+ const user = this.users.get(session.UserId);
236
+ if (!user) throw new Error('User not found');
237
+ if (!this.verifyPassword(PreviousPassword, user.Password)) throw new Error('Incorrect previous password');
238
+ user.Password = this.hashPassword(ProposedPassword);
239
+ this.persistUsers();
240
+ return {};
241
+ }
242
+
243
+ respondToAuthChallenge(params = {}) {
244
+ return { AuthenticationResult: null, ChallengeName: null };
245
+ }
246
+
247
+ revokeToken(params = {}) {
248
+ const { Token, ClientId } = params;
249
+ // Remove refresh token session if it exists
250
+ const session = this.refreshTokens.get(Token);
251
+ if (session) {
252
+ this.sessions.delete(session.Id);
253
+ this.accessTokens.delete(session.AccessToken);
254
+ this.refreshTokens.delete(Token);
255
+ this.persistSessions();
256
+ }
257
+ return {};
258
+ }
259
+
260
+ globalSignOut(params = {}) {
261
+ const { AccessToken } = params;
262
+ const session = this.accessTokens.get(AccessToken);
263
+ if (session) {
264
+ this.sessions.delete(session.Id);
265
+ this.accessTokens.delete(AccessToken);
266
+ this.refreshTokens.delete(session.RefreshToken);
267
+ this.persistSessions();
268
+ }
269
+ return {};
270
+ }
271
+
272
+ getUser(params = {}) {
273
+ const { AccessToken } = params;
274
+ const session = this.accessTokens.get(AccessToken);
275
+ if (!session) throw new Error('Invalid access token');
276
+ // Check session is still active
277
+ if (!this.sessions.has(session.Id)) throw new Error('Token has been revoked');
278
+ const user = this.users.get(session.UserId);
279
+ if (!user) throw new Error('User not found');
280
+ return {
281
+ Username: user.Username,
282
+ UserAttributes: this.formatUserAttributes(user.Attributes),
283
+ UserStatus: user.UserStatus
284
+ };
285
+ }
286
+
287
+ updateUserAttributes(params = {}) {
288
+ const { AccessToken, UserAttributes } = params;
289
+ const session = this.accessTokens.get(AccessToken);
290
+ if (!session) throw new Error('Invalid access token');
291
+ const user = this.users.get(session.UserId);
292
+ if (!user) throw new Error('User not found');
293
+ const updates = this.normalizeUserAttributes(UserAttributes || []);
294
+ Object.assign(user.Attributes, updates);
295
+ this.persistUsers();
296
+ return { CodeDeliveryDetailsList: [] };
297
+ }
298
+
299
+ deleteUser(params = {}) {
300
+ const { AccessToken } = params;
301
+ const session = this.accessTokens.get(AccessToken);
302
+ if (!session) throw new Error('Invalid access token');
303
+ this.users.delete(session.UserId);
304
+ this.persistUsers();
305
+ return {};
306
+ }
307
+
308
+ adminDisableUser(params = {}) {
309
+ const { UserPoolId, Username } = params;
310
+ const user = this.findUserByUsername(Username, null, UserPoolId);
311
+ if (!user) throw new Error(`User not found: ${Username}`);
312
+ user.Enabled = false;
313
+ this.persistUsers();
314
+ return {};
315
+ }
316
+
317
+ adminEnableUser(params = {}) {
318
+ const { UserPoolId, Username } = params;
319
+ const user = this.findUserByUsername(Username, null, UserPoolId);
320
+ if (!user) throw new Error(`User not found: ${Username}`);
321
+ user.Enabled = true;
322
+ this.persistUsers();
323
+ return {};
324
+ }
325
+
326
+ adminResetUserPassword(params = {}) {
327
+ const { UserPoolId, Username } = params;
328
+ const user = this.findUserByUsername(Username, null, UserPoolId);
329
+ if (!user) throw new Error(`User not found: ${Username}`);
330
+ user.UserStatus = 'RESET_REQUIRED';
331
+ this.persistUsers();
332
+ return {};
333
+ }
334
+
335
+ adminUserGlobalSignOut(params = {}) {
336
+ const { UserPoolId, Username } = params;
337
+ const user = this.findUserByUsername(Username, null, UserPoolId);
338
+ if (!user) throw new Error(`User not found: ${Username}`);
339
+ // Invalidate all sessions for this user
340
+ for (const [id, session] of this.sessions.entries()) {
341
+ if (session.UserId === user.UserId) {
342
+ this.accessTokens.delete(session.AccessToken);
343
+ this.refreshTokens.delete(session.RefreshToken);
344
+ this.sessions.delete(id);
345
+ }
346
+ }
347
+ this.persistSessions();
348
+ return {};
349
+ }
350
+
351
+ adminListGroupsForUser(params = {}) {
352
+ return { Groups: [], NextToken: null };
353
+ }
354
+
143
355
  deleteUserPool(params) {
144
356
  const { UserPoolId } = params;
145
357
 
@@ -207,7 +419,7 @@ class CognitoSimulator {
207
419
 
208
420
  // ============ User Operations ============
209
421
 
210
- signUp(params) {
422
+ signUp(params = {}) {
211
423
  const { ClientId, Username, Password, UserAttributes, ValidationData } = params;
212
424
 
213
425
  // Encontra o user pool pelo client id
@@ -319,6 +531,7 @@ class CognitoSimulator {
319
531
  this.persistSessions();
320
532
 
321
533
  logger.debug(`🔐 Usuário autenticado: ${username}`);
534
+ this.audit.record({ eventName: 'InitiateAuth', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { clientId: ClientId, authFlow: AuthFlow } });
322
535
 
323
536
  return {
324
537
  AuthenticationResult: {
@@ -726,10 +939,10 @@ class CognitoSimulator {
726
939
  // ============ Persistence ============
727
940
 
728
941
  loadUserPools() {
942
+ // Load persisted pools first
729
943
  const saved = this.store.read('__userpools__');
730
944
  if (saved) {
731
945
  for (const [id, data] of Object.entries(saved)) {
732
- // Reconstitui Maps
733
946
  data.Clients = new Map(Object.entries(data.Clients || {}));
734
947
  data.Groups = new Map(Object.entries(data.Groups || {}));
735
948
  data.IdentityProviders = new Map(Object.entries(data.IdentityProviders || {}));
@@ -737,6 +950,64 @@ class CognitoSimulator {
737
950
  this.userPools.set(id, data);
738
951
  }
739
952
  }
953
+
954
+ // Create user pools from config if not already persisted
955
+ if (this.config.cognito?.userPools) {
956
+ let configChanged = false;
957
+ const configPath = this.config._configPath;
958
+
959
+ for (let i = 0; i < this.config.cognito.userPools.length; i++) {
960
+ const poolConfig = this.config.cognito.userPools[i];
961
+ const existing = Array.from(this.userPools.values()).find(p => p.Name === poolConfig.PoolName);
962
+
963
+ if (!existing) {
964
+ const result = this.createUserPool(poolConfig);
965
+ const poolId = result.UserPool.Id;
966
+ logger.debug(`✅ User Pool criado a partir da config: ${poolConfig.PoolName} (${poolId})`);
967
+
968
+ // Auto-create a default client if not specified
969
+ if (!poolConfig.ClientId) {
970
+ const clientResult = this.createUserPoolClient({
971
+ UserPoolId: poolId,
972
+ ClientName: `${poolConfig.PoolName}-client`,
973
+ GenerateSecret: false
974
+ });
975
+ const clientId = clientResult.UserPoolClient.ClientId;
976
+ this.config.cognito.userPools[i].ClientId = clientId;
977
+ this.config.cognito.userPools[i].UserPoolId = poolId;
978
+ configChanged = true;
979
+ logger.debug(`✅ Client criado automaticamente: ${clientId}`);
980
+ }
981
+ } else if (!poolConfig.ClientId) {
982
+ // Pool exists but no clientId in config — write it back
983
+ const firstClient = existing.Clients.size > 0 ? existing.Clients.values().next().value : null;
984
+ if (firstClient) {
985
+ this.config.cognito.userPools[i].ClientId = firstClient.ClientId;
986
+ this.config.cognito.userPools[i].UserPoolId = existing.Id;
987
+ configChanged = true;
988
+ }
989
+ }
990
+ }
991
+
992
+ // Write clientId back to aws-local-simulator.json
993
+ if (configChanged && configPath) {
994
+ try {
995
+ const fs = require('fs');
996
+ const fileContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
997
+ fileContent.cognito = fileContent.cognito || {};
998
+ fileContent.cognito.userPools = this.config.cognito.userPools.map(p => ({
999
+ PoolName: p.PoolName,
1000
+ AutoVerifiedAttributes: p.AutoVerifiedAttributes,
1001
+ UserPoolId: p.UserPoolId,
1002
+ ClientId: p.ClientId
1003
+ }));
1004
+ fs.writeFileSync(configPath, JSON.stringify(fileContent, null, 2));
1005
+ logger.info(`✅ ClientId gravado em: ${configPath}`);
1006
+ } catch (err) {
1007
+ logger.warn(`⚠️ Não foi possível gravar clientId no config: ${err.message}`);
1008
+ }
1009
+ }
1010
+ }
740
1011
  }
741
1012
 
742
1013
  loadIdentityPools() {
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview AWS Config Service
5
+ * Porta padrão: 4013
6
+ */
7
+
8
+ const http = require('http');
9
+ const path = require('path');
10
+ const { ConfigSimulator } = require('./simulador');
11
+ const { createConfigServer } = require('./server');
12
+ const LocalStore = require('../../utils/local-store');
13
+
14
+ class ConfigService {
15
+ constructor(config) {
16
+ this.config = config;
17
+ this.logger = require('../../utils/logger');
18
+ this.name = 'config';
19
+ this.port = config?.ports?.config || config?.services?.config?.port || 4013;
20
+ this.store = null;
21
+ this.simulator = null;
22
+ this._server = null;
23
+ this.isRunning = false;
24
+ }
25
+
26
+ async initialize() {
27
+ this.logger.debug(`Inicializando AWS Config Service na porta ${this.port}...`);
28
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
29
+ this.store = new LocalStore(path.join(dataDir, 'config'));
30
+ this.simulator = new ConfigSimulator(this.config, this.store, this.logger);
31
+ await this.simulator.load();
32
+ this.logger.debug('AWS Config Service inicializado');
33
+ }
34
+
35
+ injectDependencies(server) {
36
+ if (!server) return;
37
+ const s3 = server.getService('s3');
38
+ if (s3?.simulator) this.simulator.s3Simulator = s3.simulator;
39
+ const sns = server.getService('sns');
40
+ if (sns?.simulator) this.simulator.snsSimulator = sns.simulator;
41
+ const lambda = server.getService('lambda');
42
+ if (lambda?.simulator) this.simulator.lambdaSimulator = lambda.simulator;
43
+ const dynamo = server.getService('dynamodb');
44
+ if (dynamo?.simulator) this.simulator.dynamoSimulator = dynamo.simulator;
45
+ const ct = server.getService('cloudtrail');
46
+ if (ct?.simulator) this.simulator.cloudtrailSimulator = ct.simulator;
47
+ }
48
+
49
+ async start() {
50
+ if (this.isRunning) return;
51
+ const { handler } = createConfigServer(this.simulator, this.logger);
52
+ this._server = http.createServer((req, res) => {
53
+ handler(req, res).catch(err => {
54
+ this.logger.error('[Config] Unhandled error:', err);
55
+ if (!res.headersSent) {
56
+ res.writeHead(500, { 'Content-Type': 'application/x-amz-json-1.1' });
57
+ res.end(JSON.stringify({ __type: 'InternalFailure', message: err.message }));
58
+ }
59
+ });
60
+ });
61
+ return new Promise((resolve, reject) => {
62
+ this._server.listen(this.port, '0.0.0.0', (err) => {
63
+ if (err) return reject(err);
64
+ this.isRunning = true;
65
+ this.logger.debug(`AWS Config rodando na porta ${this.port}`);
66
+ resolve();
67
+ });
68
+ });
69
+ }
70
+
71
+ async stop() {
72
+ if (!this.isRunning || !this._server) return;
73
+ await new Promise((resolve) => this._server.close(resolve));
74
+ this.simulator._stopRecording?.();
75
+ this._server = null;
76
+ this.isRunning = false;
77
+ }
78
+
79
+ async reset() {
80
+ this.simulator.reset();
81
+ await this.simulator.save();
82
+ }
83
+
84
+ getStatus() {
85
+ return {
86
+ running: this.isRunning,
87
+ port: this.port,
88
+ endpoint: `http://localhost:${this.port}`,
89
+ ...this.simulator?.getStatus(),
90
+ };
91
+ }
92
+
93
+ getSimulator() { return this.simulator; }
94
+ }
95
+
96
+ module.exports = { ConfigService };