docs: verify against real WHP + capture real screenshots
Discovery against the demo account on whp01 surfaced several inaccuracies: - Cache is Valkey (Redis wire-compatible), not Redis or Memcached. No Memcached is offered as a separate service. - Site Monitoring is the sidebar label (not 'AI Monitor'). - 'Add a domain' has no Primary/Add-on distinction. - Sites form: 'Container Type' (not 'Site type'), Number of Containers (1-10 for horizontal scaling), CPU per Container (default 0.25), Memory per Container (default 256MB), SSL inline on the same form. - Backups: default retention 5 days / 10 backups; on-demand + scheduled; S3 backup targets are visible and configurable. - Email: per-domain settings live behind 'Setup Instructions' on the Email page; mail server hostname is on the Dashboard (per-server, e.g. mail01.cloud-hosting.io), not per-domain. Also reworked the screenshot pipeline: - New shots.config.ts targets the real index.php?page=... URLs - Added redactSensitive() step that runs before each screenshot to swap server names, IPs, mail hostnames, and demo-user-isms with neutral placeholders. This keeps docs portable across the fleet. - Hides .brand-full and .navbar-text (top-bar server identifier and Welcome greeting). - Captured 9 real WHP screenshots; removed stale placeholders.
This commit is contained in:
@@ -22,13 +22,57 @@ const WHP_USER = envOrDie('WHP_USER');
|
||||
const WHP_PASS = envOrDie('WHP_PASS');
|
||||
|
||||
async function login(page: Page): Promise<void> {
|
||||
await page.goto(`${WHP_BASE}/login`);
|
||||
await page.fill('input[name="username"]', WHP_USER);
|
||||
await page.goto(`${WHP_BASE}/login.php`);
|
||||
await page.fill('input[name="user"]', WHP_USER);
|
||||
await page.fill('input[name="password"]', WHP_PASS);
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Neutralise server-identifying and account-specific text/labels before the
|
||||
* screenshot — we run a multi-server fleet, so docs shouldn't bake in a single
|
||||
* host's name or IP.
|
||||
*
|
||||
* Runs in the page context. Self-contained: no external bindings, no eval.
|
||||
*/
|
||||
async function redactSensitive(page: Page): Promise<void> {
|
||||
await page.addStyleTag({
|
||||
content: `
|
||||
.navbar-text { visibility: hidden !important; }
|
||||
.brand-full { visibility: hidden !important; }
|
||||
`,
|
||||
});
|
||||
|
||||
// String swaps for inline text that mask-by-selector can't cover.
|
||||
await page.evaluate(() => {
|
||||
const root = document.body;
|
||||
const swaps: [RegExp, string][] = [
|
||||
// server / mail / nameserver hostnames (with possible -s3 etc. suffix)
|
||||
[/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '<your-server>.cloud-hosting.io'],
|
||||
[/mail\d+\.cloud-hosting\.io/gi, '<your-mail-server>.cloud-hosting.io'],
|
||||
[/ns[12]\.whp\d+\.cloud-hosting\.io/gi, 'ns<n>.your-server.cloud-hosting.io'],
|
||||
// backup target / bucket names that bake in server number
|
||||
[/whp\d+(-[a-z0-9]+)?\b/gi, '<your-server>'],
|
||||
[/WHP\d+(-[A-Z0-9]+)?\b/g, '<YOUR-SERVER>'],
|
||||
// IP addresses
|
||||
[/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '<your-server-IP>'],
|
||||
// home dir + demo user
|
||||
[/\/docker\/users\/[a-z0-9-]+/g, '/docker/users/<your-username>'],
|
||||
[/demo-user/g, 'your-username'],
|
||||
];
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
||||
const nodes: Text[] = [];
|
||||
let n: Node | null = walker.nextNode();
|
||||
while (n) { nodes.push(n as Text); n = walker.nextNode(); }
|
||||
for (const node of nodes) {
|
||||
let v = node.nodeValue ?? '';
|
||||
for (const [re, replacement] of swaps) v = v.replace(re, replacement);
|
||||
if (v !== node.nodeValue) node.nodeValue = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function captureShot(page: Page, shot: Shot): Promise<void> {
|
||||
const viewport = shot.viewport ?? { width: 1440, height: 900 };
|
||||
await page.setViewportSize(viewport);
|
||||
@@ -36,6 +80,7 @@ async function captureShot(page: Page, shot: Shot): Promise<void> {
|
||||
await page.goto(`${WHP_BASE}${shot.path}`);
|
||||
await page.waitForLoadState('networkidle');
|
||||
if (shot.waitFor) await page.waitForSelector(shot.waitFor);
|
||||
await redactSensitive(page);
|
||||
|
||||
const maskSelectors = [...DEFAULT_MASK, ...(shot.mask ?? [])];
|
||||
const maskLocators: Locator[] = maskSelectors.map((sel) => page.locator(sel));
|
||||
|
||||
Reference in New Issue
Block a user