#!/usr/bin/env node /** * Direct connection test to Omada Controller * Uses Node.js https module directly for better SSL control */ import https from 'https'; import { readFileSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; // Load environment variables const envPath = join(homedir(), '.env'); let envVars = {}; try { const envFile = readFileSync(envPath, 'utf8'); envFile.split('\n').forEach(line => { if (line.includes('=') && !line.trim().startsWith('#')) { const [key, ...values] = line.split('='); if (key && /^[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); } envVars[key.trim()] = value; } } }); } catch (error) { console.error('Error loading .env file:', error.message); process.exit(1); } const baseUrl = envVars.OMADA_CONTROLLER_URL || 'https://192.168.11.8:8043'; const username = envVars.OMADA_ADMIN_USERNAME || envVars.OMADA_API_KEY; const password = envVars.OMADA_ADMIN_PASSWORD || envVars.OMADA_API_SECRET; const siteId = envVars.OMADA_SITE_ID; const verifySSL = envVars.OMADA_VERIFY_SSL !== 'false'; if (!username || !password) { console.error('Error: Missing credentials'); console.error('Required: OMADA_ADMIN_USERNAME/OMADA_API_KEY and OMADA_ADMIN_PASSWORD/OMADA_API_SECRET'); process.exit(1); } // Parse base URL const urlObj = new URL(baseUrl); const hostname = urlObj.hostname; const port = urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80); console.log('=== Omada Controller Direct Connection Test ===\n'); console.log(`Controller URL: ${baseUrl}`); console.log(`Hostname: ${hostname}`); console.log(`Port: ${port}`); console.log(`Site ID: ${siteId || 'auto-detect'}`); console.log(`SSL Verification: ${verifySSL}\n`); // Create HTTPS agent const agent = new https.Agent({ rejectUnauthorized: verifySSL, }); // Function to make API request function apiRequest(method, path, data = null, token = null) { return new Promise((resolve, reject) => { const options = { hostname, port, path, method, agent, headers: { 'Content-Type': 'application/json', }, }; if (token) { options.headers['Authorization'] = `Bearer ${token}`; } const req = https.request(options, (res) => { let body = ''; res.on('data', (chunk) => { body += chunk; }); res.on('end', () => { try { const json = JSON.parse(body); if (res.statusCode >= 200 && res.statusCode < 300) { resolve(json); } else { reject(new Error(`HTTP ${res.statusCode}: ${body}`)); } } catch (e) { resolve({ raw: body, statusCode: res.statusCode }); } }); }); req.on('error', (error) => { reject(error); }); if (data) { req.write(JSON.stringify(data)); } req.end(); }); } async function test() { try { console.log('1. Testing login...'); const loginResponse = await apiRequest('POST', '/api/v2/login', { username, password, }); if (loginResponse.errorCode !== 0) { console.error(` ✗ Login failed: ${loginResponse.msg || 'Unknown error'}`); console.error(` Error Code: ${loginResponse.errorCode}`); process.exit(1); } const token = loginResponse.result?.token || loginResponse.token; if (!token) { console.error(' ✗ No token received'); process.exit(1); } console.log(' ✓ Login successful'); console.log(` Token: ${token.substring(0, 20)}...\n`); console.log('2. Getting site information...'); // Try different endpoint formats let sitesResponse; try { sitesResponse = await apiRequest('GET', '/api/v2/sites', null, token); } catch (e) { // If redirect, try with omadacId parameter try { sitesResponse = await apiRequest('GET', `/api/v2/sites?omadacId=${siteId}`, null, token); } catch (e2) { console.log(' Note: Sites endpoint may require different format'); sitesResponse = { errorCode: -1, msg: 'Could not query sites endpoint' }; } } if (sitesResponse.errorCode === 0 && sitesResponse.result) { const sites = Array.isArray(sitesResponse.result) ? sitesResponse.result : []; console.log(` ✓ Found ${sites.length} site(s)`); sites.forEach((site, index) => { console.log(` Site ${index + 1}: ${site.name} (${site.id})`); if (site.id === siteId) { console.log(` ^ Using this site`); } }); } else if (sitesResponse.statusCode === 302 || sitesResponse.raw === '') { console.log(' Note: Sites endpoint returned redirect (may require web interface)'); console.log(` Using provided site ID: ${siteId}`); } else { console.log(' Response:', JSON.stringify(sitesResponse, null, 2)); } console.log(''); const effectiveSiteId = siteId || (sitesResponse.result?.[0]?.id); if (effectiveSiteId) { console.log(`3. Using site ID: ${effectiveSiteId}\n`); console.log('4. Listing devices...'); let devicesResponse; try { devicesResponse = await apiRequest('GET', `/api/v2/sites/${effectiveSiteId}/devices`, null, token); } catch (e) { try { devicesResponse = await apiRequest('GET', `/api/v2/sites/${effectiveSiteId}/devices?omadacId=${effectiveSiteId}`, null, token); } catch (e2) { devicesResponse = { errorCode: -1, msg: 'Could not query devices endpoint' }; } } if (devicesResponse.errorCode === 0 && devicesResponse.result) { const devices = Array.isArray(devicesResponse.result) ? devicesResponse.result : []; console.log(` ✓ Found ${devices.length} device(s)`); if (devices.length > 0) { console.log('\n Devices:'); const routers = devices.filter(d => d.type === 'router' || d.type === 'Gateway'); const switches = devices.filter(d => d.type === 'switch' || d.type === 'Switch'); const aps = devices.filter(d => d.type === 'ap' || d.type === 'AccessPoint'); if (routers.length > 0) { console.log('\n ROUTERS:'); routers.forEach((device, index) => { const status = device.status === 1 ? '🟢 Online' : device.status === 0 ? '🔴 Offline' : '🟡 Unknown'; console.log(` ${index + 1}. ${device.name || device.mac || 'Unknown'}`); console.log(` Model: ${device.model || 'N/A'}`); console.log(` Status: ${status}`); console.log(` IP: ${device.ip || 'N/A'}`); console.log(` MAC: ${device.mac || 'N/A'}`); console.log(` Device ID: ${device.id || 'N/A'}`); console.log(''); }); } if (switches.length > 0) { console.log('\n SWITCHES:'); switches.forEach((device, index) => { const status = device.status === 1 ? '🟢 Online' : device.status === 0 ? '🔴 Offline' : '🟡 Unknown'; console.log(` ${index + 1}. ${device.name || device.mac || 'Unknown'}`); console.log(` Model: ${device.model || 'N/A'}`); console.log(` Status: ${status}`); console.log(` IP: ${device.ip || 'N/A'}`); console.log(` MAC: ${device.mac || 'N/A'}`); console.log(` Device ID: ${device.id || 'N/A'}`); console.log(''); }); } if (aps.length > 0) { console.log('\n ACCESS POINTS:'); aps.forEach((device, index) => { const status = device.status === 1 ? '🟢 Online' : device.status === 0 ? '🔴 Offline' : '🟡 Unknown'; console.log(` ${index + 1}. ${device.name || device.mac || 'Unknown'}`); console.log(` Model: ${device.model || 'N/A'}`); console.log(` Status: ${status}`); console.log(` IP: ${device.ip || 'N/A'}`); console.log(''); }); } const others = devices.filter(d => d.type !== 'router' && d.type !== 'Gateway' && d.type !== 'switch' && d.type !== 'Switch' && d.type !== 'ap' && d.type !== 'AccessPoint' ); if (others.length > 0) { console.log('\n OTHER DEVICES:'); others.forEach((device, index) => { const status = device.status === 1 ? '🟢 Online' : device.status === 0 ? '🔴 Offline' : '🟡 Unknown'; console.log(` ${index + 1}. ${device.name || device.mac || 'Unknown'} (${device.type || 'Unknown'})`); console.log(` Status: ${status}`); console.log(` IP: ${device.ip || 'N/A'}`); console.log(''); }); } } else { console.log(' No devices found'); } } else if (devicesResponse.statusCode === 302 || devicesResponse.raw === '') { console.log(' Note: Devices endpoint returned redirect (API may require different format)'); console.log(' Try accessing web interface at https://192.168.11.8:8043 to view devices'); } else { console.log(' Response:', JSON.stringify(devicesResponse, null, 2)); } console.log('5. Listing VLANs...'); const vlansResponse = await apiRequest('GET', `/api/v2/sites/${effectiveSiteId}/vlans`, null, token); if (vlansResponse.errorCode === 0 && vlansResponse.result) { const vlans = Array.isArray(vlansResponse.result) ? vlansResponse.result : []; console.log(` ✓ Found ${vlans.length} VLAN(s)`); if (vlans.length > 0) { console.log('\n VLANs:'); vlans.forEach((vlan, index) => { console.log(` ${index + 1}. VLAN ${vlan.vlanId || 'N/A'}: ${vlan.name || 'Unnamed'}`); if (vlan.subnet) console.log(` Subnet: ${vlan.subnet}`); if (vlan.gateway) console.log(` Gateway: ${vlan.gateway}`); console.log(''); }); } else { console.log(' No VLANs configured'); } } else { console.log(' Response:', JSON.stringify(vlansResponse, null, 2)); } } console.log('\n=== Connection test completed successfully! ==='); } catch (error) { console.error('\n=== Test failed ==='); console.error('Error:', error.message); if (error.stack) { console.error('\nStack trace:'); console.error(error.stack); } process.exit(1); } } test();