@gugananuvem/aws-local-simulator 1.0.25 → 1.0.26
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 +45 -0
- package/package.json +28 -83
- package/src/config/default-config.js +15 -4
- package/src/server.js +257 -21
- package/src/services/apigateway/server.js +37 -0
- package/src/services/apigateway/simulator.js +148 -4
- package/src/services/cognito/server.js +8 -14
- package/src/services/cognito/simulator.js +38 -11
- package/src/services/dynamodb/simulator.js +126 -49
- package/src/services/kms/server.js +15 -1
- package/src/services/kms/simulator.js +48 -28
- package/src/services/lambda/server.js +24 -0
- package/src/services/lambda/simulator.js +136 -12
- package/src/services/parameter-store/simulator.js +1 -1
- package/src/services/s3/server.js +21 -0
- package/src/services/s3/simulator.js +4 -1
- package/src/services/secret-manager/server.js +2 -1
- package/src/services/secret-manager/simulator.js +21 -10
- package/src/services/sns/server.js +32 -5
- package/src/services/sqs/server.js +11 -0
- package/src/services/sqs/simulator.js +74 -6
package/README.md
CHANGED
|
@@ -186,6 +186,50 @@ npx aws-local-simulator reset
|
|
|
186
186
|
npx aws-local-simulator status
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
+
## 🖥️ Management API
|
|
190
|
+
|
|
191
|
+
O simulador expõe uma API de gerenciamento em tempo de execução na porta `9999` (configurável via `adminPort`). Ela permite habilitar e desabilitar serviços individualmente sem reiniciar o processo.
|
|
192
|
+
|
|
193
|
+
### Endpoints da Management API
|
|
194
|
+
|
|
195
|
+
| Método | Rota | Descrição |
|
|
196
|
+
|--------|------|-----------|
|
|
197
|
+
| GET | `/__admin/services` | Lista todos os 19 serviços com status |
|
|
198
|
+
| GET | `/__admin/services/:name` | Status de um serviço específico |
|
|
199
|
+
| POST | `/__admin/services/:name/enable` | Habilita um serviço parado |
|
|
200
|
+
| POST | `/__admin/services/:name/disable` | Desabilita um serviço em execução |
|
|
201
|
+
|
|
202
|
+
### Exemplos
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Listar todos os serviços
|
|
206
|
+
curl http://localhost:9999/__admin/services
|
|
207
|
+
|
|
208
|
+
# Habilitar DynamoDB
|
|
209
|
+
curl -X POST http://localhost:9999/__admin/services/dynamodb/enable
|
|
210
|
+
|
|
211
|
+
# Desabilitar SQS
|
|
212
|
+
curl -X POST http://localhost:9999/__admin/services/sqs/disable
|
|
213
|
+
|
|
214
|
+
# Status de um serviço
|
|
215
|
+
curl http://localhost:9999/__admin/services/lambda
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Configuração da porta admin
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"adminPort": 9999
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Ou via variável de ambiente:
|
|
227
|
+
```bash
|
|
228
|
+
AWS_LOCAL_SIMULATOR_ADMIN_PORT=9999 npx aws-local-simulator start
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> A Management API nunca persiste alterações em disco — todo o estado é mantido em memória. Para uso em desenvolvimento local apenas.
|
|
232
|
+
|
|
189
233
|
## 🔌 Endpoints
|
|
190
234
|
|
|
191
235
|
| Serviço | Endpoint | Admin |
|
|
@@ -210,6 +254,7 @@ npx aws-local-simulator status
|
|
|
210
254
|
| CloudFormation | http://localhost:4580 | http://localhost:4580/__admin/stacks |
|
|
211
255
|
| Athena | http://localhost:4599 | http://localhost:4599/__admin/health |
|
|
212
256
|
| ECS | http://localhost:8080 | http://localhost:8080/__admin/clusters |
|
|
257
|
+
| Management API | http://localhost:9999 | http://localhost:9999/__admin/services |
|
|
213
258
|
|
|
214
259
|
## 🧪 Testando com AWS CLI
|
|
215
260
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gugananuvem/aws-local-simulator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Simulador local completo para serviços AWS",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,31 +9,30 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/aws-local-simulator.js start"
|
|
11
11
|
},
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"detectOpenHandles": true
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@aws-sdk/client-api-gateway": "^3.0.0",
|
|
14
|
+
"@aws-sdk/client-cognito-identity-provider": "^3.0.0",
|
|
15
|
+
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
16
|
+
"@aws-sdk/client-ecs": "^3.0.0",
|
|
17
|
+
"@aws-sdk/client-lambda": "^3.0.0",
|
|
18
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
19
|
+
"@aws-sdk/client-sqs": "^3.0.0",
|
|
20
|
+
"@aws-sdk/lib-dynamodb": "^3.0.0",
|
|
21
|
+
"child_process": "1.0.2",
|
|
22
|
+
"cors": "^2.8.5",
|
|
23
|
+
"crypto": "1.0.1",
|
|
24
|
+
"dotenv": "^16.3.1",
|
|
25
|
+
"express": "^4.18.2",
|
|
26
|
+
"fs": "0.0.1-security",
|
|
27
|
+
"glob": "10.5.0",
|
|
28
|
+
"http": "0.0.1-security",
|
|
29
|
+
"js-yaml": "4.1.1",
|
|
30
|
+
"jsonwebtoken": "^9.0.2",
|
|
31
|
+
"mkdirp": "^3.0.1",
|
|
32
|
+
"path": "0.12.7",
|
|
33
|
+
"url": "0.11.4",
|
|
34
|
+
"urlpattern-polyfill": "^10.0.0",
|
|
35
|
+
"uuid": "14.0.0"
|
|
37
36
|
},
|
|
38
37
|
"keywords": [
|
|
39
38
|
"aws",
|
|
@@ -43,21 +42,7 @@
|
|
|
43
42
|
"s3",
|
|
44
43
|
"sqs",
|
|
45
44
|
"lambda",
|
|
46
|
-
"cognito"
|
|
47
|
-
"apigateway",
|
|
48
|
-
"kms",
|
|
49
|
-
"config",
|
|
50
|
-
"parameter-store",
|
|
51
|
-
"secret-manager",
|
|
52
|
-
"cloudformation",
|
|
53
|
-
"eventbridge",
|
|
54
|
-
"sns",
|
|
55
|
-
"ecs",
|
|
56
|
-
"sts",
|
|
57
|
-
"cloudtrail",
|
|
58
|
-
"cloudwatch",
|
|
59
|
-
"development",
|
|
60
|
-
"testing"
|
|
45
|
+
"cognito"
|
|
61
46
|
],
|
|
62
47
|
"author": {
|
|
63
48
|
"name": "gugananuvem",
|
|
@@ -69,7 +54,7 @@
|
|
|
69
54
|
"url": "git+https://github.com/gugananuvem/aws-local-simulator.git"
|
|
70
55
|
},
|
|
71
56
|
"engines": {
|
|
72
|
-
"node": ">=
|
|
57
|
+
"node": ">=18.0.0"
|
|
73
58
|
},
|
|
74
59
|
"files": [
|
|
75
60
|
"src/",
|
|
@@ -81,46 +66,6 @@
|
|
|
81
66
|
"publishConfig": {
|
|
82
67
|
"directory": "dist"
|
|
83
68
|
},
|
|
84
|
-
"
|
|
85
|
-
"cors": "^2.8.5",
|
|
86
|
-
"dotenv": "^16.3.1",
|
|
87
|
-
"express": "^4.18.2",
|
|
88
|
-
"glob": "10.5.0",
|
|
89
|
-
"jsonwebtoken": "^9.0.2",
|
|
90
|
-
"mkdirp": "^3.0.1",
|
|
91
|
-
"urlpattern-polyfill": "^10.0.0",
|
|
92
|
-
"uuid": "14.0.0"
|
|
93
|
-
},
|
|
94
|
-
"peerDependencies": {
|
|
95
|
-
"@aws-sdk/client-api-gateway": "^3.0.0",
|
|
96
|
-
"@aws-sdk/client-cognito-identity-provider": "^3.0.0",
|
|
97
|
-
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
98
|
-
"@aws-sdk/client-ecs": "^3.0.0",
|
|
99
|
-
"@aws-sdk/client-lambda": "^3.1032.0",
|
|
100
|
-
"@aws-sdk/client-s3": "^3.0.0",
|
|
101
|
-
"@aws-sdk/client-sqs": "^3.0.0",
|
|
102
|
-
"@aws-sdk/lib-dynamodb": "^3.0.0"
|
|
103
|
-
},
|
|
104
|
-
"peerDependenciesMeta": {
|
|
105
|
-
"@aws-sdk/client-dynamodb": {
|
|
106
|
-
"optional": true
|
|
107
|
-
},
|
|
108
|
-
"@aws-sdk/client-s3": {
|
|
109
|
-
"optional": true
|
|
110
|
-
},
|
|
111
|
-
"@aws-sdk/client-sqs": {
|
|
112
|
-
"optional": true
|
|
113
|
-
},
|
|
114
|
-
"@aws-sdk/client-cognito-identity-provider": {
|
|
115
|
-
"optional": true
|
|
116
|
-
},
|
|
117
|
-
"@aws-sdk/client-api-gateway": {
|
|
118
|
-
"optional": true
|
|
119
|
-
},
|
|
120
|
-
"@aws-sdk/client-ecs": {
|
|
121
|
-
"optional": true
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
"buildDate": "2026-04-24T17:52:50.093Z",
|
|
69
|
+
"buildDate": "2026-04-27T11:03:00.202Z",
|
|
125
70
|
"published": true
|
|
126
71
|
}
|
|
@@ -9,12 +9,16 @@ module.exports = {
|
|
|
9
9
|
s3: true,
|
|
10
10
|
sqs: true,
|
|
11
11
|
lambda: true,
|
|
12
|
-
sns:
|
|
13
|
-
eventbridge:
|
|
12
|
+
sns: true,
|
|
13
|
+
eventbridge: true,
|
|
14
14
|
ecs: false,
|
|
15
|
-
cognito:
|
|
16
|
-
apigateway:
|
|
15
|
+
cognito: true,
|
|
16
|
+
apigateway: true,
|
|
17
17
|
sts: true,
|
|
18
|
+
"secret-manager": true,
|
|
19
|
+
"parameter-store": true,
|
|
20
|
+
kms: true,
|
|
21
|
+
kinesis: true,
|
|
18
22
|
},
|
|
19
23
|
|
|
20
24
|
// Portas padrão
|
|
@@ -30,6 +34,10 @@ module.exports = {
|
|
|
30
34
|
apigateway: 4567,
|
|
31
35
|
sts: 9326,
|
|
32
36
|
athena: 4599,
|
|
37
|
+
secretManager: 4001,
|
|
38
|
+
parameterStore: 4002,
|
|
39
|
+
kms: 4000,
|
|
40
|
+
kinesis: 4568,
|
|
33
41
|
},
|
|
34
42
|
apigateway: {
|
|
35
43
|
defaultCors: {
|
|
@@ -65,4 +73,7 @@ module.exports = {
|
|
|
65
73
|
|
|
66
74
|
// Configurações adicionais
|
|
67
75
|
additional: {},
|
|
76
|
+
|
|
77
|
+
// Porta do servidor de administração (Management API)
|
|
78
|
+
adminPort: 9999,
|
|
68
79
|
};
|
package/src/server.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const mkdirp = require("mkdirp");
|
|
8
|
+
const express = require("express");
|
|
9
|
+
const cors = require("cors");
|
|
8
10
|
const logger = require("./utils/logger");
|
|
9
11
|
|
|
10
12
|
// Importa serviços
|
|
@@ -34,6 +36,10 @@ class Server {
|
|
|
34
36
|
this.services = [];
|
|
35
37
|
this.servicesMap = new Map();
|
|
36
38
|
this.running = false;
|
|
39
|
+
this.managementApp = express();
|
|
40
|
+
this.managementApp.use(cors());
|
|
41
|
+
this.managementApp.use(express.json());
|
|
42
|
+
this.managementServer = null;
|
|
37
43
|
this.setupDataDir();
|
|
38
44
|
this.setupLogLevel();
|
|
39
45
|
}
|
|
@@ -63,6 +69,8 @@ class Server {
|
|
|
63
69
|
try {
|
|
64
70
|
await this.initializeServices();
|
|
65
71
|
await this.startServices();
|
|
72
|
+
this.setupManagementRoutes();
|
|
73
|
+
await this.startManagementServer();
|
|
66
74
|
|
|
67
75
|
this.running = true;
|
|
68
76
|
this.printStatus();
|
|
@@ -72,28 +80,150 @@ class Server {
|
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
+
|
|
96
222
|
];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async initializeServices() {
|
|
226
|
+
const serviceOrder = this.buildServiceRegistry();
|
|
97
227
|
|
|
98
228
|
for (const serviceDef of serviceOrder) {
|
|
99
229
|
if (this.config.services[serviceDef.name]) {
|
|
@@ -143,6 +273,11 @@ class Server {
|
|
|
143
273
|
const stopPromises = [...this.services].reverse().map((service) => service.stop());
|
|
144
274
|
await Promise.all(stopPromises);
|
|
145
275
|
|
|
276
|
+
if (this.managementServer) {
|
|
277
|
+
await new Promise((resolve) => this.managementServer.close(resolve));
|
|
278
|
+
this.managementServer = null;
|
|
279
|
+
}
|
|
280
|
+
|
|
146
281
|
this.running = false;
|
|
147
282
|
logger.success("✅ Todos os serviços foram parados");
|
|
148
283
|
} catch (error) {
|
|
@@ -177,6 +312,107 @@ class Server {
|
|
|
177
312
|
return this.servicesMap.get(name);
|
|
178
313
|
}
|
|
179
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
|
+
|
|
180
416
|
printStatus() {
|
|
181
417
|
logger.info("\n" + "=".repeat(60));
|
|
182
418
|
logger.info("📊 Status dos Serviços:");
|
|
@@ -209,6 +209,18 @@ class APIGatewayServer {
|
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
+
this.app.patch('/restapis/:apiId', (req, res) => {
|
|
213
|
+
try {
|
|
214
|
+
const result = this.simulator.updateRestApi({
|
|
215
|
+
...req.body,
|
|
216
|
+
restApiId: req.params.apiId
|
|
217
|
+
});
|
|
218
|
+
res.json(result);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
res.status(400).json({ error: error.message });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
212
224
|
this.app.delete('/restapis/:apiId', (req, res) => {
|
|
213
225
|
try {
|
|
214
226
|
this.simulator.deleteRestApi({ restApiId: req.params.apiId });
|
|
@@ -509,6 +521,31 @@ class APIGatewayServer {
|
|
|
509
521
|
}
|
|
510
522
|
});
|
|
511
523
|
|
|
524
|
+
this.app.post('/__admin/apis/:apiId/endpoints', (req, res) => {
|
|
525
|
+
try {
|
|
526
|
+
const result = this.simulator.putEndpoint({
|
|
527
|
+
...req.body,
|
|
528
|
+
restApiId: req.params.apiId
|
|
529
|
+
});
|
|
530
|
+
res.json(result);
|
|
531
|
+
} catch (error) {
|
|
532
|
+
res.status(400).json({ error: error.message });
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
this.app.delete('/__admin/apis/:apiId/endpoints', (req, res) => {
|
|
537
|
+
try {
|
|
538
|
+
const result = this.simulator.deleteEndpoint({
|
|
539
|
+
restApiId: req.params.apiId,
|
|
540
|
+
path: req.query.path,
|
|
541
|
+
method: req.query.method
|
|
542
|
+
});
|
|
543
|
+
res.json(result);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
res.status(400).json({ error: error.message });
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
512
549
|
this.app.get('/__admin/apikeys', (req, res) => {
|
|
513
550
|
const keys = Array.from(this.simulator.apiKeys.values()).map(k => ({
|
|
514
551
|
id: k.id,
|