ArchiveBox/bin/setup_ui_screenshot_data.js

134 lines
4.9 KiB
JavaScript
Executable File

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer');
const [adminBaseUrl, username] = process.argv.slice(2);
if (!adminBaseUrl || !username || !process.env.SCREENSHOT_USER_DATA_DIR) {
throw new Error('usage: setup_ui_screenshot_data.js ADMIN_BASE_URL USERNAME (with SCREENSHOT_USER_DATA_DIR)');
}
function chromePath() {
const configured = process.env.CHROME_BINARY || process.env.PUPPETEER_EXECUTABLE_PATH;
return configured && fs.existsSync(configured) ? configured : undefined;
}
function needsNoSandbox() {
return process.platform === 'linux' && !String(process.env.DISPLAY || '').trim();
}
function addLaunchArg(args, arg) {
if (!args.includes(arg)) args.push(arg);
}
async function submitAdminAdd(page, url, configure) {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
if (!page.url().includes('/add/')) return false;
await configure();
const saveButton = 'button[name="_save"], input[name="_save"]';
await page.waitForSelector(saveButton, { visible: true, timeout: 10000 });
await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 60000 }),
page.click(saveButton),
]);
if (page.url().includes('/add/')) {
const errors = await page.$$eval('.errornote, .errorlist', (nodes) => nodes.map((node) => node.textContent.trim()).join(' '));
throw new Error(`admin setup form failed at ${page.url()}: ${errors || 'no form error shown'}`);
}
return true;
}
async function selectUser(page) {
const selector = '#id_created_by';
if (!(await page.$(selector))) return;
const value = await page.$eval(selector, (select, wanted) => {
const option = [...select.options].find((candidate) => candidate.textContent.trim() === wanted);
return option ? option.value : '';
}, username);
if (value) await page.select(selector, value);
}
async function selectFirstRealOption(page, selector) {
if (!(await page.$(selector))) return;
const value = await page.$eval(selector, (select) => {
const option = [...select.options].find((candidate) => candidate.value);
return option ? option.value : '';
});
if (value) await page.select(selector, value);
}
async function replaceText(page, selector, value) {
if (!(await page.$(selector))) return;
await page.click(selector);
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
await page.keyboard.down(modifier);
await page.keyboard.press('KeyA');
await page.keyboard.up(modifier);
await page.type(selector, value);
}
async function collapseAdminFilters(page, adminBaseUrl) {
await page.goto(`${adminBaseUrl}/admin/core/snapshot/`, { waitUntil: 'domcontentloaded', timeout: 60000 });
const toggle = await page.$('#changelist-filter-toggle');
if (!toggle) throw new Error('snapshot filter control is missing');
const expanded = await page.$eval('#changelist-filter-toggle', (button) => button.getAttribute('aria-expanded') === 'true');
if (expanded) {
await page.click('#changelist-filter-toggle');
await page.waitForFunction(() => document.body.classList.contains('filters-collapsed'));
}
}
async function main() {
const launchOptions = {
headless: true,
userDataDir: path.resolve(process.env.SCREENSHOT_USER_DATA_DIR),
defaultViewport: {
width: Number(process.env.SCREENSHOT_WIDTH || 1600),
height: Number(process.env.SCREENSHOT_HEIGHT || 1000),
},
protocolTimeout: 300000,
args: [],
};
if (needsNoSandbox()) {
addLaunchArg(launchOptions.args, '--no-sandbox');
addLaunchArg(launchOptions.args, '--disable-setuid-sandbox');
}
const executablePath = chromePath();
if (executablePath) launchOptions.executablePath = executablePath;
const browser = await puppeteer.launch(launchOptions);
try {
const page = await browser.newPage();
await page.goto(`${adminBaseUrl}/admin/`, { waitUntil: 'domcontentloaded', timeout: 60000 });
if (page.url().includes('/admin/login/')) throw new Error('screenshot persona is not logged in');
if (process.env.CREATE_API_TOKEN === '1') {
await submitAdminAdd(page, `${adminBaseUrl}/admin/api/apitoken/add/`, async () => {
await selectUser(page);
});
}
if (process.env.CREATE_WEBHOOK === '1') await submitAdminAdd(page, `${adminBaseUrl}/admin/api/outboundwebhook/add/`, async () => {
await replaceText(page, '#id_name', 'Snapshot completion notifications');
await selectFirstRealOption(page, '#id_signal');
await selectFirstRealOption(page, '#id_ref');
await replaceText(page, '#id_endpoint', 'https://httpbin.org/post');
if (await page.$('#id_enabled')) {
const enabled = await page.$eval('#id_enabled', (input) => input.checked);
if (!enabled) await page.click('#id_enabled');
}
await selectUser(page);
});
await collapseAdminFilters(page, adminBaseUrl);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exit(1);
});