ntropi 1.0.1-beta → 1.0.3-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.
Potentially problematic release.
This version of ntropi might be problematic. Click here for more details.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/libs/backend.js +0 -171
- package/src/libs/cli_parser.js +0 -111
- package/src/libs/config_generator.js +0 -36
- package/src/libs/config_state_management.js +0 -183
- package/src/libs/config_updater.js +0 -56
- package/src/libs/config_validator.js +0 -109
package/README.md
CHANGED
package/package.json
CHANGED
package/src/libs/backend.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
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 http://localhost:${PORT}/`);
|
|
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
|
-
}
|
package/src/libs/cli_parser.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import { validateConfig } from './config_validator.js';
|
|
3
|
-
import { updateConfig } from './config_updater.js';
|
|
4
|
-
|
|
5
|
-
class ConfigStateManager {
|
|
6
|
-
constructor(configPath = 'ntropi-config.json') {
|
|
7
|
-
this.configPath = configPath;
|
|
8
|
-
this.currentState = null;
|
|
9
|
-
this.lastWorkingState = null;
|
|
10
|
-
this.fileWatcher = null;
|
|
11
|
-
this.subscribers = [];
|
|
12
|
-
this.isInitialized = false;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Initialize the state manager and load initial config
|
|
16
|
-
initialize() {
|
|
17
|
-
if (this.isInitialized) {
|
|
18
|
-
console.warn('Config state manager already initialized');
|
|
19
|
-
return { success: true, state: this.currentState };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const result = this.loadConfig();
|
|
23
|
-
if (result.success) {
|
|
24
|
-
this.isInitialized = true;
|
|
25
|
-
this.lastWorkingState = JSON.parse(JSON.stringify(this.currentState));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return result;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Load and validate config file
|
|
32
|
-
loadConfig() {
|
|
33
|
-
try {
|
|
34
|
-
if (!fs.existsSync(this.configPath)) {
|
|
35
|
-
console.log('Config file not found, creating default config');
|
|
36
|
-
updateConfig({ config: this.configPath });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const fileContent = fs.readFileSync(this.configPath, 'utf-8');
|
|
40
|
-
const parsedConfig = JSON.parse(fileContent);
|
|
41
|
-
|
|
42
|
-
const validation = validateConfig(parsedConfig);
|
|
43
|
-
if (!validation.valid) {
|
|
44
|
-
console.error('Config validation failed:', validation.error);
|
|
45
|
-
return {
|
|
46
|
-
success: false,
|
|
47
|
-
error: 'CONFIG_VALIDATION_FAILED',
|
|
48
|
-
message: 'Config validation failed',
|
|
49
|
-
validationError: validation.error
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
this.currentState = validation.config;
|
|
54
|
-
return { success: true, state: this.currentState };
|
|
55
|
-
|
|
56
|
-
} catch (error) {
|
|
57
|
-
if (error instanceof SyntaxError) {
|
|
58
|
-
return {
|
|
59
|
-
success: false,
|
|
60
|
-
error: 'CONFIG_PARSE_ERROR',
|
|
61
|
-
message: 'Invalid JSON in config file'
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
error: 'CONFIG_LOAD_ERROR',
|
|
67
|
-
message: error.message
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Enable hot reload with file watching
|
|
73
|
-
enableHotReload() {
|
|
74
|
-
if (!this.isInitialized) {
|
|
75
|
-
throw new Error('Config state manager must be initialized before enabling hot reload');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (this.fileWatcher) {
|
|
79
|
-
console.warn('Hot reload already enabled');
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this.fileWatcher = fs.watch(this.configPath, (eventType) => {
|
|
84
|
-
if (eventType === 'change') {
|
|
85
|
-
this.handleConfigChange();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
console.log(`Hot reload enabled for ${this.configPath}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Handle config file changes
|
|
93
|
-
handleConfigChange() {
|
|
94
|
-
console.log('Config file change detected, reloading...');
|
|
95
|
-
|
|
96
|
-
const result = this.loadConfig();
|
|
97
|
-
|
|
98
|
-
if (result.success) {
|
|
99
|
-
this.lastWorkingState = JSON.parse(JSON.stringify(this.currentState));
|
|
100
|
-
console.log('Config reloaded successfully');
|
|
101
|
-
this.notifySubscribers('updated', this.currentState);
|
|
102
|
-
} else {
|
|
103
|
-
console.error('Config reload failed, falling back to last working state');
|
|
104
|
-
this.currentState = this.lastWorkingState;
|
|
105
|
-
this.notifySubscribers('fallback', this.lastWorkingState, result);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Subscribe to config changes
|
|
110
|
-
subscribe(callback) {
|
|
111
|
-
if (typeof callback !== 'function') {
|
|
112
|
-
throw new Error('Callback must be a function');
|
|
113
|
-
}
|
|
114
|
-
this.subscribers.push(callback);
|
|
115
|
-
return () => this.unsubscribe(callback);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Unsubscribe from config changes
|
|
119
|
-
unsubscribe(callback) {
|
|
120
|
-
this.subscribers = this.subscribers.filter(cb => cb !== callback);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Notify all subscribers of state changes
|
|
124
|
-
notifySubscribers(event, state, details = null) {
|
|
125
|
-
this.subscribers.forEach(callback => {
|
|
126
|
-
try {
|
|
127
|
-
callback(event, state, details);
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error('Error in subscriber callback:', error);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Get current config state as JSON
|
|
135
|
-
getState() {
|
|
136
|
-
if (!this.isInitialized) {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
return JSON.parse(JSON.stringify(this.currentState));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Get last working config state as JSON
|
|
143
|
-
getLastWorkingState() {
|
|
144
|
-
if (!this.lastWorkingState) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
return JSON.parse(JSON.stringify(this.lastWorkingState));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Disable hot reload
|
|
151
|
-
disableHotReload() {
|
|
152
|
-
if (this.fileWatcher) {
|
|
153
|
-
this.fileWatcher.close();
|
|
154
|
-
this.fileWatcher = null;
|
|
155
|
-
console.log('Hot reload disabled');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Cleanup and shutdown
|
|
160
|
-
shutdown() {
|
|
161
|
-
this.disableHotReload();
|
|
162
|
-
this.subscribers = [];
|
|
163
|
-
this.isInitialized = false;
|
|
164
|
-
console.log('Config state manager shutdown complete');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Singleton instance
|
|
169
|
-
let configStateManagerInstance = null;
|
|
170
|
-
|
|
171
|
-
export function getConfigStateManager(configPath = 'ntropi-config.json') {
|
|
172
|
-
if (!configStateManagerInstance) {
|
|
173
|
-
configStateManagerInstance = new ConfigStateManager(configPath);
|
|
174
|
-
}
|
|
175
|
-
return configStateManagerInstance;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function resetConfigStateManager() {
|
|
179
|
-
if (configStateManagerInstance) {
|
|
180
|
-
configStateManagerInstance.shutdown();
|
|
181
|
-
configStateManagerInstance = null;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import { validateConfig } from "./config_validator.js";
|
|
3
|
-
import { generateConfig } from "./config_generator.js";
|
|
4
|
-
|
|
5
|
-
export function updateConfig(configuration_data) {
|
|
6
|
-
const config_file = configuration_data.config;
|
|
7
|
-
const delay = configuration_data.delay;
|
|
8
|
-
const failureRate = configuration_data.failureRate;
|
|
9
|
-
const apis = configuration_data.apis;
|
|
10
|
-
|
|
11
|
-
if (!fs.existsSync(config_file)) {
|
|
12
|
-
return generateConfig(delay, failureRate, apis, config_file);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
const config_data = fs.readFileSync(config_file, "utf-8");
|
|
17
|
-
const result = validateConfig(JSON.parse(config_data));
|
|
18
|
-
|
|
19
|
-
if (!result.valid) {
|
|
20
|
-
return {
|
|
21
|
-
success: false,
|
|
22
|
-
error: result.error
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let updatedConfig = result.config;
|
|
27
|
-
|
|
28
|
-
// Append new APIs to existing config
|
|
29
|
-
for (let api of apis) {
|
|
30
|
-
// Check if API already exists in config
|
|
31
|
-
const existingEndpoint = updatedConfig.endpoints.find(endpoint => endpoint.path === `/api/${api}`);
|
|
32
|
-
if (!existingEndpoint) {
|
|
33
|
-
updatedConfig.endpoints.push({
|
|
34
|
-
path: `/api/${api}`,
|
|
35
|
-
method: "GET",
|
|
36
|
-
data: [`Response from ${api}`],
|
|
37
|
-
delay: delay,
|
|
38
|
-
failureRate: failureRate
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Write updated config back to file
|
|
44
|
-
fs.writeFileSync(config_file, JSON.stringify(updatedConfig, null, 2), "utf-8");
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
success: true
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
} catch (error) {
|
|
51
|
-
return {
|
|
52
|
-
success: false,
|
|
53
|
-
error: "Error reading or parsing config file: " + error.message
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
export function validateConfig(config) {
|
|
2
|
-
if (!config || typeof config !== 'object') {
|
|
3
|
-
return { valid: false, error: 'Config must be a valid JSON object' };
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
if (!config.endpoints) {
|
|
7
|
-
return { valid: false, error: 'Config must have an "endpoints" property' };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (!Array.isArray(config.endpoints)) {
|
|
11
|
-
return { valid: false, error: 'Endpoints must be an array' };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (config.endpoints.length === 0) {
|
|
15
|
-
return { valid: false, error: 'Endpoints array cannot be empty' };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const validatedConfig = {
|
|
19
|
-
endpoints: []
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
for (let i = 0; i < config.endpoints.length; i++) {
|
|
23
|
-
const endpoint = config.endpoints[i];
|
|
24
|
-
const validationResult = validateEndpoint(endpoint, i);
|
|
25
|
-
|
|
26
|
-
if (!validationResult.valid) {
|
|
27
|
-
return validationResult;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
validatedConfig.endpoints.push(validationResult.endpoint);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
valid: true,
|
|
35
|
-
config: validatedConfig
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function validateEndpoint(endpoint, index) {
|
|
40
|
-
if (!endpoint || typeof endpoint !== 'object') {
|
|
41
|
-
return { valid: false, error: `Endpoint at index ${index} must be a valid object` };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!endpoint.path) {
|
|
45
|
-
return { valid: false, error: `Endpoint at index ${index} is missing required property: path` };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (typeof endpoint.path !== 'string') {
|
|
49
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid path: must be a string` };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!endpoint.path.startsWith('/')) {
|
|
53
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid path: must start with /` };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!endpoint.method) {
|
|
57
|
-
return { valid: false, error: `Endpoint at index ${index} is missing required property: method` };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (typeof endpoint.method !== 'string') {
|
|
61
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid method: must be a string` };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
65
|
-
const normalizedMethod = endpoint.method.toUpperCase();
|
|
66
|
-
|
|
67
|
-
if (!validMethods.includes(normalizedMethod)) {
|
|
68
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid method: ${endpoint.method}` };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!endpoint.data) {
|
|
72
|
-
return { valid: false, error: `Endpoint at index ${index} is missing required property: data` };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!Array.isArray(endpoint.data)) {
|
|
76
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid data: must be an array` };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (endpoint.data.length === 0) {
|
|
80
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid data: array cannot be empty` };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const validatedEndpoint = {
|
|
84
|
-
path: endpoint.path,
|
|
85
|
-
method: normalizedMethod,
|
|
86
|
-
data: endpoint.data,
|
|
87
|
-
delay: 0,
|
|
88
|
-
failureRate: 0
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
if (endpoint.delay !== undefined) {
|
|
92
|
-
if (typeof endpoint.delay !== 'number' || endpoint.delay < 0) {
|
|
93
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid delay: delay must be a non-negative number` };
|
|
94
|
-
}
|
|
95
|
-
validatedEndpoint.delay = endpoint.delay;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (endpoint.failureRate !== undefined) {
|
|
99
|
-
if (typeof endpoint.failureRate !== 'number' || endpoint.failureRate < 0 || endpoint.failureRate > 100) {
|
|
100
|
-
return { valid: false, error: `Endpoint at index ${index} has invalid failureRate: must be between 0 and 100` };
|
|
101
|
-
}
|
|
102
|
-
validatedEndpoint.failureRate = endpoint.failureRate;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
valid: true,
|
|
107
|
-
endpoint: validatedEndpoint
|
|
108
|
-
};
|
|
109
|
-
}
|