ntropi 1.0.0-beta
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 +298 -0
- package/package.json +22 -0
- package/src/app.js +38 -0
- package/src/libs/backend.js +171 -0
- package/src/libs/cli_parser.js +111 -0
- package/src/libs/config_generator.js +36 -0
- package/src/libs/config_state_management.js +183 -0
- package/src/libs/config_updater.js +56 -0
- package/src/libs/config_validator.js +109 -0
- package/tests/cli_parser.test.js +200 -0
- package/tests/config_genrator.test.js +23 -0
- package/tests/config_state_management.test.js +198 -0
- package/tests/config_updater.test.js +253 -0
- package/tests/config_validator.test.js +401 -0
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://ntropi.tech/" style="text-decoration: none; color: inherit;">
|
|
3
|
+
<div style="background: #14181F; color: white;
|
|
4
|
+
font-family: 'Inter', sans-serif; font-size: 4rem;
|
|
5
|
+
font-weight: bold; border: 0px; border-radius: 8px;
|
|
6
|
+
padding: 20px;">
|
|
7
|
+
Ntropi<strong style="color: red">.</strong>
|
|
8
|
+
</div>
|
|
9
|
+
</a>
|
|
10
|
+
<div style="margin-top: 8px;">
|
|
11
|
+
<strong>
|
|
12
|
+
<p>A live mock API generator that works without having any actual backend.
|
|
13
|
+
Perfect for frontend development, testing, and prototyping.</p>
|
|
14
|
+
</strong>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
## Table of Contents
|
|
19
|
+
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [Quick Start](#quick-start)
|
|
22
|
+
- [Usage](#usage)
|
|
23
|
+
- [Basic Commands](#basic-commands)
|
|
24
|
+
- [Command Options](#command-options)
|
|
25
|
+
- [Configuration File](#configuration-file)
|
|
26
|
+
- [Endpoint Properties](#endpoint-properties)
|
|
27
|
+
- [Examples](#examples)
|
|
28
|
+
- [Example 1: Simple API for Frontend Development](#example-1-simple-api-for-frontend-development)
|
|
29
|
+
- [Example 2: Simulate Slow Network](#example-2-simulate-slow-network)
|
|
30
|
+
- [Example 3: Test Error Handling](#example-3-test-error-handling)
|
|
31
|
+
- [Example 4: Multiple Endpoints](#example-4-multiple-endpoints)
|
|
32
|
+
- [Example 5: Add Endpoints](#example-5-add-endpoints)
|
|
33
|
+
- [Example 6: Custom Config File](#example-6-custom-config-file)
|
|
34
|
+
- [Example 7: Complete Workflow](#example-7-complete-workflow)
|
|
35
|
+
- [Advanced Usage](#advanced-usage)
|
|
36
|
+
- [Custom Response Data](#custom-response-data)
|
|
37
|
+
- [Testing Different Scenarios](#testing-different-scenarios)
|
|
38
|
+
- [Tips](#tips)
|
|
39
|
+
- [Troubleshooting](#troubleshooting)
|
|
40
|
+
- [Authors](#authors)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g ntropi
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
Generate a config and run the server:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ntropi --api users posts --delay 1000 --failure-rate 10
|
|
54
|
+
ntropi run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Basic Commands
|
|
60
|
+
|
|
61
|
+
#### Run the Mock Server
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Run with default config (ntropi-config.json)
|
|
65
|
+
ntropi run
|
|
66
|
+
|
|
67
|
+
# Run with custom config file
|
|
68
|
+
ntropi run --config my-config.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Generate Configuration
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Generate default config with default settings
|
|
75
|
+
ntropi
|
|
76
|
+
|
|
77
|
+
# Generate config with specific APIs
|
|
78
|
+
ntropi --api users posts products
|
|
79
|
+
|
|
80
|
+
# Generate config with delay (in milliseconds)
|
|
81
|
+
ntropi --api users --delay 2000
|
|
82
|
+
|
|
83
|
+
# Generate config with failure rate (0-100%)
|
|
84
|
+
ntropi --api users --failure-rate 50
|
|
85
|
+
|
|
86
|
+
# Combine multiple options
|
|
87
|
+
ntropi --api users posts --delay 1000 --failure-rate 25
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Command Options
|
|
91
|
+
|
|
92
|
+
| Option | Shorthand | Description | Example |
|
|
93
|
+
|--------|-----------|-------------|---------|
|
|
94
|
+
| `--help` | | Show help information | `ntropi --help` |
|
|
95
|
+
| `run` | | Run the mock server | `ntropi run` |
|
|
96
|
+
| `--api` | `-a` | Specify API endpoints | `ntropi --api users posts` |
|
|
97
|
+
| `--delay` | `-d` | Set response delay (ms) | `ntropi --delay 2000` |
|
|
98
|
+
| `--failure-rate` | `-f` | Set failure rate (0-100%) | `ntropi --failure-rate 30` |
|
|
99
|
+
| `--config` | `-c` | Specify config file | `ntropi --config custom.json` |
|
|
100
|
+
|
|
101
|
+
## Configuration File
|
|
102
|
+
|
|
103
|
+
The configuration file (`ntropi-config.json`) defines your mock API endpoints:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"endpoints": [
|
|
108
|
+
{
|
|
109
|
+
"path": "/api/users",
|
|
110
|
+
"method": "GET",
|
|
111
|
+
// If data is an array then one
|
|
112
|
+
// item is sent at random
|
|
113
|
+
"data": [
|
|
114
|
+
{"id": 1, "name": "John Doe"},
|
|
115
|
+
{"id": 2, "name": "Jane Smith"}
|
|
116
|
+
],
|
|
117
|
+
"delay": 1000,
|
|
118
|
+
"failureRate": 10
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"path": "/api/posts",
|
|
122
|
+
"method": "GET",
|
|
123
|
+
// If data is not an array then
|
|
124
|
+
// data is sent as it is
|
|
125
|
+
"data":
|
|
126
|
+
{"id": 1, "title": "Hello World"},
|
|
127
|
+
"delay": 0,
|
|
128
|
+
"failureRate": 0
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Note:** If data is an array then an item is choosen at random otherwise the data is sent as it is.
|
|
135
|
+
|
|
136
|
+
### Endpoint Properties
|
|
137
|
+
|
|
138
|
+
- **path**: API endpoint path (e.g., `/api/users`)
|
|
139
|
+
- **method**: HTTP method (`GET`, `POST`, `PUT`, `DELETE`, etc.)
|
|
140
|
+
- **data**: Response data (can be any valid JSON)
|
|
141
|
+
- **delay**: Response delay in milliseconds
|
|
142
|
+
- **failureRate**: Percentage chance of returning 500 error (0-100)
|
|
143
|
+
|
|
144
|
+
## Examples
|
|
145
|
+
|
|
146
|
+
### Example 1: Simple API for Frontend Development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Create a users API with no delay
|
|
150
|
+
ntropi --api users --delay 0 --failure-rate 0
|
|
151
|
+
ntropi run
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Access at: `http://localhost:8008/api/users`
|
|
155
|
+
|
|
156
|
+
### Example 2: Simulate Slow Network
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Create API with 3 second delay
|
|
160
|
+
ntropi --api products --delay 3000
|
|
161
|
+
ntropi run
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Example 3: Test Error Handling
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Create API with 50% failure rate
|
|
168
|
+
ntropi --api orders --failure-rate 50
|
|
169
|
+
ntropi run
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Example 4: Multiple Endpoints
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Create multiple APIs with different configurations
|
|
176
|
+
ntropi --api users posts comments products
|
|
177
|
+
ntropi run
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Then manually edit `ntropi-config.json` to customize each endpoint.
|
|
181
|
+
|
|
182
|
+
### Example 5: Add Endpoints
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Add APIs to existing configuratoin
|
|
186
|
+
ntropi --api users
|
|
187
|
+
ntropi --api posts
|
|
188
|
+
ntropi --api comments
|
|
189
|
+
ntropi --api products
|
|
190
|
+
ntropi run
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
### Example 6: Custom Config File
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Use a specific config file
|
|
198
|
+
ntropi run --config staging-api.json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Example 7: Complete Workflow
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 1. Generate config with 3 APIs
|
|
205
|
+
ntropi --api users posts comments --delay 500 --failure-rate 5
|
|
206
|
+
|
|
207
|
+
# 2. Edit ntropi-config.json to customize responses
|
|
208
|
+
|
|
209
|
+
# 3. Run the server
|
|
210
|
+
ntropi run
|
|
211
|
+
|
|
212
|
+
# 4. Access your mock APIs
|
|
213
|
+
# GET http://localhost:8008/api/users
|
|
214
|
+
# GET http://localhost:8008/api/posts
|
|
215
|
+
# GET http://localhost:8008/api/comments
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Advanced Usage
|
|
219
|
+
|
|
220
|
+
### Custom Response Data
|
|
221
|
+
|
|
222
|
+
Edit `ntropi-config.json` to add custom response data:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"endpoints": [
|
|
227
|
+
{
|
|
228
|
+
"path": "/api/users/1",
|
|
229
|
+
"method": "GET",
|
|
230
|
+
"data": {
|
|
231
|
+
"id": 1,
|
|
232
|
+
"name": "Alice Johnson",
|
|
233
|
+
"email": "alice@example.com",
|
|
234
|
+
"role": "admin"
|
|
235
|
+
},
|
|
236
|
+
"delay": 500,
|
|
237
|
+
"failureRate": 0
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Testing Different Scenarios
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"endpoints": [
|
|
248
|
+
{
|
|
249
|
+
"path": "/api/fast-endpoint",
|
|
250
|
+
"method": "GET",
|
|
251
|
+
"data": {"message": "Lightning fast!"},
|
|
252
|
+
"delay": 0,
|
|
253
|
+
"failureRate": 0
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"path": "/api/slow-endpoint",
|
|
257
|
+
"method": "GET",
|
|
258
|
+
"data": {"message": "Taking my time..."},
|
|
259
|
+
"delay": 5000,
|
|
260
|
+
"failureRate": 0
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"path": "/api/unreliable-endpoint",
|
|
264
|
+
"method": "GET",
|
|
265
|
+
"data": {"message": "Hope I work!"},
|
|
266
|
+
"delay": 1000,
|
|
267
|
+
"failureRate": 80
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Tips
|
|
274
|
+
|
|
275
|
+
1. **Start Simple**: Begin with basic endpoints and add complexity as needed
|
|
276
|
+
2. **Test Error States**: Use `failureRate` to ensure your app handles errors gracefully
|
|
277
|
+
3. **Realistic Delays**: Use delays to simulate real-world network conditions
|
|
278
|
+
4. **Version Control**: Commit your config files to share API mocks with your team
|
|
279
|
+
5. **Multiple Configs**: Use different config files for different testing scenarios
|
|
280
|
+
|
|
281
|
+
## Troubleshooting
|
|
282
|
+
|
|
283
|
+
### Port Already in Use
|
|
284
|
+
If port 8008 is busy, Ntropi will automatically try the next available port.
|
|
285
|
+
|
|
286
|
+
### Config File Not Found
|
|
287
|
+
Make sure you've generated a config file first or specify the correct path with `--config`.
|
|
288
|
+
|
|
289
|
+
### Syntax Error in Config
|
|
290
|
+
Validate your JSON file using a JSON validator or linter.
|
|
291
|
+
|
|
292
|
+
## Authors
|
|
293
|
+
|
|
294
|
+
- [Amitrajeet Konch](https://x.com/amitrajeet7635)
|
|
295
|
+
- [Pushpender Singh](https://x.com/Pushpender20359)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ntropi",
|
|
3
|
+
"version": "1.0.0-beta",
|
|
4
|
+
"description": "A live mock api generator that works without having any actual backend.",
|
|
5
|
+
"author": "Amitrajeet Konch, Pushpender Singh",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "src/app.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ntropi": "src/app.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"dotenv": "^17.2.3",
|
|
16
|
+
"fastify": "^5.7.4"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"verdaccio": "^6.2.5",
|
|
20
|
+
"vitest": "^4.0.18"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/app.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { run_server } from './libs/backend.js';
|
|
4
|
+
import { parseArgs } from './libs/cli_parser.js';
|
|
5
|
+
import { updateConfig } from './libs/config_updater.js';
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const CONFIG = parseArgs(args);
|
|
9
|
+
|
|
10
|
+
// Error Handling
|
|
11
|
+
if (CONFIG.error) {
|
|
12
|
+
console.log(`Error: ${CONFIG.error}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Help
|
|
17
|
+
if (CONFIG.help) {
|
|
18
|
+
console.log(`
|
|
19
|
+
Usage: node app.js [options]
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--help Show help information
|
|
23
|
+
-a, --api Specify API endpoints to generate (e.g., --api users orders products)
|
|
24
|
+
-d, --delay <num> Set delay in seconds (non-negative integer)
|
|
25
|
+
-f, --failure-rate <num> Set failure rate percentage (0-100)
|
|
26
|
+
-c, --config <file> Specify configuration file (default: ntropi-config.json generated)
|
|
27
|
+
run Start the server with the default configuration file (-c for custom config file)
|
|
28
|
+
`);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (CONFIG.run) {
|
|
33
|
+
// Run Server
|
|
34
|
+
const configPath = CONFIG.config || 'ntropi-config.json';
|
|
35
|
+
await run_server(configPath);
|
|
36
|
+
} else {
|
|
37
|
+
updateConfig(CONFIG);
|
|
38
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import Fastify from "fastify";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { getConfigStateManager } from "./config_state_management.js";
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
let PORT;
|
|
8
|
+
let currentFastifyInstance = null;
|
|
9
|
+
|
|
10
|
+
function getEndpointSignature(endpoint) {
|
|
11
|
+
return `${endpoint.method}:${endpoint.path}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hasEndpointChanges(oldConfig, newConfig) {
|
|
15
|
+
const oldSignatures = new Set(oldConfig.endpoints.map(getEndpointSignature));
|
|
16
|
+
const newSignatures = new Set(newConfig.endpoints.map(getEndpointSignature));
|
|
17
|
+
|
|
18
|
+
// Check if any endpoints were added or removed
|
|
19
|
+
if (oldSignatures.size !== newSignatures.size) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const sig of oldSignatures) {
|
|
24
|
+
if (!newSignatures.has(sig)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const sig of newSignatures) {
|
|
30
|
+
if (!oldSignatures.has(sig)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function start_listener(fastify) {
|
|
39
|
+
fastify.listen({ port: PORT}, function (err, address) {
|
|
40
|
+
if (err) {
|
|
41
|
+
PORT = (PORT + 1);
|
|
42
|
+
if (PORT > 65535) {
|
|
43
|
+
console.error("No available ports found. Please free up a port and try again.");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
start_listener(fastify);
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`Server listening at ${address}`);
|
|
49
|
+
const configManager = getConfigStateManager();
|
|
50
|
+
const config = configManager.getState();
|
|
51
|
+
output_endpoint_path(PORT, config);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function generate_endpoint_path(fastify, config, configManager) {
|
|
57
|
+
for (let endpoint of config.endpoints) {
|
|
58
|
+
fastify.route({
|
|
59
|
+
method: endpoint.method,
|
|
60
|
+
url: endpoint.path,
|
|
61
|
+
handler: async (request, reply) => {
|
|
62
|
+
// Get latest config state for hot reload support
|
|
63
|
+
const currentConfig = configManager.getState();
|
|
64
|
+
const currentEndpoint = currentConfig.endpoints.find(
|
|
65
|
+
ep => ep.path === endpoint.path && ep.method === endpoint.method
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!currentEndpoint) {
|
|
69
|
+
reply.code(404).send({ error: "Endpoint not found in current config" });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Simulate delay with current config value
|
|
74
|
+
if (currentEndpoint.delay && currentEndpoint.delay > 0) {
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, currentEndpoint.delay));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Simulate failure with current config value
|
|
79
|
+
if (currentEndpoint.failureRate && Math.random() * 100 < currentEndpoint.failureRate) {
|
|
80
|
+
reply.code(500).send({ error: "Simulated server error" });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(currentEndpoint.data)) {
|
|
85
|
+
currentEndpoint.data = currentEndpoint.data[Math.floor(Math.random() * currentEndpoint.data.length)];
|
|
86
|
+
}
|
|
87
|
+
reply.send(currentEndpoint.data);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function output_endpoint_path(PORT, config) {
|
|
94
|
+
for (let endpoint of config.endpoints) {
|
|
95
|
+
console.log(`Route: http://localhost:${PORT}${endpoint.path}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function printNtropi() {
|
|
100
|
+
console.log(`
|
|
101
|
+
_ _ _ _
|
|
102
|
+
| \\ | | |_ _ __ ___ _ __ (_)
|
|
103
|
+
| \\| | __| '__/ _ \\| '_ \\| |
|
|
104
|
+
| |\\ | |_| | | (_) | |_) | | _
|
|
105
|
+
|_| \\_|\\__|_| \\___/| .__/|_| (_)
|
|
106
|
+
|_|
|
|
107
|
+
`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function run_server(configPath = 'ntropi-config.json') {
|
|
111
|
+
printNtropi();
|
|
112
|
+
|
|
113
|
+
// Initialize config state manager
|
|
114
|
+
const configManager = getConfigStateManager(configPath);
|
|
115
|
+
const initResult = configManager.initialize();
|
|
116
|
+
|
|
117
|
+
if (!initResult.success) {
|
|
118
|
+
console.error(`Failed to load config: ${initResult.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let currentConfig = configManager.getState();
|
|
123
|
+
|
|
124
|
+
// Enable hot reload for automatic config updates
|
|
125
|
+
configManager.enableHotReload();
|
|
126
|
+
|
|
127
|
+
// Subscribe to config changes for logging and server restart
|
|
128
|
+
configManager.subscribe((event, state, details) => {
|
|
129
|
+
if (event === 'updated') {
|
|
130
|
+
// Check if endpoints were added or removed
|
|
131
|
+
if (hasEndpointChanges(currentConfig, state)) {
|
|
132
|
+
console.log('Endpoints added or removed - restarting server...');
|
|
133
|
+
restartServer(configManager);
|
|
134
|
+
currentConfig = state;
|
|
135
|
+
} else {
|
|
136
|
+
console.log('Config updated - endpoints will use new delay and failure rate values');
|
|
137
|
+
currentConfig = state;
|
|
138
|
+
}
|
|
139
|
+
} else if (event === 'fallback') {
|
|
140
|
+
console.log('Config update failed - using previous working configuration');
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
startServer(configManager);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function startServer(configManager) {
|
|
148
|
+
const config = configManager.getState();
|
|
149
|
+
const fastify = Fastify({ logger: false });
|
|
150
|
+
PORT = Number(process.env.PORT) || 8008;
|
|
151
|
+
|
|
152
|
+
generate_endpoint_path(fastify, config, configManager);
|
|
153
|
+
currentFastifyInstance = fastify;
|
|
154
|
+
start_listener(fastify);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function restartServer(configManager) {
|
|
158
|
+
if (currentFastifyInstance) {
|
|
159
|
+
try {
|
|
160
|
+
await currentFastifyInstance.close();
|
|
161
|
+
console.log('Server stopped');
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('Error stopping server:', error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Reset to find next available port if needed
|
|
168
|
+
PORT = Number(process.env.PORT) || 8008;
|
|
169
|
+
console.log('Starting server with new configuration...');
|
|
170
|
+
startServer(configManager);
|
|
171
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { updateConfig } from "./config_updater.js";
|
|
2
|
+
|
|
3
|
+
export function parseArgs(args) {
|
|
4
|
+
const result = {};
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < args.length; i++) {
|
|
7
|
+
let arg = args[i];
|
|
8
|
+
|
|
9
|
+
// Handle 'run' command (without dashes)
|
|
10
|
+
if (arg === 'run') {
|
|
11
|
+
result.run = true;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Skip if not a flag
|
|
16
|
+
if (!arg.startsWith('-')) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Map shorthand to full argument names
|
|
21
|
+
const shorthandMap = {
|
|
22
|
+
'-d': '--delay',
|
|
23
|
+
'-f': '--failure-rate',
|
|
24
|
+
'-c': '--config',
|
|
25
|
+
'-a': '--api',
|
|
26
|
+
'-r': '--run'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (shorthandMap[arg]) {
|
|
30
|
+
arg = shorthandMap[arg];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const validArgs = ['--help', '--delay', '--failure-rate', '--config', "--api", '--run'];
|
|
34
|
+
if (!validArgs.includes(arg)) {
|
|
35
|
+
return { error: `Unknown argument: ${args[i]}` };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (arg === '--help') {
|
|
39
|
+
result.help = true;
|
|
40
|
+
} else if (arg === '--run') {
|
|
41
|
+
result.run = true;
|
|
42
|
+
} else if (arg === '--delay') {
|
|
43
|
+
const delayValue = args[i + 1];
|
|
44
|
+
const delayNum = Number(delayValue);
|
|
45
|
+
|
|
46
|
+
if (isNaN(delayNum) || delayNum < 0 || !Number.isInteger(delayNum)) {
|
|
47
|
+
return { error: 'Delay must be a non-negative integer' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
result.delay = delayNum;
|
|
51
|
+
i++;
|
|
52
|
+
} else if (arg === '--api') {
|
|
53
|
+
// Collect all values following --api until the next flag or end of args
|
|
54
|
+
const apiValues = [];
|
|
55
|
+
let j = i + 1;
|
|
56
|
+
while (j < args.length && !args[j].startsWith('--')) {
|
|
57
|
+
apiValues.push(args[j]);
|
|
58
|
+
j++;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (apiValues.length === 0) {
|
|
62
|
+
return { error: 'API argument requires at least one API name' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
result.apis = apiValues;
|
|
66
|
+
i = j - 1; // Update i to the last processed value
|
|
67
|
+
} else if (arg === '--failure-rate') {
|
|
68
|
+
const failureRateValue = args[i + 1];
|
|
69
|
+
const failureRateNum = Number(failureRateValue);
|
|
70
|
+
|
|
71
|
+
if (isNaN(failureRateNum) || failureRateNum < 0 || failureRateNum > 100) {
|
|
72
|
+
return { error: 'Failure rate must be between 0 and 100' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
result.failureRate = failureRateNum;
|
|
76
|
+
i++;
|
|
77
|
+
} else if (arg === '--config') {
|
|
78
|
+
result.config = args[i + 1];
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (result.run) {
|
|
84
|
+
if (result.config) {
|
|
85
|
+
return result;
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
result.config = 'ntropi-config.json';
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!result.help) {
|
|
93
|
+
if (!result.config) {
|
|
94
|
+
result.config = 'ntropi-config.json';
|
|
95
|
+
if (!result.delay) {
|
|
96
|
+
result.delay = 0;
|
|
97
|
+
}
|
|
98
|
+
if (!result.failureRate) {
|
|
99
|
+
result.failureRate = 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if(result.config) {
|
|
103
|
+
const response = updateConfig(result);
|
|
104
|
+
if (response.error) {
|
|
105
|
+
console.log(`Error: ${response.error}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export function generateConfig(delay=0, failureRate=0, apis=[], configFileName='ntropi-config.json') {
|
|
4
|
+
let fileConfigurationJSON = {"endpoints": []};
|
|
5
|
+
if (apis.length == 0) {
|
|
6
|
+
fileConfigurationJSON["endpoints"].push(
|
|
7
|
+
{
|
|
8
|
+
'path': '/api/health_check',
|
|
9
|
+
"method": "GET",
|
|
10
|
+
"data": ["OK", "Healthy", "Working Fine", "All Good", "Up and Running"],
|
|
11
|
+
"delay": delay,
|
|
12
|
+
"failureRate": failureRate
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
} else {
|
|
16
|
+
for (let api of apis) {
|
|
17
|
+
fileConfigurationJSON["endpoints"].push(
|
|
18
|
+
{
|
|
19
|
+
'path': `/api/${api}`,
|
|
20
|
+
"method": "GET",
|
|
21
|
+
"data": [`Response from ${api}`],
|
|
22
|
+
"delay": delay,
|
|
23
|
+
"failureRate": failureRate
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (fs.existsSync(configFileName)) {
|
|
30
|
+
return {error: "CONFIG_ALREADY_EXISTS"};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fs.writeFileSync(configFileName, JSON.stringify(fileConfigurationJSON, null, 2));
|
|
34
|
+
return {success: true};
|
|
35
|
+
|
|
36
|
+
}
|