Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- 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>
183 lines
7.4 KiB
JavaScript
183 lines
7.4 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Request SSL certificates for the 7 NPMplus proxy hosts that have no cert.
|
||
* Uses browser automation: for each host, edit → SSL → Request new certificate
|
||
* → DNS Challenge → Cloudflare → (first credential) → email + agree → submit.
|
||
*
|
||
* Run from repo root: node scripts/nginx-proxy-manager/request-npmplus-7-certs-dns-ui.js
|
||
* Requires: .env with NPM_URL, NPM_EMAIL, NPM_PASSWORD. HEADLESS=false to watch.
|
||
*/
|
||
|
||
import { chromium } from 'playwright';
|
||
import { fileURLToPath } from 'url';
|
||
import { dirname, join } from 'path';
|
||
import { config } from 'dotenv';
|
||
import https from 'https';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = dirname(__filename);
|
||
const PROJECT_ROOT = join(__dirname, '../..');
|
||
config({ path: join(PROJECT_ROOT, '.env') });
|
||
|
||
const NPM_URL = process.env.NPM_URL || 'https://192.168.11.167:81';
|
||
const NPM_EMAIL = process.env.NPM_EMAIL || 'admin@example.org';
|
||
const NPM_PASSWORD = process.env.NPM_PASSWORD;
|
||
const LETSENCRYPT_EMAIL = process.env.SSL_EMAIL || process.env.NPM_EMAIL || NPM_EMAIL;
|
||
const HEADLESS = process.env.HEADLESS !== 'false';
|
||
const PAUSE_MODE = process.env.PAUSE_MODE === 'true';
|
||
|
||
// Host IDs for the 7 proxy hosts without a certificate (from list-npmplus-proxy-hosts-cert-status.sh)
|
||
// Set FIRST_ONLY=1 to process only host 22 (for testing)
|
||
const ALL_HOST_IDS = [22, 26, 24, 27, 28, 29, 25];
|
||
const HOST_IDS_WITHOUT_CERT = process.env.FIRST_ONLY === '1' || process.env.FIRST_ONLY === 'true' ? ALL_HOST_IDS.slice(0, 1) : ALL_HOST_IDS;
|
||
|
||
if (!NPM_PASSWORD) {
|
||
console.error('❌ NPM_PASSWORD is required. Set it in .env or export NPM_PASSWORD=...');
|
||
process.exit(1);
|
||
}
|
||
|
||
function log(msg, type = 'info') {
|
||
const icons = { success: '✅', error: '❌', warning: '⚠️', info: '📋' };
|
||
console.log(`${icons[type] || '📋'} ${msg}`);
|
||
}
|
||
|
||
async function pause(page, message) {
|
||
if (PAUSE_MODE) {
|
||
log(`Paused: ${message}`, 'info');
|
||
await page.pause();
|
||
}
|
||
}
|
||
|
||
async function login(page) {
|
||
log('Logging in to NPMplus...');
|
||
await page.goto(NPM_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||
await pause(page, 'At login page');
|
||
|
||
await page.waitForSelector('input[type="email"], input[name="email"], input[placeholder*="email" i]', { timeout: 10000 });
|
||
const emailInput = await page.$('input[type="email"]') || await page.$('input[name="email"]') || await page.$('input[placeholder*="email" i]');
|
||
if (emailInput) await emailInput.fill(NPM_EMAIL);
|
||
const passwordInput = await page.$('input[type="password"]');
|
||
if (passwordInput) await passwordInput.fill(NPM_PASSWORD);
|
||
const loginButton = await page.$('button[type="submit"]') || await page.$('button:has-text("Sign In")') || await page.$('button:has-text("Login")');
|
||
if (loginButton) await loginButton.click();
|
||
else await page.keyboard.press('Enter');
|
||
|
||
await page.waitForTimeout(3000);
|
||
const url = page.url();
|
||
if (url.includes('login') && !url.includes('proxy')) {
|
||
const body = await page.textContent('body').catch(() => '');
|
||
if (!body.includes('Proxy Hosts') && !body.includes('dashboard')) {
|
||
log('Login may have failed – still on login page', 'warning');
|
||
await page.screenshot({ path: join(PROJECT_ROOT, 'npmplus-login-check.png') }).catch(() => {});
|
||
}
|
||
}
|
||
log('Logged in', 'success');
|
||
return true;
|
||
}
|
||
|
||
async function requestCertForHostId(page, hostId) {
|
||
log(`Requesting cert for host ID ${hostId}...`);
|
||
|
||
try {
|
||
// NPM edit URL: open edit form directly by host ID
|
||
await page.goto(`${NPM_URL}/#/proxy-hosts/edit/${hostId}`, { waitUntil: 'networkidle' });
|
||
await page.waitForTimeout(3000);
|
||
|
||
// SSL tab: NPM edit form usually has Details | SSL | Advanced
|
||
await page.getByText('SSL').first().click();
|
||
await page.waitForTimeout(1500);
|
||
|
||
// "Request a new SSL Certificate" / "Get a new certificate"
|
||
const requestBtn = page.getByRole('button', { name: /request.*(new )?ssl certificate|get.*certificate/i }).or(
|
||
page.locator('button:has-text("Request"), button:has-text("Get a new"), a:has-text("Request")').first()
|
||
);
|
||
await requestBtn.click();
|
||
await page.waitForTimeout(1500);
|
||
|
||
// DNS Challenge: click option/label for "DNS Challenge" or "Use a DNS Challenge"
|
||
const dnsOption = page.getByText(/use a dns challenge|dns challenge/i).first();
|
||
await dnsOption.click();
|
||
await page.waitForTimeout(800);
|
||
|
||
// DNS Provider: Cloudflare (dropdown or first Cloudflare option)
|
||
const cloudflareOption = page.getByText('Cloudflare').first();
|
||
await cloudflareOption.click();
|
||
await page.waitForTimeout(800);
|
||
|
||
// Credential: usually first in dropdown if only one Cloudflare credential
|
||
const credSelect = page.locator('select').filter({ has: page.locator('option') }).first();
|
||
if (await credSelect.count() > 0) {
|
||
await credSelect.selectOption({ index: 1 });
|
||
}
|
||
await page.waitForTimeout(500);
|
||
|
||
// Email for Let's Encrypt
|
||
const emailField = page.locator('input[type="email"], input[name*="email" i]').first();
|
||
await emailField.fill(LETSENCRYPT_EMAIL);
|
||
|
||
// Agree to ToS
|
||
const agree = page.locator('input[type="checkbox"]').filter({ has: page.locator('..') }).first();
|
||
if (await agree.count() > 0 && !(await agree.isChecked())) await agree.check();
|
||
|
||
await pause(page, `Ready to submit cert request for host ${hostId}`);
|
||
|
||
// Submit
|
||
const submitBtn = page.getByRole('button', { name: /save|submit|request|get certificate/i }).first();
|
||
await submitBtn.click();
|
||
await page.waitForTimeout(5000);
|
||
|
||
// Check for success or error
|
||
const body = await page.textContent('body').catch(() => '');
|
||
if (body.includes('error') && body.toLowerCase().includes('internal')) {
|
||
log(`Request for host ${hostId} may have failed (Internal Error). Check NPM UI.`, 'warning');
|
||
return false;
|
||
}
|
||
log(`Submitted cert request for host ${hostId}`, 'success');
|
||
return true;
|
||
} catch (e) {
|
||
log(`Error for host ${hostId}: ${e.message}`, 'error');
|
||
await page.screenshot({ path: join(PROJECT_ROOT, `npmplus-cert-error-${hostId}.png`) }).catch(() => {});
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
console.log('🔒 NPMplus – Request 7 certificates (DNS Cloudflare)');
|
||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||
console.log('');
|
||
log(`NPM: ${NPM_URL}`);
|
||
log(`Host IDs: ${HOST_IDS_WITHOUT_CERT.join(', ')}`);
|
||
console.log('');
|
||
|
||
const browser = await chromium.launch({ headless: HEADLESS, ignoreHTTPSErrors: true });
|
||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||
const page = await context.newPage();
|
||
|
||
try {
|
||
const ok = await login(page);
|
||
if (!ok) {
|
||
log('Login failed', 'error');
|
||
process.exit(1);
|
||
}
|
||
|
||
let success = 0;
|
||
for (const hostId of HOST_IDS_WITHOUT_CERT) {
|
||
const ok = await requestCertForHostId(page, hostId);
|
||
if (ok) success++;
|
||
await page.waitForTimeout(2000);
|
||
}
|
||
|
||
console.log('');
|
||
log(`Done. Submitted requests for ${success}/${HOST_IDS_WITHOUT_CERT.length} hosts. Check NPM SSL Certificates and Hosts to confirm.`, 'success');
|
||
log('Run: ./scripts/list-npmplus-proxy-hosts-cert-status.sh', 'info');
|
||
} finally {
|
||
await browser.close();
|
||
}
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
});
|