Redesign admin peering settings and catalogs

This commit is contained in:
Codex committed 2026-10-11 01:06:03 +02:00
1 parent 492477f86a
commit da40d73f02
16 files changed
+1037 -85

No files matched your search

+51 -38
View File
@@ -12,61 +12,74 @@ module.exports = async function testPeerUI(url, cookie) {
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.goto(url + '/peers.html#admin');
await page.getByRole('heading', { name: 'Known peers' }).waitFor();
assert.ok(await page.getByRole('link', { name: 'Peer catalogs' }).count() >= 1);
const state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
assert.equal(state.identity?.name, 'ComposeQA');
await page.getByRole('button', { name: 'Add a peer' }).click();
await page.getByRole('heading', { name: 'Add a peer' }).waitFor();
await page.getByLabel('This server’s 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');
assert.match(await page.getByLabel('This server’s reachable peer gateway URL').evaluate(el => el.validationMessage), /HTTPS peer gateway origin/);
await page.getByLabel('This server’s 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());
const outgoing = page.getByLabel('Invitation to send');
await page.waitForFunction(() => document.querySelector('textarea[aria-label="Invitation to send"]')?.value.includes('fingerprint'));
const invitation = JSON.parse(await outgoing.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');
assert.match(invitation.name, /^[A-Za-z0-9]{1,25}$/);
assert.ok(Date.parse(invitation.expiresAt) > Date.now());
await page.getByLabel('Invitation received').fill('{bad json');
await page.getByRole('button', { name: 'Import invitation' }).click();
await page.getByText('Invalid invitation JSON').waitFor();
await page.getByText('That is not valid invitation JSON.').waitFor();
const peerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-peer-ui-'));
let peerId;
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.getByLabel('Invitation received').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');
await page.getByRole('heading', { name: '<UIFixturePeer>', exact: true }).waitFor();
const apiState = await page.evaluate(() => fetch('/api/peering').then(response => response.json()));
peerId = apiState.peers.find(item => item.alias === 'UIFixturePeer').id;
await page.locator('.peer-page-tabs').getByRole('link', { name: 'Peer catalogs' }).click();
await page.getByRole('heading', { name: 'Peer catalogs' }).waitFor();
if (!await page.getByRole('button', { name: 'Select visible' }).count()) {
console.error('Peer catalog page content:', await page.locator('main').innerText());
console.error('Peer catalog JS errors:', errors);
}
await page.getByLabel('Select peer').selectOption(peerId);
assert.equal(await page.getByText('Setup is incomplete', { exact: false }).count(), 0, 'Sharing can be prepared before pairing');
await page.getByRole('button', { name: 'Select visible' }).click();
await page.getByRole('button', { name: 'Save approvals' }).click();
await page.getByText('Approved titles updated.').waitFor();
const saved = await page.evaluate(id => fetch('/api/admin/peering').then(r => r.json()).then(s => s.peers.find(p => p.id === id).shares.length), peerId);
assert.equal(saved, 3, 'Bulk approval saves the selected local catalog');
assert.equal(await page.getByRole('button', { name: 'Browse peer catalog' }).count(), 1);
await page.getByRole('button', { name: 'Browse peer catalog' }).click();
await page.getByText('Setup is incomplete', { exact: false }).waitFor();
await page.setViewportSize({ width: 390, height: 844 });
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Peer catalog should fit mobile width');
} 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;
});
const created = await page.evaluate(async () => (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 }) })).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);
await page.goto(url + '/peer-catalog.html');
await page.getByText('Peer catalogs are available to administrators only.').waitFor();
const denied = await page.evaluate(async () => Promise.all(['/api/admin/peering', '/api/peering', '/api/peering/catalog?peer=x', '/api/peering/cover?peer=x&manga=x']
.map(async endpoint => (await fetch(endpoint)).status)));
assert.deepEqual(denied, [403, 403, 403, 403], 'Every peering API remains admin-only');
await page.goto(url + '/');
await page.getByRole('link', { name: 'Peer catalogs' }).waitFor({ state: 'detached' });
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
} finally { await browser.close(); }
};