#!/usr/bin/env node /** * Configure SSL certificates for all domains in Nginx Proxy Manager via API * This script uses the NPM API directly instead of browser automation */ import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { config } from 'dotenv'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const PROJECT_ROOT = join(__dirname, '../..'); // Load environment variables config({ path: join(PROJECT_ROOT, '.env') }); // Configuration const NPM_URL = process.env.NPM_URL || 'http://192.168.11.26:81'; const NPM_EMAIL = process.env.NPM_EMAIL || process.env.NGINX_EMAIL || 'nsatoshi2007@hotmail.com'; const NPM_PASSWORD = process.env.NPM_PASSWORD || process.env.NGINX_PASSWORD || 'L@ker$2010'; // All domains to configure const DOMAINS = [ // sankofa.nexus zone { domain: 'sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false }, { domain: 'www.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false }, { domain: 'phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false }, { domain: 'www.phoenix.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false }, { domain: 'the-order.sankofa.nexus', target: 'http://192.168.11.140:80', websocket: false }, // d-bis.org zone { domain: 'explorer.d-bis.org', target: 'http://192.168.11.140:4000', websocket: false }, { domain: 'rpc-http-pub.d-bis.org', target: 'https://192.168.11.252:443', websocket: true }, { domain: 'rpc-ws-pub.d-bis.org', target: 'https://192.168.11.252:443', websocket: true }, { domain: 'rpc-http-prv.d-bis.org', target: 'https://192.168.11.251:443', websocket: true }, { domain: 'rpc-ws-prv.d-bis.org', target: 'https://192.168.11.251:443', websocket: true }, { domain: 'dbis-admin.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, { domain: 'dbis-api.d-bis.org', target: 'http://192.168.11.155:3000', websocket: false }, { domain: 'dbis-api-2.d-bis.org', target: 'http://192.168.11.156:3000', websocket: false }, { domain: 'secure.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, // mim4u.org zone // MIM4U - VMID 7810 (mim-web-1) @ 192.168.11.37 - Web Frontend serves main site and proxies /api/* to 7811 { domain: 'mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, { domain: 'www.mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, { domain: 'secure.mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, { domain: 'training.mim4u.org', target: 'http://192.168.11.37:80', websocket: false }, // defi-oracle.io zone { domain: 'rpc.public-0138.defi-oracle.io', target: 'https://192.168.11.252:443', websocket: true }, ]; // Helper functions function log(message, type = 'info') { const prefix = type === 'error' ? '[✗]' : type === 'success' ? '[✓]' : '[INFO]'; console.log(`${prefix} ${message}`); } async function getAuthToken() { try { const response = await fetch(`${NPM_URL}/api/tokens`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: NPM_EMAIL, secret: NPM_PASSWORD }) }); const data = await response.json(); if (data.token) { log('Authentication successful', 'success'); return data.token; } else { log(`Authentication failed: ${data.error?.message || 'Unknown error'}`, 'error'); return null; } } catch (error) { log(`Authentication error: ${error.message}`, 'error'); return null; } } async function checkExistingProxyHost(token, domain) { try { const response = await fetch(`${NPM_URL}/api/nginx/proxy-hosts`, { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); if (data.result) { const existing = data.result.find(host => host.domain_names && host.domain_names.includes(domain) ); return existing; } return null; } catch (error) { log(`Error checking existing host for ${domain}: ${error.message}`, 'error'); return null; } } async function createProxyHost(token, domainConfig) { const { domain, target, websocket } = domainConfig; const url = new URL(target); const scheme = url.protocol.replace(':', ''); const hostname = url.hostname; const port = url.port || (scheme === 'https' ? '443' : '80'); const payload = { domain_names: [domain], forward_scheme: scheme, forward_hostname: hostname, forward_port: parseInt(port), allow_websocket_upgrade: websocket, block_exploits: true, cache_enabled: false, ssl_forced: true, http2_support: true, hsts_enabled: true, hsts_subdomains: true, access_list_id: 0, certificate_id: 0, meta: { letsencrypt_agree: true, letsencrypt_email: NPM_EMAIL } }; try { const response = await fetch(`${NPM_URL}/api/nginx/proxy-hosts`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); if (data.id) { log(`Created proxy host for ${domain} (ID: ${data.id})`, 'success'); return data; } else { log(`Failed to create proxy host for ${domain}: ${data.error?.message || 'Unknown error'}`, 'error'); return null; } } catch (error) { log(`Error creating proxy host for ${domain}: ${error.message}`, 'error'); return null; } } async function requestSSLCertificate(token, proxyHostId, domain) { try { // First, get the certificate ID by requesting it const response = await fetch(`${NPM_URL}/api/nginx/certificates`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ domain_names: [domain], provider: 'letsencrypt', letsencrypt_email: NPM_EMAIL, letsencrypt_agree: true }) }); const certData = await response.json(); if (certData.id) { log(`Requested SSL certificate for ${domain} (Cert ID: ${certData.id})`, 'success'); // Update proxy host to use this certificate const updateResponse = await fetch(`${NPM_URL}/api/nginx/proxy-hosts/${proxyHostId}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ certificate_id: certData.id, ssl_forced: true }) }); const updateData = await updateResponse.json(); if (updateData.id) { log(`Updated proxy host ${proxyHostId} with SSL certificate`, 'success'); return true; } } else { log(`Failed to request SSL certificate for ${domain}: ${certData.error?.message || 'Unknown error'}`, 'error'); } return false; } catch (error) { log(`Error requesting SSL certificate for ${domain}: ${error.message}`, 'error'); return false; } } async function main() { console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('🔒 Nginx Proxy Manager SSL Configuration (API)'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log(''); log(`NPM URL: ${NPM_URL}`); log(`Email: ${NPM_EMAIL}`); log(`Domains to configure: ${DOMAINS.length}`); console.log(''); // Get authentication token const token = await getAuthToken(); if (!token) { log('Cannot proceed without authentication token', 'error'); process.exit(1); } let successCount = 0; let failCount = 0; let skipCount = 0; // Configure each domain for (const domainConfig of DOMAINS) { const { domain } = domainConfig; log(`Processing ${domain}...`); // Check if already exists const existing = await checkExistingProxyHost(token, domain); if (existing) { log(`Proxy host for ${domain} already exists (ID: ${existing.id}), skipping creation`, 'info'); // Check if it has SSL certificate if (!existing.certificate_id || existing.certificate_id === 0) { log(`Requesting SSL certificate for existing proxy host ${domain}...`); const sslSuccess = await requestSSLCertificate(token, existing.id, domain); if (sslSuccess) { successCount++; } else { failCount++; } } else { log(`SSL certificate already configured for ${domain}`, 'success'); successCount++; } skipCount++; continue; } // Create proxy host const proxyHost = await createProxyHost(token, domainConfig); if (proxyHost) { // Request SSL certificate log(`Requesting SSL certificate for ${domain}...`); const sslSuccess = await requestSSLCertificate(token, proxyHost.id, domain); if (sslSuccess) { successCount++; log(`✓ Successfully configured ${domain}`, 'success'); } else { failCount++; log(`✗ Failed to configure SSL for ${domain}`, 'error'); } } else { failCount++; log(`✗ Failed to create proxy host for ${domain}`, 'error'); } // Small delay between requests await new Promise(resolve => setTimeout(resolve, 1000)); } console.log(''); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('📊 Configuration Summary'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log(`✅ Successful: ${successCount}`); console.log(`⚠️ Skipped: ${skipCount}`); console.log(`✗ Failed: ${failCount}`); console.log(`📋 Total: ${DOMAINS.length}`); console.log(''); if (failCount === 0) { log('All domains configured successfully!', 'success'); console.log(''); log('Note: SSL certificates may take 1-2 minutes to be issued by Let\'s Encrypt'); log('Run verification: bash scripts/nginx-proxy-manager/verify-ssl-config.sh'); } else { log('Some domains failed to configure. Check errors above.', 'error'); } } main().catch(error => { log(`Fatal error: ${error.message}`, 'error'); process.exit(1); });