UNPKG

pbx-admin

Version:

CLI tool to manage PBX administrators on multiple Vodia PBX servers

400 lines (360 loc) 15.4 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const axios = require('axios'); const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); const jmespath = require('jmespath'); // Configuration file path const CONFIG_FILE = path.join(require('os').homedir(), '.pbx-admin.conf'); // Load configuration function loadConfig() { if (!fs.existsSync(CONFIG_FILE)) { console.error(`Error: Configuration file '${CONFIG_FILE}' not found.`); process.exit(1); } const config = { username: null, password: null, server: null }; const content = fs.readFileSync(CONFIG_FILE, 'utf-8'); content.split('\n').forEach(line => { if (!line.trim()) return; const match = line.match(/^([^:]+):\s*(.+)$/) || line.match(/^([^:]+):(.+)$/); if (match) { const key = match[1].trim(); const value = match[2].trim(); if (key === 'username') config.username = value; if (key === 'password') config.password = value; if (key === 'server') config.server = value.split(' ').filter(Boolean); } }); if (!config.username || !config.password || !config.server || config.server.length === 0) { console.error('Error: Missing required variables in the configuration file. Ensure "username", "password", and "server" are set.'); process.exit(1); } return config; } // Make API requests to all servers async function makeRequests(hostnames, options) { const results = []; for (const hostname of hostnames) { try { const url = `${hostname}${options.path}`; const response = await axios({ method: options.method || 'GET', url, auth: { username: options.auth.username, password: options.auth.password }, data: options.data, httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) }); results.push({ hostname, success: true, data: response.data }); } catch (error) { results.push({ hostname, success: false, error: error.response ? error.response.data : error.message }); } } return results; } // Main function async function main() { const config = loadConfig(); const argv = yargs(hideBin(process.argv)) .command('add', 'Add a new PBX administrator', (yargs) => { return yargs .option('username', { describe: 'Admin username', type: 'string', required: true }) .option('password', { describe: 'Admin password', type: 'string', required: true }) .option('display', { describe: 'Admin display name', type: 'string', default: null }) .option('email', { describe: 'Admin email', type: 'string', default: null }) .option('ip', { describe: 'IP restrictions', type: 'string', default: null }) .option('api', { describe: 'API access flag', type: 'boolean', default: false }) .option('phone', { describe: 'Admin phone number', type: 'string', default: null }); }) .command('delete <username>', 'Delete the specified PBX administrator', (yargs) => { return yargs .positional('username', { describe: 'Admin username to delete', type: 'string' }); }) .command('modify', 'Modify an existing PBX administrator', (yargs) => { return yargs .option('username', { describe: 'Admin username', type: 'string', required: true }) .option('password', { describe: 'New admin password', type: 'string' }) .option('display', { describe: 'New admin display name', type: 'string' }) .option('email', { describe: 'New admin email', type: 'string' }) .option('ip', { describe: 'New IP restrictions', type: 'string' }) .option('api', { describe: 'New API access flag', type: 'boolean' }) .option('phone', { describe: 'New admin phone number', type: 'string' }) .check(argv => { if (!argv.password && !argv.display && !argv.email && !argv.ip && argv.api === undefined && !argv.phone) { throw new Error('At least one modification option must be provided'); } return true; }); }) .command('list_versions', 'List version, build date and uptime of all servers') .command('system_usage', 'Get system usage statistics for all PBXs', () => {}) .command('config_global_setting', 'Set global setting for the PBXs', (yargs) => { return yargs .option('config_variable', { describe: 'Configuration variable name', type: 'string', required: true }) .option('config_value', { describe: 'Configuration value', type: 'string', required: true }); }) .command('blocked_ips', 'List all blocked IP addresses with duration in hours', () => {}) .demandCommand(1, 'You need to specify a command') .help() .argv; const command = argv._[0]; try { switch (command) { case 'add': const adminData = { name: argv.username, display: argv.display || argv.username, password: argv.password, adr: argv.ip || undefined, email: argv.email || undefined, api: argv.api, phone: argv.phone || undefined }; const results = await makeRequests(config.server, { path: '/rest/system/administrators', method: 'POST', auth: { username: config.username, password: config.password }, data: adminData }); results.forEach(result => { if (result.success) { console.log(`Admin ${argv.username} added to ${result.hostname}`); } else { console.error(`Failed to add admin to ${result.hostname}:`, result.error); } }); break; case 'delete': const deleteResults = await makeRequests(config.server, { path: `/rest/system/administrators?name=${argv.username}`, method: 'DELETE', auth: { username: config.username, password: config.password } }); deleteResults.forEach(result => { if (result.success) { console.log(`Admin ${argv.username} deleted from ${result.hostname}`); } else { console.error(`Failed to delete admin from ${result.hostname}:`, result.error); } }); break; case 'modify': const modifyData = { name: argv.username }; if (argv.password) modifyData.password = argv.password; if (argv.display) modifyData.display = argv.display; if (argv.email) modifyData.email = argv.email; if (argv.ip) modifyData.adr = argv.ip; if (argv.api !== undefined) modifyData.api = argv.api; if (argv.phone) modifyData.phone = argv.phone; const modifyResults = await makeRequests(config.server, { path: '/rest/system/administrators', method: 'POST', auth: { username: config.username, password: config.password }, data: modifyData }); modifyResults.forEach(result => { if (result.success) { console.log(`Admin ${argv.username} modified on ${result.hostname}`); } else { console.error(`Failed to modify admin on ${result.hostname}:`, result.error); } }); break; case 'list_versions': const versionResults = await makeRequests(config.server, { path: '/rest/system/status', auth: { username: config.username, password: config.password } }); versionResults.forEach(result => { if (result.success) { const version = result.data.version; const buildDate = result.data.build_date; const uptimeDays = (result.data.uptime / 86400).toFixed(1); console.log(`PBX ${result.hostname}: Version ${version}, Build ${buildDate}, Uptime ${uptimeDays} days`); } else { console.error(`Failed to get version from ${result.hostname}:`, result.error); } }); break; case 'system_usage': const usageResults = await makeRequests(config.server, { path: '/rest/system/usage', auth: { username: config.username, password: config.password } }); console.log(`PBX,Tenant,Extensions,Call Queues,Conferences`); usageResults.forEach(result => { if (result.success) { result.data.forEach(pbx => { const name = pbx.name; // Keep full domain name const extensions = pbx.extensions || 0; const acd = pbx.acd || 0; const conferences = pbx.conf || 0; console.log(`"${result.hostname}","${name}",${extensions},${acd},${conferences}`); }); } else { console.error(`Failed to get usage from ${result.hostname}:`, result.error); } }); break; case 'config_global_setting': const configData = { [argv.config_variable]: argv.config_value }; const configResults = await makeRequests(config.server, { path: '/rest/system/config', method: 'POST', auth: { username: config.username, password: config.password }, data: configData }); configResults.forEach(result => { if (result.success) { console.log(`Configuration ${argv.config_variable} set to ${argv.config_value} on ${result.hostname}`); } else { console.error(`Failed to set configuration on ${result.hostname}:`, result.error); } }); break; case 'blocked_ips': const blockedResults = await makeRequests(config.server, { path: '/rest/system/access', auth: { username: config.username, password: config.password } }); blockedResults.forEach(result => { if (result.success) { const blockedIps = result.data.filter(item => item.enabled === false); if (blockedIps.length === 0) { console.log(`No blocked IPs found on ${result.hostname}`); return; } console.log(`\nBlocked IPs on ${result.hostname}:`); console.log('='.repeat(80)); console.log('IP Address'.padEnd(20) + 'Type'.padEnd(10) + 'Port'.padEnd(10) + 'Duration (hrs)'.padEnd(15) + 'Comment'); console.log('-'.repeat(80)); blockedIps.forEach(ip => { const durationHours = ip.duration ? (ip.duration / 3600).toFixed(2) : 'N/A'; console.log( (ip.adr || '').padEnd(20) + (ip.type || '').padEnd(10) + (ip.port || '0').toString().padEnd(10) + durationHours.padEnd(15) + (ip.comment || '') ); }); } else { console.error(`Failed to get blocked IPs from ${result.hostname}:`, result.error); } }); break; default: console.error('Error: Invalid command'); process.exit(1); } } catch (error) { console.error('Error:', error.message); process.exit(1); } } main();