'use strict'; 'require baseclass'; 'require rpc'; /** * CrowdSec Dashboard API * Package: luci-app-crowdsec-dashboard * RPCD object: luci.crowdsec-dashboard * CrowdSec Core: 1.7.4+ */ // Version: 0.7.0 var callStatus = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'status', expect: { } }); var callDecisions = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'decisions', expect: { decisions: [] } }); var callAlerts = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'alerts', expect: { alerts: [] } }); var callBouncers = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'bouncers', expect: { bouncers: [] } }); var callMetrics = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'metrics', expect: { } }); var callMachines = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'machines', expect: { machines: [] } }); var callHub = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'hub', expect: { } }); var callStats = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'stats', expect: { } }); var callGetOverview = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'get_overview', expect: { } }); var callSecuboxLogs = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'secubox_logs', expect: { } }); var callCollectDebug = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'collect_debug', expect: { } }); var callBan = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'ban', params: ['ip', 'duration', 'reason'], expect: { } }); var callUnban = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'unban', params: ['ip'], expect: { } }); // CrowdSec v1.7.4+ features var callWAFStatus = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'waf_status', expect: { } }); var callMetricsConfig = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'metrics_config', expect: { } }); var callConfigureMetrics = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'configure_metrics', params: ['enable'], expect: { } }); var callCollections = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'collections', expect: { collections: [] } }); var callInstallCollection = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'install_collection', params: ['collection'], expect: { } }); var callRemoveCollection = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'remove_collection', params: ['collection'], expect: { } }); var callUpdateHub = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'update_hub', expect: { } }); var callRegisterBouncer = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'register_bouncer', params: ['bouncer_name'], expect: { } }); var callDeleteBouncer = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'delete_bouncer', params: ['bouncer_name'], expect: { } }); // Firewall Bouncer Management var callFirewallBouncerStatus = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'firewall_bouncer_status', expect: { } }); var callControlFirewallBouncer = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'control_firewall_bouncer', params: ['action'], expect: { } }); var callFirewallBouncerConfig = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'firewall_bouncer_config', expect: { } }); var callUpdateFirewallBouncerConfig = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'update_firewall_bouncer_config', params: ['key', 'value'], expect: { } }); var callNftablesStats = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'nftables_stats', expect: { } }); // Wizard Methods var callCheckWizardNeeded = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'check_wizard_needed', expect: { } }); var callWizardState = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'wizard_state', expect: { } }); var callRepairLapi = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'repair_lapi', expect: { } }); var callRepairCapi = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'repair_capi', expect: { } }); var callResetWizard = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'reset_wizard', expect: { } }); // Console Methods var callConsoleStatus = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'console_status', expect: { } }); var callConsoleEnroll = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'console_enroll', params: ['key', 'name'], expect: { } }); var callConsoleDisable = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'console_disable', expect: { } }); var callServiceControl = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'service_control', params: ['action'], expect: { } }); // Acquisition Methods var callConfigureAcquisition = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'configure_acquisition', params: ['syslog_enabled', 'firewall_enabled', 'ssh_enabled', 'http_enabled', 'syslog_path'], expect: { } }); var callAcquisitionConfig = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'acquisition_config', expect: { } }); var callAcquisitionMetrics = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'acquisition_metrics', expect: { } }); // Health Check & CAPI Methods var callHealthCheck = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'health_check', expect: { } }); var callCapiMetrics = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'capi_metrics', expect: { } }); var callHubAvailable = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'hub_available', expect: { } }); var callInstallHubItem = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'install_hub_item', params: ['item_type', 'item_name'], expect: { } }); var callRemoveHubItem = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'remove_hub_item', params: ['item_type', 'item_name'], expect: { } }); var callGetSettings = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'get_settings', expect: { } }); var callSaveSettings = rpc.declare({ object: 'luci.crowdsec-dashboard', method: 'save_settings', params: ['enrollment_key', 'machine_name', 'auto_enroll'], expect: { } }); function formatDuration(seconds) { if (!seconds) return 'N/A'; if (seconds < 60) return seconds + 's'; if (seconds < 3600) return Math.floor(seconds / 60) + 'm'; if (seconds < 86400) return Math.floor(seconds / 3600) + 'h'; return Math.floor(seconds / 86400) + 'd'; } function formatDate(dateStr) { if (!dateStr) return 'N/A'; try { var date = new Date(dateStr); return date.toLocaleString(); } catch(e) { return dateStr; } } function isValidIP(ip) { if (!ip) return false; // IPv4 regex var ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; // IPv6 regex (simplified) var ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; return ipv4Regex.test(ip) || ipv6Regex.test(ip); } function parseScenario(scenario) { if (!scenario) return 'N/A'; // Extract human-readable part from scenario name // e.g., "crowdsecurity/ssh-bruteforce" -> "SSH Bruteforce" var parts = scenario.split('/'); var name = parts[parts.length - 1]; // Convert dash-separated to title case return name.split('-').map(function(word) { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); } function getCountryFlag(countryCode) { if (!countryCode || countryCode === 'N/A') return ''; // Convert country code to flag emoji // e.g., "US" -> "🇺🇸" var code = countryCode.toUpperCase(); if (code.length !== 2) return ''; var codePoints = []; for (var i = 0; i < code.length; i++) { codePoints.push(0x1F1E6 - 65 + code.charCodeAt(i)); } return String.fromCodePoint.apply(null, codePoints); } function formatRelativeTime(dateStr) { if (!dateStr) return 'N/A'; try { var date = new Date(dateStr); var now = new Date(); var seconds = Math.floor((now - date) / 1000); if (seconds < 60) return seconds + 's ago'; if (seconds < 3600) return Math.floor(seconds / 60) + 'm ago'; if (seconds < 86400) return Math.floor(seconds / 3600) + 'h ago'; if (seconds < 2592000) return Math.floor(seconds / 86400) + 'd ago'; return Math.floor(seconds / 2592000) + 'mo ago'; } catch(e) { return dateStr; } } return baseclass.extend({ getStatus: callStatus, getDecisions: callDecisions, getAlerts: callAlerts, getBouncers: callBouncers, getMetrics: callMetrics, getMachines: callMachines, getHub: callHub, getStats: callStats, getOverview: callGetOverview, getSecuboxLogs: callSecuboxLogs, collectDebugSnapshot: callCollectDebug, addBan: callBan, removeBan: callUnban, // CrowdSec v1.7.4+ features getWAFStatus: callWAFStatus, getMetricsConfig: callMetricsConfig, configureMetrics: callConfigureMetrics, getCollections: callCollections, installCollection: callInstallCollection, removeCollection: callRemoveCollection, updateHub: callUpdateHub, registerBouncer: callRegisterBouncer, deleteBouncer: callDeleteBouncer, // Firewall Bouncer Management getFirewallBouncerStatus: callFirewallBouncerStatus, controlFirewallBouncer: callControlFirewallBouncer, getFirewallBouncerConfig: callFirewallBouncerConfig, updateFirewallBouncerConfig: callUpdateFirewallBouncerConfig, getNftablesStats: callNftablesStats, // Wizard Methods checkWizardNeeded: callCheckWizardNeeded, getWizardState: callWizardState, repairLapi: callRepairLapi, repairCapi: callRepairCapi, resetWizard: callResetWizard, // Console Methods getConsoleStatus: callConsoleStatus, consoleEnroll: callConsoleEnroll, consoleDisable: callConsoleDisable, // Service Control serviceControl: callServiceControl, // Acquisition Methods configureAcquisition: callConfigureAcquisition, getAcquisitionConfig: callAcquisitionConfig, getAcquisitionMetrics: callAcquisitionMetrics, // Health Check & CAPI Methods getHealthCheck: callHealthCheck, getCapiMetrics: callCapiMetrics, getHubAvailable: callHubAvailable, installHubItem: callInstallHubItem, removeHubItem: callRemoveHubItem, // Settings Management getSettings: callGetSettings, saveSettings: callSaveSettings, formatDuration: formatDuration, formatDate: formatDate, formatRelativeTime: formatRelativeTime, isValidIP: isValidIP, parseScenario: parseScenario, getCountryFlag: getCountryFlag, // Aliases for compatibility banIP: callBan, unbanIP: callUnban, getDashboardData: function() { return Promise.all([ callStatus(), callStats(), callDecisions(), callAlerts() ]).then(function(results) { // Check if any result has an error (service not running) var status = results[0] || {}; var stats = results[1] || {}; var decisionsRaw = results[2] || []; var alerts = results[3] || []; // Flatten alerts->decisions structure var decisions = []; if (Array.isArray(decisionsRaw)) { decisionsRaw.forEach(function(alert) { if (alert.decisions && Array.isArray(alert.decisions)) { decisions = decisions.concat(alert.decisions); } }); } return { status: status, stats: (stats.error) ? {} : stats, decisions: decisions, alerts: alerts, error: stats.error || null }; }); } });