Add scoped peer access and reversible serving controls

This commit is contained in:
Codex committed 2026-10-11 00:25:14 +02:00
1 parent 6c994113c6
commit 492477f86a
7 files changed
+1081

No files matched your search

+72
View File
@@ -0,0 +1,72 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs'), os = require('node:os'), path = require('node:path');
const { PeeringState } = require('../peering/state');
module.exports = async function testPeerUI(url, cookie) {
const { chromium } = require('playwright');
const browser = await chromium.launch({ channel: process.env.QA_BROWSER_CHANNEL || 'msedge', headless: true });
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
const [name, ...value] = cookie.split('=');
await context.addCookies([{ name, value: value.join('='), url }]);
const page = await context.newPage(), errors = [];
page.on('pageerror', error => errors.push(error.message));
try {
await page.goto(url + '/peers.html');
await page.getByRole('heading', { name: 'Peer catalogs', exact: true }).waitFor();
let state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
if (!state.identity) {
await page.getByRole('button', { name: 'Generate local certificate' }).click();
await page.getByText('Identity generated').waitFor();
state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
}
assert.match(state.identity.fingerprint, /^[0-9a-f]{64}$/);
await page.getByLabel('Reachable peer-gateway URL').fill('http://localhost:8080');
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
assert.match(await page.getByLabel('Reachable peer-gateway URL').evaluate(el => el.validationMessage), /HTTPS peer-gateway origin/);
await page.getByLabel('Reachable peer-gateway URL').fill('https://peer-test.example:8093');
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
await page.waitForFunction(() => document.querySelector('textarea[aria-label="Invitation to exchange"]')?.value.includes('fingerprint'));
const invitation = JSON.parse(await page.getByLabel('Invitation to exchange').inputValue());
assert.equal(invitation.url, 'https://peer-test.example:8093');
assert.equal(invitation.fingerprint, state.identity.fingerprint);
await page.getByLabel('Other server invitation').fill('{bad json');
await page.getByRole('button', { name: 'Import invitation' }).click();
await page.getByText('Invalid invitation JSON').waitFor();
const peerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-peer-ui-'));
try {
if (process.platform === 'win32' && !process.env.OPENSSL_BIN) process.env.OPENSSL_BIN = 'C:\\Program Files\\Git\\usr\\bin\\openssl.exe';
const peer = new PeeringState(peerDir);
peer.generateIdentity('UIFixturePeer');
const fixtureInvitation = peer.createInvitation('https://fixture-peer.example:8093');
await page.getByLabel('Other server invitation').fill(JSON.stringify(fixtureInvitation));
await page.getByRole('button', { name: 'Import invitation' }).click();
await page.getByRole('heading', { name: 'UI fixture peer' }).waitFor();
const card = page.locator('.peer-card').filter({ has: page.getByRole('heading', { name: 'UI fixture peer' }) });
await card.getByText('Transfer limits').click();
await card.getByText('Approved manga (0)').click();
await card.getByText('Access grant').click();
await card.getByLabel('Moon Garden').check();
await card.getByText('Approved manga (1)').waitFor();
assert.equal(await card.locator('details[open]').count(), 3, 'Saving a share must preserve all expanded sections');
} finally { fs.rmSync(peerDir, { recursive: true, force: true }); }
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
await page.setViewportSize({ width: 390, height: 844 });
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Peering page overflows on mobile');
// An ordinary reader may browse, but cannot operate certificate controls.
const created = await page.evaluate(async () => {
const response = await fetch('/api/admin/user', { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'peer-reader-qa', password: 'disposable-reader-test', admin: false }) });
return response.status;
});
assert.equal(created, 200);
await page.evaluate(() => fetch('/api/logout', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' }));
await page.evaluate(() => fetch('/api/login', { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'peer-reader-qa', password: 'disposable-reader-test' }) }));
await page.reload();
await page.getByRole('heading', { name: 'Peer catalogs', exact: true }).waitFor();
assert.equal(await page.getByRole('heading', { name: 'Server identity & invitations' }).count(), 0);
assert.equal((await page.evaluate(() => fetch('/api/admin/peering').then(response => response.status))), 403);
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
} finally { await browser.close(); }
};