replica-failover-mongodb-ts 2.0.5 → 2.2.0

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/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2025 João Ito
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/Readme.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Node Balancer
2
2
 
3
+ [![NPM Version](https://img.shields.io/npm/v/replica-failover-mongodb-ts?style=flat-square)](https://www.npmjs.com/package/replica-failover-mongodb-ts)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-20.x-green)](https://nodejs.org/)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)
7
+
8
+ ## 🚀 Quick Start (Dashboard CLI)
9
+
10
+ Se você quer apenas rodar o painel de controle visualmente para testar qualquer API:
11
+
12
+ ```bash
13
+ npm install -g replica-failover-mongodb-ts
14
+ node-balancer-dashboard
15
+ ```
16
+
17
+ ---
18
+
3
19
  ## Sobre o Projeto: "Node Balancer"
4
20
 
5
21
  O Node Balancer é uma API escalável construída utilizando Node.js, MongoDB com replica set para alta disponibilidade, e Nginx como balanceador de carga. O sistema foi projetado para garantir resiliência, escalabilidade e alta disponibilidade. A arquitetura permite a adição manual de instâncias backend (Node.js) e garante que, em caso de falhas, o sistema continue operando sem interrupções, com a replicação automática dos dados e balanceamento de carga eficiente.
@@ -11,11 +27,13 @@ O Node Balancer é uma API escalável construída utilizando Node.js, MongoDB co
11
27
  ## Sumário
12
28
 
13
29
  1. [Tecnologias](#tecnologias)
14
- 2. [Como Rodar o Projeto](#como-rodar-o-projeto)
15
- 3. [Visual Dashboard (Painel de Controle)](#visual-dashboard-painel-de-controle)
16
- 4. [Testes e Automação (Chaos Testing)](#testes-e-automação-chaos-testing)
17
- 5. [Documentação Detalhada](#documentação-detalhada)
18
- 6. [Configuração Manual (Referência)](#configuração-manual-referência)
30
+ 2. [Como Rodar o Projeto (Dev)](#como-rodar-o-projeto-dev)
31
+ 3. [Uso como Biblioteca (Library)](#uso-como-biblioteca-library)
32
+ 4. [Visual Dashboard (Painel de Controle)](#visual-dashboard-painel-de-controle)
33
+ 5. [Uso Avançado do Dashboard (CLI)](#uso-avançado-do-dashboard-cli)
34
+ 6. [Testes e Automação (Chaos Testing)](#testes-e-automação-chaos-testing)
35
+ 7. [Documentação Detalhada](#documentação-detalhada)
36
+ 8. [Configuração Manual (Referência)](#configuração-manual-referência)
19
37
 
20
38
  ---
21
39
 
@@ -31,7 +49,7 @@ O Node Balancer utiliza as seguintes tecnologias:
31
49
 
32
50
  ---
33
51
 
34
- ## Como Rodar o Projeto
52
+ ## Como Rodar o Projeto (Dev)
35
53
 
36
54
  ### Pré-requisitos
37
55
  - Docker e Docker Compose instalados.
@@ -61,6 +79,33 @@ O Node Balancer utiliza as seguintes tecnologias:
61
79
 
62
80
  ---
63
81
 
82
+ ## Uso como Biblioteca (Library)
83
+
84
+ Você pode usar o gerenciador de conexões resiliente deste projeto em sua própria aplicação Node.js.
85
+
86
+ 1. **Instale a lib:**
87
+ ```bash
88
+ npm install replica-failover-mongodb-ts
89
+ ```
90
+
91
+ 2. **Importe e use:**
92
+ ```typescript
93
+ import { ConnectionManager } from 'replica-failover-mongodb-ts';
94
+
95
+ const db = new ConnectionManager({
96
+ nodes: [
97
+ 'mongodb://mongo1:27017/mydb',
98
+ 'mongodb://mongo2:27017/mydb'
99
+ ],
100
+ healthCheckIntervalMs: 5000
101
+ });
102
+
103
+ await db.init();
104
+ const myCollection = db.getDb().collection('users');
105
+ ```
106
+
107
+ ---
108
+
64
109
  ## Visual Dashboard (Painel de Controle)
65
110
 
66
111
  Para uma experiência visual e interativa, utilize o nosso Dashboard via Terminal (TUI). Ele permite monitorar a topologia do cluster, gráficos de latência e controlar os nós (Stop/Start) manualmente.
@@ -106,6 +151,35 @@ Ao realizar testes de carga ou criar usuários via dashboard, a API retornará r
106
151
 
107
152
  ---
108
153
 
154
+ ## Uso Avançado do Dashboard (CLI)
155
+
156
+ O dashboard pode ser configurado para monitorar **qualquer API** e **qualquer cluster MongoDB**, não apenas o deste projeto.
157
+
158
+ ```bash
159
+ node-balancer-dashboard [opções]
160
+ ```
161
+
162
+ ### Opções Disponíveis
163
+
164
+ | Flag | Descrição | Padrão |
165
+ | :--- | :--- | :--- |
166
+ | `--api-url` | URL da API para testar latência/requests | `http://localhost:3000/api/users` |
167
+ | `--nodes` | Lista de URIs do MongoDB (separados por vírgula) | `mongodb://localhost:27017...` |
168
+ | `--no-docker` | Desabilita controles do Docker (para clusters remotos) | `false` |
169
+
170
+ ### Exemplo Real
171
+
172
+ Testando uma API de produção sem acesso ao Docker local:
173
+
174
+ ```bash
175
+ node-balancer-dashboard \
176
+ --api-url https://api.minhaempresa.com/health \
177
+ --nodes mongodb://mongo-prod-1:27017,mongodb://mongo-prod-2:27017 \
178
+ --no-docker
179
+ ```
180
+
181
+ ---
182
+
109
183
  ## Testes e Automação (Chaos Testing)
110
184
 
111
185
  Se preferir rodar apenas o script de teste sem o dashboard visual:
@@ -185,3 +259,18 @@ Se você está utilizando o **MongoDB replica set**, certifique-se de que o repl
185
259
  ```javascript
186
260
  rs.status();
187
261
  ```
262
+
263
+ ---
264
+
265
+ ## Autor
266
+
267
+ | [<img src="https://github.com/JoaoIto.png" width="100px;" alt="João Ito"/>](https://github.com/JoaoIto) |
268
+ | :---: |
269
+ | **João Ito** |
270
+ | [GitHub](https://github.com/JoaoIto) • [Email](mailto:joaovictorpfr@gmail.com) |
271
+
272
+ Feito com ❤️ por João Ito. Entre em contato!
273
+
274
+ ## Licença
275
+
276
+ Este projeto está licenciado sob a licença ISC - veja o arquivo [LICENSE](LICENSE) para detalhes.
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionManager = void 0;
4
+ var connectionManager_1 = require("./config/connectionManager");
5
+ Object.defineProperty(exports, "ConnectionManager", { enumerable: true, get: function () { return connectionManager_1.ConnectionManager; } });
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
4
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -17,13 +18,21 @@ const blessed_contrib_1 = __importDefault(require("blessed-contrib"));
17
18
  const mongodb_1 = require("mongodb");
18
19
  const http_1 = __importDefault(require("http"));
19
20
  const child_process_1 = require("child_process");
20
- // Configuration
21
- const API_URL = 'http://localhost:3000/api/users';
22
- const NODES = [
23
- { name: 'mongo1', uri: 'mongodb://localhost:27017/node-balancer' },
24
- { name: 'mongo2', uri: 'mongodb://localhost:27018/node-balancer' },
25
- { name: 'mongo3', uri: 'mongodb://localhost:27019/node-balancer' }
26
- ];
21
+ // Parse Args
22
+ const args = process.argv.slice(2);
23
+ function getArg(flag, def) {
24
+ const idx = args.indexOf(flag);
25
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : def;
26
+ }
27
+ const API_URL = getArg('--api-url', 'http://localhost:3000/api/users');
28
+ const NODES_ARG = getArg('--nodes', 'mongodb://localhost:27017/node-balancer,mongodb://localhost:27018/node-balancer,mongodb://localhost:27019/node-balancer');
29
+ const DOCKER_CONTAINERS_ARG = getArg('--docker-containers', 'mongo1,mongo2,mongo3');
30
+ const NO_DOCKER = args.includes('--no-docker');
31
+ const NODES = NODES_ARG.split(',').map((uri, i) => ({
32
+ name: `node${i + 1}`,
33
+ uri: uri.trim()
34
+ }));
35
+ const DOCKER_NODES = DOCKER_CONTAINERS_ARG.split(',').map(n => n.trim());
27
36
  // Screen Setup
28
37
  const screen = blessed_1.default.screen({
29
38
  smartCSR: true,
@@ -64,11 +73,11 @@ const controls = grid.set(6, 8, 6, 4, blessed_1.default.list, {
64
73
  items: [
65
74
  'RUN CHAOS DEMO (Auto)',
66
75
  'SEND BATCH (2 POST + 1 GET)',
67
- 'STOP PRIMARY',
68
- 'START MONGO1',
69
- 'START MONGO2',
70
- 'START MONGO3',
71
- 'START STACK',
76
+ ...(NO_DOCKER ? [] : [
77
+ 'STOP PRIMARY',
78
+ ...DOCKER_NODES.map(n => `START ${n.toUpperCase()}`),
79
+ 'START STACK'
80
+ ]),
72
81
  'EXIT'
73
82
  ]
74
83
  });
@@ -155,7 +164,7 @@ function updateTopology() {
155
164
  try {
156
165
  const client = new mongodb_1.MongoClient(node.uri, { serverSelectionTimeoutMS: 500, directConnection: true });
157
166
  yield client.connect();
158
- const db = client.db('node-balancer');
167
+ const db = client.db('node-balancer'); // Assuming DB name is consistent or part of URI, but here hardcoded for now or could be arg
159
168
  const hello = yield db.command({ hello: 1 });
160
169
  count = (yield db.collection('users').countDocuments()).toString();
161
170
  yield client.close();
@@ -186,34 +195,43 @@ function runChaosDemo() {
186
195
  // Phase 1
187
196
  log('Phase 1: Healthy State');
188
197
  yield runBatch();
189
- // Chaos
190
- const primary = yield getPrimary();
191
- if (primary) {
192
- log(`💥 Stopping PRIMARY: ${primary}`);
193
- try {
194
- (0, child_process_1.execSync)(`docker stop ${primary}`);
195
- }
196
- catch (e) {
197
- log('Error stopping node');
198
- }
198
+ if (NO_DOCKER) {
199
+ log('Skipping Chaos (No Docker Mode)');
199
200
  }
200
201
  else {
201
- log('No Primary found to stop!');
202
- }
203
- // Phase 2
204
- log('Phase 2: Failover State');
205
- yield sleep(2000);
206
- yield runBatch();
207
- // Recovery
208
- log('Waiting 5s before recovery...');
209
- yield sleep(5000);
210
- if (primary) {
211
- log(`♻️ Restarting ${primary}`);
212
- try {
213
- (0, child_process_1.execSync)(`docker start ${primary}`);
202
+ // Chaos
203
+ const primaryName = yield getPrimary(); // returns node1, node2...
204
+ // We need to map node name to container name if they differ, but here we assume index matching or we need smarter logic
205
+ // For simplicity in this generic version, let's try to find the container name based on index
206
+ const primaryIndex = NODES.findIndex(n => n.name === primaryName);
207
+ const containerName = primaryIndex !== -1 ? DOCKER_NODES[primaryIndex] : null;
208
+ if (containerName) {
209
+ log(`💥 Stopping PRIMARY: ${containerName}`);
210
+ try {
211
+ (0, child_process_1.execSync)(`docker stop ${containerName}`);
212
+ }
213
+ catch (e) {
214
+ log('Error stopping node');
215
+ }
214
216
  }
215
- catch (e) {
216
- log('Error starting node');
217
+ else {
218
+ log('No Primary found to stop!');
219
+ }
220
+ // Phase 2
221
+ log('Phase 2: Failover State');
222
+ yield sleep(2000);
223
+ yield runBatch();
224
+ // Recovery
225
+ log('Waiting 5s before recovery...');
226
+ yield sleep(5000);
227
+ if (containerName) {
228
+ log(`♻️ Restarting ${containerName}`);
229
+ try {
230
+ (0, child_process_1.execSync)(`docker start ${containerName}`);
231
+ }
232
+ catch (e) {
233
+ log('Error starting node');
234
+ }
217
235
  }
218
236
  }
219
237
  // Phase 3
@@ -233,11 +251,15 @@ controls.on('select', (item, index) => __awaiter(void 0, void 0, void 0, functio
233
251
  runBatch();
234
252
  }
235
253
  else if (cmd.includes('STOP PRIMARY')) {
236
- const p = yield getPrimary();
237
- if (p) {
238
- log(`Stopping ${p}...`);
254
+ if (NO_DOCKER)
255
+ return log('Docker disabled.');
256
+ const pName = yield getPrimary();
257
+ const pIdx = NODES.findIndex(n => n.name === pName);
258
+ const container = pIdx !== -1 ? DOCKER_NODES[pIdx] : null;
259
+ if (container) {
260
+ log(`Stopping ${container}...`);
239
261
  try {
240
- (0, child_process_1.execSync)(`docker stop ${p}`);
262
+ (0, child_process_1.execSync)(`docker stop ${container}`);
241
263
  log('Stopped.');
242
264
  }
243
265
  catch (e) {
@@ -248,11 +270,15 @@ controls.on('select', (item, index) => __awaiter(void 0, void 0, void 0, functio
248
270
  log('No Primary found.');
249
271
  }
250
272
  }
251
- else if (cmd.includes('START MONGO')) {
252
- const node = cmd.split(' ')[1].toLowerCase();
253
- log(`Starting ${node}...`);
273
+ else if (cmd.includes('START MONGO') || cmd.includes('START NODE')) { // Generic match
274
+ if (NO_DOCKER)
275
+ return log('Docker disabled.');
276
+ // Extract container name from string "START MONGO1"
277
+ const parts = cmd.split(' ');
278
+ const container = parts[1].toLowerCase(); // mongo1
279
+ log(`Starting ${container}...`);
254
280
  try {
255
- (0, child_process_1.execSync)(`docker start ${node}`);
281
+ (0, child_process_1.execSync)(`docker start ${container}`);
256
282
  log('Started.');
257
283
  }
258
284
  catch (e) {
@@ -260,6 +286,8 @@ controls.on('select', (item, index) => __awaiter(void 0, void 0, void 0, functio
260
286
  }
261
287
  }
262
288
  else if (cmd.includes('START STACK')) {
289
+ if (NO_DOCKER)
290
+ return log('Docker disabled.');
263
291
  log('Starting stack...');
264
292
  try {
265
293
  (0, child_process_1.execSync)('docker-compose up -d');
@@ -278,6 +306,9 @@ setInterval(updateTopology, 2000);
278
306
  // Init
279
307
  controls.focus();
280
308
  log('Control Center Ready.');
309
+ if (NO_DOCKER)
310
+ log('Docker Control: DISABLED');
311
+ log(`API: ${API_URL}`);
281
312
  updateTopology();
282
313
  screen.render();
283
314
  // Exit
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "replica-failover-mongodb-ts",
3
- "version": "2.0.5",
3
+ "version": "2.2.0",
4
4
  "description": "O sistema replica um dos conceitos básicos da arquitetura de sistemas distribuído, na qual ele tem uma API em express.js com TypeScript, e usando o mongoDB com replicaSET e failover, consegue automáticamente garantir disponibilidade e redundância de dados das requisições.",
5
- "main": "dist/server.js",
6
- "types": "dist/server.d.ts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "dev": "nodemon --exec ts-node src/server.ts",
9
9
  "build": "tsc",