fix(terminal): показывать последние записи журнала RouterOS

This commit is contained in:
Denozordec committed 2026-10-11 11:26:59 +07:00
1 parent 1e4d3b4678
commit bd023b8aaa
15 files changed
+321 -16

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
"db:migrate-from-sqlite": "tsx src/scripts/migrate-sqlite-to-pg.ts",
"facts:rebuild": "tsx src/scripts/rebuild-flow-facts.ts",
"test:probes": "tsx src/routes/probes.test.ts && tsx src/routes/probes.cancel.test.ts",
"test:exec": "tsx src/routes/exec.test.ts && tsx src/plugins/auth.exec.test.ts",
"test:exec": "tsx src/services/mikrotik-logs.test.ts && tsx src/routes/exec.test.ts && tsx src/plugins/auth.exec.test.ts",
"test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts && tsx src/plugins/auth.ospf-optimizer.test.ts",
"test:wireguard": "npx tsx src/services/wireguard-config.test.ts && npx tsx --test src/services/wireguard-import.test.ts src/services/wireguard-import-apply.test.ts src/services/wireguard-import-revision.test.ts && npx tsx src/routes/wireguard-import.test.ts",
"test:traffic-rate": "tsx src/services/traffic-rate.test.ts",
+2
View File
@@ -30,6 +30,8 @@ outputs.ping=[{sent:"1",received:"0","avg-rtt":"0ms"}];assert.match((await send(
outputs.ping=[{sent:"1",received:"1","avg-rtt":"0ms"}];assert.match((await send("ping 192.0.2.2 count=1")).json().output,/avg-rtt=0ms/)
assert.equal((await send("ping 192.0.2.2 count=99")).statusCode,200);assert.deepEqual(calls.at(-1),["ping","192.0.2.2",20])
assert.equal((await send("log print limit=500")).statusCode,200);assert.deepEqual(calls.at(-1),["getLogs",200])
outputs.getLogs=new Error("Журнал недоступен");const failedLogs=await send("log print limit=3");assert.equal(failedLogs.statusCode,502);assert.equal(failedLogs.json().error,"Журнал недоступен");assert.equal(failedLogs.json().output,undefined)
outputs.getLogs=[];const emptyLogs=await send("log print limit=3");assert.equal(emptyLogs.statusCode,200);assert.equal(emptyLogs.json().output,"(no log entries)")
for(const command of ["system reboot","pingbad 192.0.2.2","log printable","ping","ping 192.0.2.2 count=0","ping 192.0.2.2 count=1junk","ping 192.0.2.2 count=1 count=2","log print limit=0","log print limit=3junk"]){const before=calls.length;assert.equal((await send(command)).statusCode,400,command);assert.equal(calls.length,before)}
exists=false;assert.equal((await send("interface print")).statusCode,404);exists=true
enabled=false;const beforeDisabled={clients,calls:calls.length};for(const command of["interface print","ping 192.0.2.2 count=1","help"])assert.equal((await send(command)).statusCode,409);assert.deepEqual({clients,calls:calls.length},beforeDisabled);enabled=true
@@ -0,0 +1,44 @@
import assert from "node:assert/strict"
import { createServer } from "node:http"
import { test } from "node:test"
import { MikrotikClient } from "./mikrotik.js"
test("REST logs use the default buffer and return the latest bounded entries", async () => {
const history = Array.from({ length: 1000 }, (_, i) => ({
".id": `*${i.toString(16)}`, time: `time-${i}`, topics: "system,info", message: `entry-${i}`,
}))
let rows = history, denied = false, requests = 0
const server = createServer((req, res) => {
requests++
const url = new URL(req.url!, "http://127.0.0.1")
assert.equal(req.method, "GET")
assert.equal(url.pathname, "/rest/log")
assert.equal(url.searchParams.get("buffer"), "memory")
assert.equal(url.searchParams.get(".proplist"), ".id,time,topics,message")
assert.equal(url.searchParams.has("limit"), false)
res.writeHead(denied ? 401 : 200, { "content-type": "application/json" })
res.end(JSON.stringify(denied ? { message: "Unauthorized" } : rows))
})
await new Promise<void>(resolve => server.listen(0, "127.0.0.1", resolve))
const address = server.address()
assert.ok(address && typeof address !== "string")
const client = new MikrotikClient({ host: "127.0.0.1", port: address.port, useSsl: false, verifySsl: false, username: "fixture", password: "fixture" })
try {
assert.deepEqual(await client.getLogs(3), history.slice(-3))
assert.deepEqual(await client.getLogs(), history.slice(-50))
assert.deepEqual(await client.getLogs(500), history.slice(-200))
rows = history.slice(0, 2)
assert.deepEqual(await client.getLogs(3), rows)
rows = []
assert.deepEqual(await client.getLogs(3), [])
const before = requests
for (const limit of [0, -1, 1.5, NaN, Infinity, Number.MAX_SAFE_INTEGER + 1]) {
await assert.rejects(client.getLogs(limit), RangeError)
}
assert.equal(requests, before, "Invalid limits must not poll RouterOS")
denied = true
await assert.rejects(client.getLogs(3), /401.*Unauthorized/)
} finally {
await new Promise<void>((resolve, reject) => server.close(error => error ? reject(error) : resolve()))
}
})
+7 -1
View File
@@ -696,7 +696,13 @@ export class MikrotikClient {
}
async getLogs(limit = 50): Promise<RosLogEntry[]> {
return this.get<RosLogEntry[]>(`/log?limit=${limit}`)
if (!Number.isSafeInteger(limit) || limit < 1) {
throw new RangeError("limit должен быть положительным целым числом.")
}
// REST query parameters filter properties; limit is not a print row limit.
// Read only the default buffer and retain its latest entries in native order.
const entries = await this.get<RosLogEntry[]>("/log?buffer=memory&.proplist=.id,time,topics,message")
return entries.slice(-Math.min(limit, 200))
}
async ping(