Files
proxmox/scripts/unifi/inspect-routing-page.js
defiQUG fbda1b4beb
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 15:46:57 -08:00

197 lines
6.8 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Inspect Routing Page - Find Add Button
*
* This script opens the UDM Pro routing page and inspects all elements
* to help identify the Add button selector.
*/
import { chromium } from 'playwright';
import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
// Load environment variables
const envPath = join(homedir(), '.env');
function loadEnvFile(filePath) {
try {
const envFile = readFileSync(filePath, 'utf8');
const envVars = envFile.split('\n').filter(
(line) => line.includes('=') && !line.trim().startsWith('#')
);
for (const line of envVars) {
const [key, ...values] = line.split('=');
if (key && values.length > 0 && /^[A-Z_][A-Z0-9_]*$/.test(key.trim())) {
let value = values.join('=').trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
process.env[key.trim()] = value;
}
}
return true;
} catch {
return false;
}
}
loadEnvFile(envPath);
const UDM_URL = process.env.UNIFI_UDM_URL || 'https://192.168.0.1';
const USERNAME = process.env.UNIFI_BROWSER_USERNAME || process.env.UNIFI_USERNAME || 'unifi_api';
const PASSWORD = process.env.UNIFI_BROWSER_PASSWORD || process.env.UNIFI_PASSWORD;
console.log('🔍 UDM Pro Routing Page Inspector');
console.log('==================================\n');
if (!PASSWORD) {
console.error('❌ UNIFI_PASSWORD must be set in ~/.env');
process.exit(1);
}
(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
console.log('1. Logging in...');
await page.goto(UDM_URL, { waitUntil: 'networkidle' });
await page.waitForSelector('input[type="text"]');
await page.fill('input[type="text"]', USERNAME);
await page.fill('input[type="password"]', PASSWORD);
await page.click('button[type="submit"]');
await page.waitForTimeout(3000);
console.log('2. Navigating to Routing page...');
await page.goto(`${UDM_URL}/network/default/settings/routing`, { waitUntil: 'networkidle' });
await page.waitForTimeout(5000);
console.log('3. Inspecting page elements...\n');
// Get all clickable elements
const clickableElements = await page.evaluate(() => {
const elements = [];
const allElements = document.querySelectorAll('button, a, [role="button"], [onclick], [class*="button"], [class*="click"]');
allElements.forEach((el, index) => {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
const styles = window.getComputedStyle(el);
if (styles.display !== 'none' && styles.visibility !== 'hidden') {
const text = el.textContent?.trim() || '';
const tagName = el.tagName.toLowerCase();
const className = el.className || '';
const id = el.id || '';
const ariaLabel = el.getAttribute('aria-label') || '';
const dataTestId = el.getAttribute('data-testid') || '';
const onclick = el.getAttribute('onclick') || '';
// Get parent info
const parent = el.parentElement;
const parentClass = parent?.className || '';
const parentTag = parent?.tagName.toLowerCase() || '';
elements.push({
index,
tagName,
text: text.substring(0, 50),
className: className.substring(0, 100),
id,
ariaLabel,
dataTestId,
onclick: onclick.substring(0, 50),
parentTag,
parentClass: parentClass.substring(0, 100),
xpath: getXPath(el),
});
}
}
});
function getXPath(element) {
if (element.id !== '') {
return `//*[@id="${element.id}"]`;
}
if (element === document.body) {
return '/html/body';
}
let ix = 0;
const siblings = element.parentNode.childNodes;
for (let i = 0; i < siblings.length; i++) {
const sibling = siblings[i];
if (sibling === element) {
return getXPath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix + 1) + ']';
}
if (sibling.nodeType === 1 && sibling.tagName === element.tagName) {
ix++;
}
}
}
return elements;
});
console.log(`Found ${clickableElements.length} clickable elements:\n`);
// Filter for potential Add buttons
const potentialAddButtons = clickableElements.filter(el => {
const text = el.text.toLowerCase();
const className = el.className.toLowerCase();
const ariaLabel = el.ariaLabel.toLowerCase();
return (
text.includes('add') ||
text.includes('create') ||
text.includes('new') ||
text === '+' ||
text === '' ||
className.includes('add') ||
className.includes('create') ||
ariaLabel.includes('add') ||
ariaLabel.includes('create')
);
});
console.log('🎯 Potential Add Buttons:');
console.log('='.repeat(80));
potentialAddButtons.forEach((el, i) => {
console.log(`\n${i + 1}. Element ${el.index}:`);
console.log(` Tag: ${el.tagName}`);
console.log(` Text: "${el.text}"`);
console.log(` Class: ${el.className}`);
console.log(` ID: ${el.id || 'none'}`);
console.log(` Aria Label: ${el.ariaLabel || 'none'}`);
console.log(` Data Test ID: ${el.dataTestId || 'none'}`);
console.log(` Parent: <${el.parentTag}> ${el.parentClass}`);
console.log(` XPath: ${el.xpath}`);
console.log(` Selector: ${el.tagName}${el.id ? `#${el.id}` : ''}${el.className ? `.${el.className.split(' ')[0]}` : ''}`);
});
console.log('\n\n📋 All Buttons on Page:');
console.log('='.repeat(80));
const buttons = clickableElements.filter(el => el.tagName === 'button');
buttons.forEach((el, i) => {
console.log(`\n${i + 1}. Button ${el.index}:`);
console.log(` Text: "${el.text}"`);
console.log(` Class: ${el.className}`);
console.log(` Aria Label: ${el.ariaLabel || 'none'}`);
console.log(` XPath: ${el.xpath}`);
});
console.log('\n\n⏸ Page is open in browser. Inspect elements manually if needed.');
console.log('Press Ctrl+C to close...\n');
// Keep browser open
await page.waitForTimeout(60000);
} catch (error) {
console.error('❌ Error:', error.message);
await page.screenshot({ path: 'inspect-error.png', fullPage: true });
} finally {
await browser.close();
}
})();