- Added dashboard functionality at /status with Basic Auth using DASHBOARD_PASSWORD. - Created server-side event logging to track catalog and university status. - Implemented traffic metrics tracking and display on the dashboard. - Enhanced API tests to cover new dashboard features and authentication. - Introduced CSS and JS for dashboard styling and functionality. - Added tests for dashboard access control and data retrieval.
55 lines
2.6 KiB
JavaScript
55 lines
2.6 KiB
JavaScript
import { test, before, after } from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { startApi } from "./support/nextcloud.js";
|
||
|
||
// The dashboard at /status: the browser's sign-in window, a lockout after ten wrong passwords,
|
||
// the page under the strict CSP, and its data.
|
||
let api;
|
||
before(async () => {
|
||
api = await startApi({ DASHBOARD_PASSWORD: "пароль-123" });
|
||
});
|
||
after(() => api?.stop());
|
||
const basic = (user, password) => ({ Authorization: "Basic " + Buffer.from(`${user}:${password}`).toString("base64") });
|
||
const get = (path, headers = {}) => fetch(api.base + path, { headers });
|
||
|
||
test("Дашборд: без пароля — окно входа браузера", async () => {
|
||
const response = await get("/status/");
|
||
assert.equal(response.status, 401);
|
||
assert.match(response.headers.get("www-authenticate"), /^Basic realm=/);
|
||
assert.equal(response.headers.get("cache-control"), "no-store");
|
||
assert.equal(response.headers.get("x-robots-tag"), "noindex");
|
||
assert.equal((await get("/status/data.json")).status, 401);
|
||
});
|
||
|
||
test("Дашборд: с паролем — страница, её файлы и данные", async () => {
|
||
const auth = basic("любой", "пароль-123");
|
||
const page = await get("/status/", auth);
|
||
assert.equal(page.status, 200);
|
||
const html = await page.text();
|
||
assert.match(html, /<script type="module" src="status\.js"><\/script>/);
|
||
// Nothing inline: the strict CSP allows only the server's own files.
|
||
assert.doesNotMatch(html, /<script>|style=/);
|
||
assert.match(page.headers.get("content-security-policy"), /script-src 'self'/);
|
||
assert.equal((await get("/status/status.js", auth)).status, 200);
|
||
assert.equal((await get("/status/status.css", auth)).status, 200);
|
||
|
||
await get("/api/groups?q=23");
|
||
const data = await (await get("/status/data.json", auth)).json();
|
||
assert.equal(typeof data.server.apiVersion, "number");
|
||
assert.ok(Array.isArray(data.server.waiting));
|
||
assert.ok(data.catalog.files > 0);
|
||
assert.equal(data.directory.documents, 2);
|
||
assert.equal(data.traffic.series.length, 60);
|
||
assert.ok(Array.isArray(data.events));
|
||
assert.equal(typeof data.diagnostics.problems, "number");
|
||
assert.equal(data.diagnostics.failed.length, 2);
|
||
assert.equal(data.telegram.enabled, false);
|
||
});
|
||
|
||
test("Дашборд: десять неверных паролей — блокировка на 10 минут", async () => {
|
||
for (let i = 0; i < 10; i++) assert.equal((await get("/status/", basic("admin", "не тот"))).status, 401);
|
||
const locked = await get("/status/", basic("admin", "пароль-123"));
|
||
assert.equal(locked.status, 429);
|
||
assert.ok(Number(locked.headers.get("retry-after")) > 0);
|
||
});
|