diff --git a/docker-compose.yml b/docker-compose.yml index a738038..07b1725 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,3 +22,4 @@ services: - ./provision-discourse-apikey.py:/app/provision-discourse-apikey.py:ro - ./provision-redmine.py:/app/provision-redmine.py:ro - ./merge-invites.py:/app/merge-invites.py:ro + - ./dump-invite-page.py:/app/dump-invite-page.py:ro diff --git a/dump-invite-page.py b/dump-invite-page.py new file mode 100644 index 0000000..aa7c16c --- /dev/null +++ b/dump-invite-page.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +dump-invite-page.py -- DOM dump of a Cloudron invite/setup page (pre-acceptance). + +Usage: + docker compose run --rm --entrypoint python3 \ + -e AGENT=vp-secops provision dump-invite-page.py +""" + +import os, sys, time +from pathlib import Path +from playwright.sync_api import sync_playwright + +STATE_DIR = Path("/app/state") + +AGENT = os.environ.get("AGENT", "vp-secops") + +import yaml +with open("/app/agents.yaml") as f: + manifest = yaml.safe_load(f) +agent = next(a for a in manifest["agents"] if a["name"] == AGENT) +invite_url = agent["cloudron_invite"] + +with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + page = browser.new_context(viewport={"width": 1280, "height": 1024}).new_page() + page.goto(invite_url, wait_until="networkidle", timeout=20000) + page.wait_for_timeout(3000) + + ts = time.strftime("%H%M%S") + page.screenshot(path=str(STATE_DIR / f"invite-{AGENT}-{ts}.png"), full_page=True) + elements = page.evaluate("""() => { + const out = []; + document.querySelectorAll('input, button, [role="button"], label, a, h1, h2, h3, form').forEach(el => { + const tag = el.tagName.toLowerCase(); + const text = (el.textContent || '').trim().substring(0, 60); + const id = el.id || ''; + const type = el.getAttribute('type') || ''; + const cls = (el.getAttribute('class') || '').substring(0, 50); + const role = el.getAttribute('role') || ''; + const vis = el.offsetParent !== null; + out.push(`<${tag}> id=${id} type=${type} role=${role} class=${cls} vis=${vis} text="${text}"`); + }); + return out; + }""") + body = page.evaluate("() => document.body.innerText.substring(0, 400)") + out = f"URL: {page.url}\n\nBODY:\n{body}\n\nELEMENTS:\n" + "\n".join(elements) + (STATE_DIR / f"invite-{AGENT}-{ts}.txt").write_text(out) + print(out) + browser.close() diff --git a/provision-agent.py b/provision-agent.py index 2b14997..1da97e3 100644 --- a/provision-agent.py +++ b/provision-agent.py @@ -119,28 +119,33 @@ def enroll_cloudron( log.info(f"[{name}] Generated password ({len(password)} chars)") # Navigate to invite link + # Proven selectors (session 3 DOM dump of setupaccount.html): + # #inputUsername (prefilled from invite), #inputDisplayName, + # #inputPassword, #inputPasswordRepeat, + # submit =