/**
* HTML templates for the admin area.
*
* Deliberately plain. Every action is a form submission that redirects back,
* so a JavaScript-off session works — the only script we ship is a tiny
* convenience to copy the sign-this string on the login page.
*
* Pages follow the same {title, description, body, here?} shape as the public
* pages, so the shared layout wraps them unchanged. The admin topnav sits
* inside each body (so the public topnav still appears above it for continuity).
*/
import { escapeHtml } from './layout.js';
import { CATEGORIES } from './pages-browse.js';
import { MODES } from './modes.js';
import { adminPublisher } from './admin-session.js';
/* ------------------------------- login ------------------------------- */
export function loginPage({ challenge, error = null, name }) {
const message = `PROMETHEUS-ADMIN-LOGIN\nname=${name}\nchallenge=${challenge}`;
return {
title: 'Admin — sign in',
description: 'Sign in to the Prometheus admin.',
here: '',
body: `
Admin
Sign in with the key that owns ${escapeHtml(name)}.
${error ? `
Sign-in failed. ${escapeHtml(error)}
` : ''}
1. Copy the challenge below into your wallet's "sign message" box.The entire block, exactly as shown.
2. Sign with the key for the address that owns the admin name.Theseus's Aegis panel, Electron Cash, Deviant — any BIP-137
wallet works.
3. Paste the signature below and submit.The signature authenticates you as the admin for the next 24 hours.
payment gate — ${escapeHtml(config.required ? 'new listings land as pending' : 'new listings publish immediately')}
$${escapeHtml(String(config.fee_usd))}
listing fee per publication
${escapeHtml(config.address || '(not set)')}
operator receive address
${!config.address ? `
No receive address configured. Set PROMETHEUS_PAYMENT_ADDRESS
in the service environment and restart. Until then, /api/invoice refuses to issue.
` : ''}
${config.required && !config.address ? `
Payments are required but no address is set. New listings are being created as
pending but cannot be settled. Set the address and restart,
or unset PAYMENT_REQUIRED.
The mempool watcher that would auto-reconcile incoming payments to pending
invoices is a follow-up milestone. For now, confirm a payment by hand once it lands —
paste the txid into the Mark paid form on an invoice row. The listing becomes visible
on browse the moment the invoice flips to paid.
The three modes describe how new listings get accepted. The
global default applies everywhere unless a category has its own override.
Clear an override by picking "inherits global" from the per-category dropdown.
append — any valid listing goes in; readers see the signed scan report and decide. scan-first — infected uploads are rejected before they land. Clean ones publish. review — clean listings sit in a human-review queue before browse shows them. Toggle persists; enforcement lands in a later milestone.
Append-only. Each row's this_hash
is computed over its own fields plus the previous row's hash — a retroactive
edit anywhere in the chain shows up as a mismatch in the next row.