rosinterface 1.0.1 → 1.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/README.md +517 -37
- package/dist/cli/SchemaInferrer.d.ts +4 -0
- package/dist/cli/SchemaInferrer.js +42 -0
- package/dist/cli/SchemaInferrer.js.map +1 -0
- package/dist/cli/generate.d.ts +1 -0
- package/dist/cli/generate.js +139 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/client/CommandBuilder.js +19 -3
- package/dist/client/CommandBuilder.js.map +1 -1
- package/dist/client/MikrotikClient.d.ts +11 -1
- package/dist/client/MikrotikClient.js +47 -5
- package/dist/client/MikrotikClient.js.map +1 -1
- package/dist/client/MikrotikTransaction.d.ts +12 -0
- package/dist/client/MikrotikTransaction.js +52 -0
- package/dist/client/MikrotikTransaction.js.map +1 -0
- package/dist/client/SnapshotSubscription.d.ts +35 -0
- package/dist/client/SnapshotSubscription.js +143 -0
- package/dist/client/SnapshotSubscription.js.map +1 -0
- package/dist/core/SocketClient.d.ts +2 -1
- package/dist/core/SocketClient.js +12 -8
- package/dist/core/SocketClient.js.map +1 -1
- package/dist/features/LiveCollection.d.ts +18 -0
- package/dist/features/LiveCollection.js +98 -0
- package/dist/features/LiveCollection.js.map +1 -0
- package/dist/features/PrometheusExporter.js +1 -1
- package/dist/features/PrometheusExporter.js.map +1 -1
- package/dist/generated/ISecret.d.ts +14 -0
- package/dist/generated/ISecret.js +3 -0
- package/dist/generated/ISecret.js.map +1 -0
- package/dist/test_library.d.ts +1 -0
- package/dist/test_library.js +34 -0
- package/dist/test_library.js.map +1 -0
- package/dist/utils/MikrotikCollection.d.ts +7 -2
- package/dist/utils/MikrotikCollection.js +37 -6
- package/dist/utils/MikrotikCollection.js.map +1 -1
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
# RosInterface
|
|
1
|
+
# RosInterface v1.2
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
3
|
+
> **High-Performance RouterOS Automation A modern TypeScript and JavasScript Library for MikroTik interactions.**
|
|
4
|
+
> Built with Reactive Streaming (onSnapshot), Circuit Breakers for fault tolerance, and an Offline Queue system.
|
|
5
|
+
> Transform raw RouterOS commands into synchronized, type-safe application state.
|
|
6
|
+
|
|
7
|
+
> **Automatización de Alto Rendimiento para RouterOS Una librería moderna en TypeScript y JavaScript para interactuar con MikroTik.**
|
|
8
|
+
> Diseñada con Streaming Reactivo (onSnapshot), Circuit Breakers para tolerancia a fallos y un sistema de Cola Offline.
|
|
9
|
+
> Transforma comandos crudos de RouterOS en un estado de aplicación sincronizado y con tipado seguro.
|
|
5
10
|
|
|
6
11
|
[](https://www.npmjs.com/package/rosinterface)
|
|
7
12
|
[](https://opensource.org/licenses/MIT)
|
|
8
13
|
|
|
9
14
|
---
|
|
10
15
|
|
|
16
|
+
|
|
11
17
|
## English Documentation
|
|
12
18
|
|
|
13
19
|
**RosInterface** is designed for **ISPs and mission-critical environments**.
|
|
@@ -17,21 +23,45 @@ It focuses on **connection stability**, **router hardware protection**, and **ef
|
|
|
17
23
|
|
|
18
24
|
### ✨ Key Features
|
|
19
25
|
|
|
20
|
-
-
|
|
26
|
+
- **Hardware Protection**
|
|
21
27
|
Built-in **Rate Limiter** with intelligent backoff to prevent RouterOS CPU saturation.
|
|
22
28
|
|
|
23
|
-
-
|
|
29
|
+
- **Offline-First Architecture**
|
|
24
30
|
Commands marked with `.persistent()` are automatically queued if the router is offline and synchronized once connectivity is restored.
|
|
25
31
|
|
|
26
|
-
-
|
|
32
|
+
- **Smart Caching**
|
|
27
33
|
Read operations are cached for **5 seconds (TTL)** to reduce redundant API calls and router load.
|
|
28
34
|
|
|
29
|
-
-
|
|
35
|
+
- **Source-Side Filtering**
|
|
30
36
|
Use `.findBy()` and `.findOne()` to filter data **directly on the router**, minimizing bandwidth and latency.
|
|
31
37
|
|
|
32
|
-
-
|
|
38
|
+
- **On-time changes**
|
|
39
|
+
Use `.collection()` and `.onSnapshot()` to get changes on real time **directly the router**, for to get new data without call to many times to your router.
|
|
40
|
+
|
|
41
|
+
- **Fault Tolerance (Circuit Breaker)**
|
|
42
|
+
|
|
43
|
+
Implements the `Circuit Breaker` pattern. If the router stops responding or encounters critical errors, the library temporarily "cuts the circuit" to prevent the accumulation of failed requests and protect the stability of your Node.js server.
|
|
44
|
+
|
|
45
|
+
- **Native Observability**
|
|
46
|
+
|
|
47
|
+
Built-in `.getMetrics()` method. Exports traffic statistics and router status directly in a format compatible with Prometheus and Grafana without the need for external plugins.
|
|
48
|
+
|
|
49
|
+
- **Transport Security**
|
|
50
|
+
|
|
51
|
+
Full support for **TLS/SSL (SSL API)**, allowing secure encrypted connections over port 8729 (recommended to change this), ideal for managing routers over the public internet.
|
|
52
|
+
|
|
53
|
+
- **Fluent API**
|
|
33
54
|
Chainable, expressive syntax for clean and maintainable automation code.
|
|
34
55
|
|
|
56
|
+
- **CLI Code Generator**
|
|
57
|
+
Auto-generate TypeScript interfaces from your live router. No more guessing field names.
|
|
58
|
+
|
|
59
|
+
- **Differential Updates (onDiff)**
|
|
60
|
+
Receive atomic added, modified, and removed events instead of full arrays. Optimized for high-performance UIs.
|
|
61
|
+
|
|
62
|
+
- **Intelligent Throttling**
|
|
63
|
+
Control the flow of real-time data with "Leading + Trailing Edge" strategies.
|
|
64
|
+
|
|
35
65
|
---
|
|
36
66
|
|
|
37
67
|
|
|
@@ -39,7 +69,7 @@ It focuses on **connection stability**, **router hardware protection**, and **ef
|
|
|
39
69
|
|
|
40
70
|
### ✨ Important Considerations
|
|
41
71
|
|
|
42
|
-
####
|
|
72
|
+
#### Environment Variables (.env)
|
|
43
73
|
|
|
44
74
|
Create a `.env` file in the root directory of your project:
|
|
45
75
|
|
|
@@ -56,32 +86,41 @@ MIKROTIK_PORT=8729
|
|
|
56
86
|
>
|
|
57
87
|
> Use **only in controlled, local or test environments**.
|
|
58
88
|
|
|
89
|
+
|
|
90
|
+
>⚠️ **Performance Warning**
|
|
91
|
+
>
|
|
92
|
+
> The `onSnapshot` function allows you to receive **real-time** router changes in RosInterface.
|
|
93
|
+
> To avoid negative performance impacts on low-spec devices, it is recommended to use it with **Resilience** enabled.
|
|
94
|
+
>
|
|
95
|
+
> Use it **in moderation; excessive use of onSnapshot can negatively affect the performance of your devices**.
|
|
96
|
+
|
|
97
|
+
|
|
59
98
|
---
|
|
60
99
|
|
|
61
100
|
|
|
62
|
-
###
|
|
101
|
+
### Usage Examples
|
|
63
102
|
|
|
64
|
-
#### 1
|
|
103
|
+
#### 1. Basic Connection
|
|
65
104
|
|
|
66
105
|
```ts
|
|
67
106
|
import { MikrotikClient } from 'rosinterface';
|
|
68
107
|
|
|
69
108
|
const client = new MikrotikClient({
|
|
70
|
-
host: '
|
|
109
|
+
host: 'ROUTER_IP (LOCAL OR REMOTE)',
|
|
71
110
|
user: 'admin',
|
|
72
111
|
password: 'password',
|
|
73
|
-
|
|
112
|
+
port: 8729, // We recommended change the default port
|
|
113
|
+
useTLS: false, // Test only
|
|
74
114
|
rateLimit: 50,
|
|
75
|
-
allowInsecureConfig: true //
|
|
115
|
+
allowInsecureConfig: true // Never in production or release
|
|
76
116
|
});
|
|
77
117
|
|
|
78
118
|
await client.connect();
|
|
79
119
|
```
|
|
80
120
|
---
|
|
81
121
|
|
|
82
|
-
---
|
|
83
122
|
|
|
84
|
-
#### 2
|
|
123
|
+
#### 2. Recommended connection (Production/Release)
|
|
85
124
|
|
|
86
125
|
```ts
|
|
87
126
|
import { MikrotikClient } from 'rosinterface';
|
|
@@ -92,8 +131,8 @@ const client = new MikrotikClient({
|
|
|
92
131
|
user: process.env.MIKROTIK_USER,
|
|
93
132
|
password: process.env.MIKROTIK_PASSWORD,
|
|
94
133
|
port: Number(process.env.MIKROTIK_PORT) || 8729,
|
|
95
|
-
useTLS: true,
|
|
96
|
-
rateLimit: 50
|
|
134
|
+
useTLS: true, //Always use TLS between TLS v1.2 or TLS v1.3 deppend that your devices
|
|
135
|
+
rateLimit: 50 //Here you adjust the number of requests per second you want it to support (Anti DDoS)
|
|
97
136
|
});
|
|
98
137
|
|
|
99
138
|
await client.connect();
|
|
@@ -103,7 +142,7 @@ await client.connect();
|
|
|
103
142
|
|
|
104
143
|
|
|
105
144
|
|
|
106
|
-
####
|
|
145
|
+
#### 3. Optimized Read (select + findOne)
|
|
107
146
|
|
|
108
147
|
```ts
|
|
109
148
|
const user = await client.command('/ppp/secret')
|
|
@@ -115,7 +154,7 @@ console.log(`User: ${user?.name}, Profile: ${user?.profile}`);
|
|
|
115
154
|
|
|
116
155
|
---
|
|
117
156
|
|
|
118
|
-
####
|
|
157
|
+
#### 4. Resilience with Persistence (Offline-Safe)
|
|
119
158
|
|
|
120
159
|
```ts
|
|
121
160
|
const result = await client.command('/ppp/secret')
|
|
@@ -131,9 +170,204 @@ if (result === 'QUEUED_OFFLINE') {
|
|
|
131
170
|
}
|
|
132
171
|
```
|
|
133
172
|
|
|
173
|
+
|
|
174
|
+
#### 5. Time-Limited Listener
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
// Start the subscription and store the cleanup function (unsubscribe)
|
|
178
|
+
const unsubscribe = client.collection('/ppp/secret')
|
|
179
|
+
.onSnapshot((secrets) => {
|
|
180
|
+
console.log(`Update received. Total users: ${secrets.length}`);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Set a timer to stop listening
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
console.log('Time up. Stopping subscription...');
|
|
186
|
+
|
|
187
|
+
// Stops the Router stream and clears local memory
|
|
188
|
+
unsubscribe();
|
|
189
|
+
|
|
190
|
+
// Optional: Close the connection if no further tasks are needed
|
|
191
|
+
client.close();
|
|
192
|
+
}, 60000); // 60 seconds
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
#### 6. Persistent Listener (Always On)
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
// The client automatically handles Keep-Alive pings (every 10s)
|
|
200
|
+
// to prevent idle disconnects. No need to call 'unsubscribe'.
|
|
201
|
+
|
|
202
|
+
client.collection('/interface')
|
|
203
|
+
.where('disabled', false) // Optional: Only active interfaces
|
|
204
|
+
.onSnapshot((interfaces) => {
|
|
205
|
+
// This callback will execute infinitely whenever a physical change
|
|
206
|
+
// occurs on the router (cable unplugged, traffic state, rename, etc.)
|
|
207
|
+
const downInterfaces = interfaces.filter(i => i.running === 'false');
|
|
208
|
+
|
|
209
|
+
if (downInterfaces.length > 0) {
|
|
210
|
+
console.alert(`ALERT: ${downInterfaces.length} interfaces are down.`);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Important: Handle network errors to reconnect if power or internet is lost
|
|
215
|
+
client.on('close', () => {
|
|
216
|
+
console.log('Connection lost. Restarting service in 5s...');
|
|
217
|
+
setTimeout(() => connectAndListen(), 5000);
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
#### 7. Prometheus Metric
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
// 1. Define the map: Mikrotik Field -> Prometheus Metric
|
|
227
|
+
const metricsDef = [
|
|
228
|
+
{
|
|
229
|
+
name: 'mikrotik_iface_rx_total', // Final name in Prometheus
|
|
230
|
+
help: 'Bytes received', // Description
|
|
231
|
+
type: 'counter', // Type: 'counter' or 'gauge'
|
|
232
|
+
path: 'rx-byte', // Original field in RouterOS
|
|
233
|
+
labels: ['name', 'type'] // Labels for filtering (e.g., wan, bridge)
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'mikrotik_iface_tx_total',
|
|
237
|
+
help: 'Bytes transmitted',
|
|
238
|
+
type: 'counter',
|
|
239
|
+
path: 'tx-byte',
|
|
240
|
+
labels: ['name', 'type']
|
|
241
|
+
}
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
// 2. Get the formatted plain text ready for Prometheus
|
|
245
|
+
// (Internally executes /interface/print and transforms the data)
|
|
246
|
+
const result = await client.getMetrics('/interface', metricsDef);
|
|
247
|
+
|
|
248
|
+
console.log(result);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
#### 8. Type-Safe Realtime Stream (onDiff)
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
client.collection('/ppp/secret')
|
|
257
|
+
.onSnapshot((data) => {
|
|
258
|
+
// Check if we are in Diff Mode
|
|
259
|
+
if ('added' in data) {
|
|
260
|
+
// TypeScript automatically infers that 'data' is a SnapshotDiff!
|
|
261
|
+
|
|
262
|
+
// Added items
|
|
263
|
+
data.added.forEach(secret => console.log('New:', secret.name));
|
|
264
|
+
|
|
265
|
+
// Modified items
|
|
266
|
+
data.modified.forEach(secret => console.log('Modified:', secret.name));
|
|
267
|
+
|
|
268
|
+
// Removed items
|
|
269
|
+
data.removed.forEach(secret => console.log('Removed:', secret.name));
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// If execution reaches here, .onDiff() was NOT used, so 'data' is a standard Array
|
|
273
|
+
console.log('Full list size:', data.length);
|
|
274
|
+
}
|
|
275
|
+
}).onDiff();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
#### 9. Data Transformers With Print
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
// EXAMPLE 1: Using .toMap() for O(1) Lookup
|
|
283
|
+
// Scenario: Quickly finding a specific user by name without looping through an array.
|
|
284
|
+
client.command('/ppp/secret').print().then(result => {
|
|
285
|
+
// Transform the array into a Dictionary (Map) indexed by 'name'
|
|
286
|
+
const usersMap = result.toMap('name');
|
|
287
|
+
|
|
288
|
+
// Instant access! No .find() required.
|
|
289
|
+
const john = usersMap['john_doe'];
|
|
290
|
+
if (john) {
|
|
291
|
+
console.log(`User Found: ${john.profile}`);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// EXAMPLE 2: Using .toGrouped() for Reporting
|
|
296
|
+
// Scenario: Grouping active connections by their service profile (e.g. 10mb, 50mb plans).
|
|
297
|
+
client.command('/ppp/active').print().then(result => {
|
|
298
|
+
// Group users by the 'profile' property
|
|
299
|
+
const byProfile = result.toGrouped('profile');
|
|
300
|
+
|
|
301
|
+
console.log('--- Connected Users Summary ---');
|
|
302
|
+
Object.keys(byProfile).forEach(profile => {
|
|
303
|
+
console.log(`Plan ${profile}: ${byProfile[profile].length} users`);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// EXAMPLE 3: Using .toPages() for Pagination
|
|
308
|
+
// Scenario: Displaying a large list of logs in a frontend table, page by page.
|
|
309
|
+
const PAGE_SIZE = 50;
|
|
310
|
+
const CURRENT_PAGE = 1;
|
|
311
|
+
|
|
312
|
+
client.command('/log').print().then(result => {
|
|
313
|
+
// Get only the items for the first page
|
|
314
|
+
const pageData = result.toPages(CURRENT_PAGE, PAGE_SIZE);
|
|
315
|
+
|
|
316
|
+
console.log(`Showing ${pageData.length} logs (Page ${CURRENT_PAGE})`);
|
|
317
|
+
pageData.forEach(log => console.log(`[${log.time}] ${log.message}`));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
#### 10. CLI Codegen - Automatic Type Generation
|
|
324
|
+
|
|
325
|
+
> **OPTION A: Using .env file (Recommended for Security)**
|
|
326
|
+
> Create a .env file with MIKROTIK_HOST, MIKROTIK_USER, MIKROTIK_PASS
|
|
327
|
+
> Run the command:
|
|
328
|
+
> > npm run codegen -- -p /ppp/secret -n ISecret
|
|
329
|
+
>
|
|
330
|
+
> OPTION B: Manual Configuration (Quick Testing / CI)
|
|
331
|
+
> Pass connection details directly via flags. Use double quotes for passwords with special chars.
|
|
332
|
+
> >npm run codegen -- -p /ppp/secret -n ISecret --host ROUTER_IP --user admin --pass "secret123" --port 8728
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
// Usage A: Type-Safe Reading (Auto-complete for properties)
|
|
337
|
+
// Pass the interface <ISecret> to .collection() or .command()
|
|
338
|
+
client.collection<ISecret>('/ppp/secret').onSnapshot((diff) => {
|
|
339
|
+
if ('added' in diff) {
|
|
340
|
+
diff.added.forEach(secret => {
|
|
341
|
+
// TypeScript knows 'profile' and 'service' exist!
|
|
342
|
+
// If you type "secret.", your IDE will show all available fields.
|
|
343
|
+
console.log(`New User: ${secret.name} (Plan: ${secret.profile})`);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Usage B: Type-Safe Writing (Validation for .add)
|
|
349
|
+
async function createSecureUser() {
|
|
350
|
+
const api = client.command<ISecret>('/ppp/secret');
|
|
351
|
+
|
|
352
|
+
// TypeScript ensures you only pass valid fields defined in ISecret.
|
|
353
|
+
// It prevents typos like 'passwrod' or using invalid profile names if unions were generated.
|
|
354
|
+
await api.add({
|
|
355
|
+
name: 'vip_user',
|
|
356
|
+
password: 'secure_password',
|
|
357
|
+
profile: 'default',
|
|
358
|
+
service: 'pppoe',
|
|
359
|
+
disabled: false
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
134
368
|
---
|
|
135
369
|
|
|
136
|
-
##
|
|
370
|
+
## Documentación en Español
|
|
137
371
|
|
|
138
372
|
**RosInterface** está diseñado para **ISPs y entornos críticos**.
|
|
139
373
|
Su objetivo es garantizar **estabilidad de conexión**, **protección del hardware MikroTik** y **consultas eficientes**, incluso con enlaces inestables.
|
|
@@ -142,26 +376,50 @@ Su objetivo es garantizar **estabilidad de conexión**, **protección del hardwa
|
|
|
142
376
|
|
|
143
377
|
### ✨ Características Principales
|
|
144
378
|
|
|
145
|
-
-
|
|
379
|
+
- **Protección del Hardware**
|
|
146
380
|
Limitador de tasa integrado con retroceso inteligente para evitar saturación de CPU.
|
|
147
381
|
|
|
148
|
-
-
|
|
382
|
+
- **Arquitectura Offline-First**
|
|
149
383
|
Los comandos con `.persistent()` se guardan en cola si el router no está disponible y se ejecutan automáticamente al reconectar.
|
|
150
384
|
|
|
151
|
-
-
|
|
385
|
+
- **Caché Inteligente**
|
|
152
386
|
Consultas con TTL de **5 segundos**, reduciendo llamadas innecesarias.
|
|
153
387
|
|
|
154
|
-
-
|
|
388
|
+
- **Filtrado en Origen**
|
|
155
389
|
`.findBy()` y `.findOne()` filtran directamente en RouterOS sin descargar tablas completas.
|
|
156
390
|
|
|
157
|
-
-
|
|
391
|
+
- **Cambios puntuales**
|
|
392
|
+
Usa `.collection()` y `.onSnapshot()` para obtener cambios en tiempo real **directamente desde el router**, para obtener datos nuevos, sin llamar a su router repetidamente.
|
|
393
|
+
|
|
394
|
+
- **Tolerancia a Fallos (Circuit Breaker)**
|
|
395
|
+
Implementa el patrón `Circuit Breaker` . Si el router deja de responder o da errores críticos, la librería "corta el circuito" temporalmente para evitar acumular peticiones fallidas y proteger la estabilidad de tu servidor Node.js.
|
|
396
|
+
|
|
397
|
+
- **Observabilidad Nativa**
|
|
398
|
+
Método `.getMetrics()`integrado. Exporta estadísticas de tráfico y estado del router directamente en formato compatible con Prometheus y Grafana sin necesidad de plugins externos.
|
|
399
|
+
|
|
400
|
+
- **Seguridad de Transporte**
|
|
401
|
+
Soporte completo para **TLS/SSL (API-SSL)**, permitiendo conexiones encriptadas seguras a través del puerto 8729 (Recomendado cambiar), ideal para administrar routers a través de internet pública.
|
|
402
|
+
|
|
403
|
+
- **API Fluida**
|
|
158
404
|
Sintaxis encadenable y clara.
|
|
159
405
|
|
|
406
|
+
- **Generador de código CLI**
|
|
407
|
+
|
|
408
|
+
Genere automáticamente interfaces TypeScript desde su enrutador en vivo. Olvídese de adivinar los nombres de los campos.
|
|
409
|
+
|
|
410
|
+
- **Actualizaciones diferenciales (onDiff)**
|
|
411
|
+
|
|
412
|
+
Reciba eventos atómicos de adición, modificación y eliminación en lugar de matrices completas. Optimizado para interfaces de usuario de alto rendimiento.
|
|
413
|
+
|
|
414
|
+
- **Limitación inteligente**
|
|
415
|
+
|
|
416
|
+
Controle el flujo de datos en tiempo real con estrategias de vanguardia y vanguardia.
|
|
417
|
+
|
|
160
418
|
---
|
|
161
419
|
|
|
162
420
|
### ✨ Consideraciones Importantes
|
|
163
421
|
|
|
164
|
-
####
|
|
422
|
+
#### Variables de Entorno (.env)
|
|
165
423
|
|
|
166
424
|
Crea un archivo `.env` en el directorio raíz de tu proyecto:
|
|
167
425
|
|
|
@@ -178,22 +436,32 @@ MIKROTIK_PORT=8729
|
|
|
178
436
|
>
|
|
179
437
|
> Úsala **solo en entornos controlados, locales o de prueba**.
|
|
180
438
|
|
|
439
|
+
|
|
440
|
+
> ⚠️ **Aviso de Rendimiento**
|
|
441
|
+
> La función `onSnapshot` permite recibir cambios en **tiempo real** del router en RosInterface.
|
|
442
|
+
> Para evitar el impacto negativo de rendimiento en equipos con pocas prestaciones, se recomienda usarlo con **Resiliencia**.
|
|
443
|
+
>
|
|
444
|
+
> Úsala **con moderación, el abuso de onSnapshot puede afectar el rendimiento de tus equipos**.
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
181
448
|
---
|
|
182
449
|
|
|
183
|
-
###
|
|
450
|
+
### Ejemplos de Uso
|
|
184
451
|
|
|
185
|
-
#### 1
|
|
452
|
+
#### 1. Conexión rápida (solo pruebas)
|
|
186
453
|
|
|
187
454
|
```ts
|
|
188
455
|
import { MikrotikClient } from 'rosinterface';
|
|
189
456
|
|
|
190
457
|
const client = new MikrotikClient({
|
|
191
|
-
host: 'IP_DEL_ROUTER',
|
|
458
|
+
host: 'IP_DEL_ROUTER (LOCAL O REMOTO)',
|
|
192
459
|
user: 'admin',
|
|
193
460
|
password: 'password',
|
|
194
|
-
|
|
461
|
+
port: 8729, // Se recomienda cambiar el puerto por defecto
|
|
462
|
+
useTLS: false, // Solo pruebas
|
|
195
463
|
rateLimit: 50,
|
|
196
|
-
allowInsecureConfig: true //
|
|
464
|
+
allowInsecureConfig: true // Nunca en producción
|
|
197
465
|
});
|
|
198
466
|
|
|
199
467
|
await client.connect();
|
|
@@ -201,7 +469,7 @@ await client.connect();
|
|
|
201
469
|
|
|
202
470
|
---
|
|
203
471
|
|
|
204
|
-
#### 2
|
|
472
|
+
#### 2. Conexión Recomendada (Producción)
|
|
205
473
|
|
|
206
474
|
```ts
|
|
207
475
|
import { MikrotikClient } from 'rosinterface';
|
|
@@ -212,8 +480,8 @@ const client = new MikrotikClient({
|
|
|
212
480
|
user: process.env.MIKROTIK_USER,
|
|
213
481
|
password: process.env.MIKROTIK_PASSWORD,
|
|
214
482
|
port: Number(process.env.MIKROTIK_PORT) || 8729,
|
|
215
|
-
useTLS: true,
|
|
216
|
-
rateLimit: 50
|
|
483
|
+
useTLS: true, //Utilice siempre TLS entre TLS v1.2 o TLS v1.3 dependiendo de sus dispositivos
|
|
484
|
+
rateLimit: 50 //Aqui ajustas la cantidad de peticiones por segundo que deseas que soporte (Anti DDoS)
|
|
217
485
|
});
|
|
218
486
|
|
|
219
487
|
await client.connect();
|
|
@@ -221,7 +489,7 @@ await client.connect();
|
|
|
221
489
|
|
|
222
490
|
---
|
|
223
491
|
|
|
224
|
-
#### 3
|
|
492
|
+
#### 3. Lectura Optimizada
|
|
225
493
|
|
|
226
494
|
```ts
|
|
227
495
|
const user = await client.command('/ppp/secret')
|
|
@@ -233,7 +501,7 @@ console.log(`User: ${user?.name}, Profile: ${user?.profile}`);
|
|
|
233
501
|
|
|
234
502
|
---
|
|
235
503
|
|
|
236
|
-
#### 4
|
|
504
|
+
#### 4. Persistencia Offline
|
|
237
505
|
|
|
238
506
|
```ts
|
|
239
507
|
const result = await client.command('/ppp/secret')
|
|
@@ -249,4 +517,216 @@ if (result === 'QUEUED_OFFLINE') {
|
|
|
249
517
|
}
|
|
250
518
|
```
|
|
251
519
|
|
|
520
|
+
|
|
521
|
+
#### 5. Escuchar por un Tiempo Determinado (Time-Limited)
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
// Iniciar la suscripción y guardar la función de limpieza (unsubscribe)
|
|
525
|
+
const unsubscribe = client.collection('/ppp/secret')
|
|
526
|
+
.onSnapshot((secrets) => {
|
|
527
|
+
console.log(`Actualización recibida. Total usuarios: ${secrets.length}`);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// Establecer un temporizador para detener la escucha
|
|
531
|
+
setTimeout(() => {
|
|
532
|
+
console.log('Tiempo finalizado. Cerrando suscripción...');
|
|
533
|
+
|
|
534
|
+
// Detiene el stream del Router y limpia la memoria local
|
|
535
|
+
unsubscribe();
|
|
536
|
+
|
|
537
|
+
// Opcional: Cerrar la conexión si ya no harás más tareas
|
|
538
|
+
client.close();
|
|
539
|
+
}, 60000); // 60 segundos
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
#### 6. Escucha Persistente (Always On)
|
|
545
|
+
|
|
546
|
+
```ts
|
|
547
|
+
// Configuración para mantener la conexión viva (Keep-Alive)
|
|
548
|
+
// El cliente enviará pings internos cada 10s para evitar desconexiones por inactividad.
|
|
549
|
+
// No es necesario llamar a 'unsubscribe' ni poner timeouts.
|
|
550
|
+
|
|
551
|
+
client.collection('/interface')
|
|
552
|
+
.where('disabled', false) // Opcional: Solo interfaces activas
|
|
553
|
+
.onSnapshot((interfaces) => {
|
|
554
|
+
// Este callback se ejecutará infinitamente cada vez que haya un cambio
|
|
555
|
+
// físico en el router (cable desconectado, tráfico, cambio de nombre, etc.)
|
|
556
|
+
const downInterfaces = interfaces.filter(i => i.running === 'false');
|
|
557
|
+
|
|
558
|
+
if (downInterfaces.length > 0) {
|
|
559
|
+
console.alert(`ALERTA: ${downInterfaces.length} interfaces caídas.`);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// Importante: Manejar errores de red para reconectar si se va la luz o internet
|
|
564
|
+
client.on('close', () => {
|
|
565
|
+
console.log('Conexión perdida. Reiniciando servicio en 5s...');
|
|
566
|
+
setTimeout(() => connectAndListen(), 5000);
|
|
567
|
+
});
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
#### 7. Métricas Prometheus
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
// 1. Define el mapeo: Campo Mikrotik -> Métrica Prometheus
|
|
575
|
+
const metricasDef = [
|
|
576
|
+
{
|
|
577
|
+
name: 'mikrotik_iface_rx_total', // Nombre final en Prometheus
|
|
578
|
+
help: 'Bytes recibidos', // Descripción
|
|
579
|
+
type: 'counter', // Tipo: 'counter' o 'gauge'
|
|
580
|
+
path: 'rx-byte', // Campo original en RouterOS
|
|
581
|
+
labels: ['name', 'type'] // Etiquetas para filtrar (ej: wan, bridge)
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
name: 'mikrotik_iface_tx_total',
|
|
585
|
+
help: 'Bytes transmitidos',
|
|
586
|
+
type: 'counter',
|
|
587
|
+
path: 'tx-byte',
|
|
588
|
+
labels: ['name', 'type']
|
|
589
|
+
}
|
|
590
|
+
];
|
|
591
|
+
|
|
592
|
+
// 2. Obtén el texto formateado listo para Prometheus
|
|
593
|
+
// (Internamente ejecuta /interface/print y transforma los datos)
|
|
594
|
+
const resultado = await client.getMetrics('/interface', metricasDef);
|
|
595
|
+
|
|
596
|
+
console.log(resultado);
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
#### 8. Transmisión en tiempo real con seguridad de tipos (onDiff)
|
|
600
|
+
|
|
601
|
+
```ts
|
|
602
|
+
client.collection('/ppp/secret') // Sin <T>, se asume Record<string, any>
|
|
603
|
+
.onSnapshot((data) => {
|
|
604
|
+
// Verificar si estamos en modo Diff
|
|
605
|
+
if ('added' in data) {
|
|
606
|
+
// TypeScript infiere automáticamente que 'data' es un SnapshotDiff
|
|
607
|
+
|
|
608
|
+
// Elementos Agregados
|
|
609
|
+
data.added.forEach(secret => console.log('Nuevo:', secret.name));
|
|
610
|
+
|
|
611
|
+
// Elementos Modificados
|
|
612
|
+
data.modified.forEach(secret => console.log('Editado:', secret.name));
|
|
613
|
+
|
|
614
|
+
// Elementos Eliminados
|
|
615
|
+
data.removed.forEach(secret => console.log('Borrado:', secret.name));
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
// Si entra aquí, es que NO se usó .onDiff() y es un Array normal
|
|
619
|
+
console.log('Tamaño lista completa:', data.length);
|
|
620
|
+
}
|
|
621
|
+
})
|
|
622
|
+
.onDiff(); // <--- IMPORTANTE: Activa el modo diferencial
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
#### 9. Tranformadores de tipo de datos con Print
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
// EJEMPLO 1: Usando .toMap() para Búsqueda Instantánea O(1)
|
|
630
|
+
// Escenario: Encontrar rápidamente un usuario específico por nombre sin recorrer un array.
|
|
631
|
+
client.command('/ppp/secret').print().then(result => {
|
|
632
|
+
// Transformar el array en un Diccionario (Map) indexado por 'name'
|
|
633
|
+
const usersMap = result.toMap('name');
|
|
634
|
+
|
|
635
|
+
// ¡Acceso instantáneo! No se requiere .find().
|
|
636
|
+
const john = usersMap['john_doe'];
|
|
637
|
+
if (john) {
|
|
638
|
+
console.log(`Usuario Encontrado: ${john.profile}`);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
// EJEMPLO 2: Usando .toGrouped() para Reportes
|
|
644
|
+
// Escenario: Agrupar conexiones activas por su perfil de servicio (ej. planes de 10mb, 50mb).
|
|
645
|
+
client.command('/ppp/active').print().then(result => {
|
|
646
|
+
// Agrupar usuarios por la propiedad 'profile'
|
|
647
|
+
const byProfile = result.toGrouped('profile');
|
|
648
|
+
|
|
649
|
+
console.log('--- Resumen de Usuarios Conectados ---');
|
|
650
|
+
Object.keys(byProfile).forEach(profile => {
|
|
651
|
+
console.log(`Plan ${profile}: ${byProfile[profile].length} usuarios`);
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
// EJEMPLO 3: Usando .toPages() para Paginación
|
|
657
|
+
// Escenario: Mostrar una lista grande de logs en una tabla frontend, página por página.
|
|
658
|
+
const PAGE_SIZE = 50;
|
|
659
|
+
const CURRENT_PAGE = 1;
|
|
660
|
+
|
|
661
|
+
client.command('/log').print().then(result => {
|
|
662
|
+
// Obtener solo los elementos para la primera página
|
|
663
|
+
const pageData = result.toPages(CURRENT_PAGE, PAGE_SIZE);
|
|
664
|
+
|
|
665
|
+
console.log(`Mostrando ${pageData.length} logs (Página ${CURRENT_PAGE})`);
|
|
666
|
+
pageData.forEach(log => console.log(`[${log.time}] ${log.message}`));
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
#### 10. CLI Codegen - Generación Automática de Tipos
|
|
673
|
+
|
|
674
|
+
> **OPCIÓN A: Usando archivo .env (Recomendado por Seguridad)**
|
|
675
|
+
> Crea un archivo .env con MIKROTIK_HOST, MIKROTIK_USER, MIKROTIK_PASS
|
|
676
|
+
> Ejecuta el comando:
|
|
677
|
+
> > npm run codegen -- -p /ppp/secret -n ISecret
|
|
678
|
+
>
|
|
679
|
+
> OPCIÓN B: Configuración Manual (Pruebas Rápidas / CI)
|
|
680
|
+
> PPasa los detalles de conexión directamente mediante banderas. Usa comillas dobles para contraseñas con caracteres especiales.
|
|
681
|
+
> >npm run codegen -- -p /ppp/secret -n ISecret --host ROUTER_IP --user admin --pass "secret123" --port 8728
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
// Uso A: Lectura con Tipado Seguro (Auto-completado para propiedades)
|
|
686
|
+
// Pasar la interfaz <ISecret> a .collection() o .command()
|
|
687
|
+
client.collection<ISecret>('/ppp/secret').onSnapshot((diff) => {
|
|
688
|
+
if ('added' in diff) {
|
|
689
|
+
diff.added.forEach(secret => {
|
|
690
|
+
// ¡TypeScript sabe que 'profile' y 'service' existen!
|
|
691
|
+
// Si escribes "secret.", tu IDE mostrará todos los campos disponibles.
|
|
692
|
+
console.log(`Nuevo Usuario: ${secret.name} (Plan: ${secret.profile})`);
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// Uso B: Escritura con Tipado Seguro (Validación para .add)
|
|
698
|
+
async function createSecureUser() {
|
|
699
|
+
const api = client.command<ISecret>('/ppp/secret');
|
|
700
|
+
|
|
701
|
+
// TypeScript asegura que solo pases campos válidos definidos en ISecret.
|
|
702
|
+
// Previene errores tipográficos como 'passwrod' o usar nombres de perfil inválidos si se generaron uniones.
|
|
703
|
+
await api.add({
|
|
704
|
+
name: 'vip_user',
|
|
705
|
+
password: 'secure_password',
|
|
706
|
+
profile: 'default',
|
|
707
|
+
service: 'pppoe',
|
|
708
|
+
disabled: false
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Author's Note / Nota del Autor
|
|
718
|
+
|
|
719
|
+
**English**
|
|
720
|
+
>Thank you for choosing RosInterface for your automation needs. I built this library to solve the real-world challenges of managing MikroTik networks at scale. I hope it helps you build your systems.
|
|
721
|
+
|
|
722
|
+
Happy Coding!
|
|
723
|
+
— **McBlockier**
|
|
724
|
+
|
|
725
|
+
**Español**
|
|
726
|
+
> Gracias por elegir **RosInterface** para tus necesidades de automatización. Construí esta librería para resolver los desafíos reales de administrar redes MikroTik a escala. Espero te ayude mucho a construir tus sistemas .
|
|
727
|
+
|
|
728
|
+
¡Feliz Código!
|
|
729
|
+
— **McBlockier**
|
|
730
|
+
|
|
731
|
+
|
|
252
732
|
---
|