diff --git a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/common.css b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/common.css index 2f0626c3..0f95f2fa 100644 --- a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/common.css +++ b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/common.css @@ -27,45 +27,25 @@ /* === Import Fonts === */ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap'); -/* === Design System Variables === */ +/* === Design System Variables - SecuBox Theme Integration === */ :root { - /* Light Mode */ - --sh-text-primary: #0f172a; - --sh-text-secondary: #475569; - --sh-bg-primary: #ffffff; - --sh-bg-secondary: #f8fafc; - --sh-bg-tertiary: #f1f5f9; - --sh-bg-card: #ffffff; - --sh-border: #e2e8f0; - --sh-hover-bg: #f8fafc; - --sh-hover-shadow: rgba(0, 0, 0, 0.1); - --sh-primary: #6366f1; - --sh-primary-end: #8b5cf6; - --sh-shadow: rgba(0, 0, 0, 0.08); - --sh-success: #22c55e; - --sh-danger: #ef4444; - --sh-warning: #f59e0b; - --sh-info: #3b82f6; -} - -[data-theme="dark"] { - /* Dark Mode (Demo-inspired) */ - --sh-text-primary: #fafafa; - --sh-text-secondary: #a0a0b0; - --sh-bg-primary: #0a0a0f; - --sh-bg-secondary: #12121a; - --sh-bg-tertiary: #1a1a24; - --sh-bg-card: #12121a; - --sh-border: #2a2a35; - --sh-hover-bg: #1a1a24; - --sh-hover-shadow: rgba(0, 0, 0, 0.6); - --sh-primary: #6366f1; - --sh-primary-end: #8b5cf6; - --sh-shadow: rgba(0, 0, 0, 0.4); - --sh-success: #22c55e; - --sh-danger: #ef4444; - --sh-warning: #f59e0b; - --sh-info: #3b82f6; + /* Map to SecuBox global theme variables with fallbacks */ + --sh-text-primary: var(--cyber-text-primary, #e2e8f0); + --sh-text-secondary: var(--cyber-text-secondary, #94a3b8); + --sh-bg-primary: var(--cyber-bg-primary, #0a0e27); + --sh-bg-secondary: var(--cyber-bg-secondary, #151932); + --sh-bg-tertiary: var(--cyber-bg-tertiary, #1e2139); + --sh-bg-card: var(--cyber-surface, #252b4a); + --sh-border: var(--cyber-glass-border, rgba(255, 255, 255, 0.1)); + --sh-hover-bg: var(--cyber-surface-light, #2d3454); + --sh-hover-shadow: var(--cyber-glass-shadow, 0 8px 32px rgba(0, 0, 0, 0.37)); + --sh-primary: var(--cyber-accent-primary, #667eea); + --sh-primary-end: var(--cyber-accent-primary-end, #764ba2); + --sh-shadow: var(--cyber-shadow-soft, 0 8px 24px rgba(2, 6, 23, 0.35)); + --sh-success: var(--cyber-success, #10b981); + --sh-danger: var(--cyber-danger, #ef4444); + --sh-warning: var(--cyber-warning, #f59e0b); + --sh-info: var(--cyber-info, #06b6d4); } /* === Global Typography === */ diff --git a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/dashboard.css b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/dashboard.css index 75b9a0e3..2f755a2c 100644 --- a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/dashboard.css +++ b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/dashboard.css @@ -56,33 +56,35 @@ body > .main > .main-right, max-width: 100% !important; } +/* CrowdSec uses SecuBox global theme variables */ :root { - --cs-bg-primary: #0a0e14; - --cs-bg-secondary: #151b23; - --cs-bg-tertiary: #1e2632; - --cs-bg-card: linear-gradient(145deg, #151b23 0%, #1a2231 100%); - --cs-border: #2a3444; - --cs-border-glow: rgba(0, 212, 170, 0.3); - - --cs-text-primary: #e6edf3; - --cs-text-secondary: #8b949e; - --cs-text-muted: #6e7681; - - --cs-accent-green: #00d4aa; - --cs-accent-green-glow: rgba(0, 212, 170, 0.4); - --cs-accent-red: #ff6b6b; - --cs-accent-red-glow: rgba(255, 107, 107, 0.4); - --cs-accent-orange: #ffa94d; - --cs-accent-blue: #4dabf7; - --cs-accent-purple: #b197fc; - - --cs-font-mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace; - --cs-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; - - --cs-radius: 8px; - --cs-radius-lg: 12px; - --cs-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); - --cs-shadow-glow: 0 0 20px var(--cs-accent-green-glow); + /* Map CrowdSec variables to SecuBox theme */ + --cs-bg-primary: var(--cyber-bg-primary, #0a0e27); + --cs-bg-secondary: var(--cyber-bg-secondary, #151932); + --cs-bg-tertiary: var(--cyber-bg-tertiary, #1e2139); + --cs-bg-card: var(--cyber-surface, #252b4a); + --cs-border: var(--cyber-glass-border, rgba(255, 255, 255, 0.1)); + --cs-border-glow: var(--cyber-accent-primary, #667eea); + + --cs-text-primary: var(--cyber-text-primary, #e2e8f0); + --cs-text-secondary: var(--cyber-text-secondary, #94a3b8); + --cs-text-muted: var(--cyber-text-muted, #64748b); + + --cs-accent-green: var(--cyber-success, #10b981); + --cs-accent-green-glow: var(--cyber-success-soft, rgba(16, 185, 129, 0.12)); + --cs-accent-red: var(--cyber-danger, #ef4444); + --cs-accent-red-glow: var(--cyber-danger-soft, rgba(239, 68, 68, 0.15)); + --cs-accent-orange: var(--cyber-warning, #f59e0b); + --cs-accent-blue: var(--cyber-info, #06b6d4); + --cs-accent-purple: var(--cyber-accent-tertiary, #8b5cf6); + + --cs-font-mono: var(--cyber-font-mono, 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace); + --cs-font-sans: var(--cyber-font-body, 'Inter', -apple-system, BlinkMacSystemFont, sans-serif); + + --cs-radius: var(--cyber-radius-sm, 10px); + --cs-radius-lg: var(--cyber-radius-md, 16px); + --cs-shadow: var(--cyber-shadow-soft, 0 8px 24px rgba(2, 6, 23, 0.35)); + --cs-shadow-glow: var(--cyber-glass-shadow, 0 8px 32px rgba(0, 0, 0, 0.37)); } /* Base styles */ diff --git a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/soc.css b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/soc.css index 41053ec6..c9c31a1c 100644 --- a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/soc.css +++ b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/soc.css @@ -1,17 +1,18 @@ /* CrowdSec SOC Dashboard - Minimal Professional Theme */ -/* Version: 1.0.0 - SOC Compliant */ +/* Version: 1.1.0 - SecuBox Theme Integration */ :root { - --soc-bg: #0d1117; - --soc-surface: #161b22; - --soc-border: #30363d; - --soc-text: #c9d1d9; - --soc-text-muted: #8b949e; - --soc-primary: #58a6ff; - --soc-success: #3fb950; - --soc-warning: #d29922; - --soc-danger: #f85149; - --soc-info: #79c0ff; + /* Map SOC variables to SecuBox global theme */ + --soc-bg: var(--cyber-bg-primary, #0a0e27); + --soc-surface: var(--cyber-bg-secondary, #151932); + --soc-border: var(--cyber-glass-border, rgba(255, 255, 255, 0.1)); + --soc-text: var(--cyber-text-primary, #e2e8f0); + --soc-text-muted: var(--cyber-text-secondary, #94a3b8); + --soc-primary: var(--cyber-accent-primary, #667eea); + --soc-success: var(--cyber-success, #10b981); + --soc-warning: var(--cyber-warning, #f59e0b); + --soc-danger: var(--cyber-danger, #ef4444); + --soc-info: var(--cyber-info, #06b6d4); } /* Hide LuCI sidebar for full-width SOC view */ diff --git a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/view/crowdsec-dashboard/settings.js b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/view/crowdsec-dashboard/settings.js index 73e3e53f..52fd1c5a 100644 --- a/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/view/crowdsec-dashboard/settings.js +++ b/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/view/crowdsec-dashboard/settings.js @@ -16,6 +16,7 @@ return view.extend({ status: {}, machines: [], collections: [], + settings: {}, load: function() { return Promise.all([ @@ -23,6 +24,7 @@ return view.extend({ api.getMachines(), api.getCollections(), api.getAcquisitionConfig(), + api.getSettings(), uci.load('crowdsec-dashboard') ]); }, @@ -36,6 +38,7 @@ return view.extend({ this.collections = collectionsData.collections || []; if (this.collections.collections) this.collections = this.collections.collections; this.acquisition = data[3] || {}; + this.settings = data[4] || {}; document.body.classList.add('cs-fullwidth'); @@ -51,6 +54,14 @@ return view.extend({ ]), E('div', { 'class': 'cs-card-body' }, this.renderServiceControl()) ]), + E('div', { 'class': 'cs-card' }, [ + E('div', { 'class': 'cs-card-header' }, [ + 'Console Enrollment', + E('span', { 'class': 'cs-severity ' + (this.status.capi_enrolled ? 'low' : 'medium') }, + this.status.capi_enrolled ? 'ENROLLED' : 'NOT ENROLLED') + ]), + E('div', { 'class': 'cs-card-body', 'id': 'console-enrollment' }, this.renderConsoleEnrollment()) + ]), E('div', { 'class': 'cs-grid-2' }, [ E('div', { 'class': 'cs-card' }, [ E('div', { 'class': 'cs-card-header' }, 'Acquisition Sources'), @@ -262,6 +273,132 @@ return view.extend({ })); }, + renderConsoleEnrollment: function() { + var self = this; + var enrolled = this.status.capi_enrolled; + var savedKey = this.settings.enrollment_key || ''; + var machineName = this.settings.machine_name || ''; + + return E('div', {}, [ + E('p', { 'style': 'color: var(--cs-text-muted); margin-bottom: 16px;' }, + 'Enroll in CrowdSec Console to receive community blocklists and share threat intelligence.'), + E('div', { 'style': 'display: grid; gap: 12px; margin-bottom: 16px;' }, [ + E('div', {}, [ + E('label', { 'style': 'display: block; margin-bottom: 4px; color: var(--cs-text-muted);' }, 'Enrollment Key'), + E('input', { + 'type': 'text', + 'id': 'enrollment-key', + 'class': 'cs-input', + 'value': savedKey, + 'placeholder': 'Enter your enrollment key from app.crowdsec.net', + 'style': 'width: 100%; padding: 8px 12px; border: 1px solid var(--cs-border); border-radius: 4px; background: var(--cs-bg); color: var(--cs-text);' + }) + ]), + E('div', {}, [ + E('label', { 'style': 'display: block; margin-bottom: 4px; color: var(--cs-text-muted);' }, 'Machine Name (optional)'), + E('input', { + 'type': 'text', + 'id': 'machine-name', + 'class': 'cs-input', + 'value': machineName, + 'placeholder': 'Custom name for this machine', + 'style': 'width: 100%; padding: 8px 12px; border: 1px solid var(--cs-border); border-radius: 4px; background: var(--cs-bg); color: var(--cs-text);' + }) + ]) + ]), + E('div', { 'style': 'display: flex; gap: 8px; flex-wrap: wrap;' }, [ + E('button', { + 'class': 'cs-btn primary', + 'click': function() { self.saveAndEnroll(); } + }, enrolled ? 'Re-enroll' : 'Save & Enroll'), + E('button', { + 'class': 'cs-btn', + 'click': function() { self.saveSettings(); } + }, 'Save Only'), + enrolled ? E('button', { + 'class': 'cs-btn danger', + 'click': function() { self.disableConsole(); } + }, 'Disable') : E('span') + ]), + E('div', { 'class': 'cs-health', 'style': 'margin-top: 16px;' }, [ + E('div', { 'class': 'cs-health-item' }, [ + E('div', { 'class': 'cs-health-icon ' + (enrolled ? 'ok' : 'error') }, enrolled ? '\u2713' : '\u2717'), + E('div', {}, [ + E('div', { 'class': 'cs-health-label' }, 'Console Status'), + E('div', { 'class': 'cs-health-value' }, enrolled ? 'Enrolled and active' : 'Not enrolled') + ]) + ]), + E('div', { 'class': 'cs-health-item' }, [ + E('div', { 'class': 'cs-health-icon ' + (savedKey ? 'ok' : 'warn') }, savedKey ? '\u2713' : '!'), + E('div', {}, [ + E('div', { 'class': 'cs-health-label' }, 'Key Saved'), + E('div', { 'class': 'cs-health-value' }, savedKey ? 'Key stored in config' : 'No key saved') + ]) + ]) + ]) + ]); + }, + + saveSettings: function() { + var self = this; + var key = document.getElementById('enrollment-key').value.trim(); + var name = document.getElementById('machine-name').value.trim(); + + api.saveSettings(key, name, '0').then(function(r) { + if (r.success) { + self.showToast('Settings saved', 'success'); + } else { + self.showToast('Failed to save: ' + (r.error || 'Unknown'), 'error'); + } + }).catch(function(e) { + self.showToast('Error: ' + e.message, 'error'); + }); + }, + + saveAndEnroll: function() { + var self = this; + var key = document.getElementById('enrollment-key').value.trim(); + var name = document.getElementById('machine-name').value.trim(); + + if (!key) { + self.showToast('Please enter an enrollment key', 'error'); + return; + } + + // First save the settings + api.saveSettings(key, name, '1').then(function(r) { + if (!r.success) { + self.showToast('Failed to save settings', 'error'); + return; + } + // Then enroll + return api.consoleEnroll(key, name); + }).then(function(r) { + if (r && r.success) { + self.showToast('Enrolled successfully!', 'success'); + setTimeout(function() { location.reload(); }, 2000); + } else if (r) { + self.showToast('Enrollment failed: ' + (r.error || 'Unknown'), 'error'); + } + }).catch(function(e) { + self.showToast('Error: ' + e.message, 'error'); + }); + }, + + disableConsole: function() { + var self = this; + if (!confirm('Disable CrowdSec Console enrollment?')) return; + + api.consoleDisable().then(function(r) { + if (r.success) { + self.showToast('Console disabled', 'success'); + setTimeout(function() { location.reload(); }, 1500); + } else { + self.showToast('Failed: ' + (r.error || 'Unknown'), 'error'); + } + }); + }, + serviceAction: function(action) { var self = this; api.serviceControl(action).then(function(r) { diff --git a/package/secubox/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js b/package/secubox/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js index f1ec16e4..a1875049 100644 --- a/package/secubox/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js +++ b/package/secubox/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js @@ -303,10 +303,12 @@ return view.extend({ if (!module.installed) { actions.push( E('button', { - 'class': 'secubox-btn secubox-btn-secondary secubox-btn-sm sb-cascade-item', + 'class': 'secubox-btn secubox-btn-primary secubox-btn-sm sb-cascade-item', 'data-cascade-action': 'install', 'data-module-target': module.id, - 'disabled': true + 'click': function(ev) { + self.installModule(module, ev.target); + } }, [ E('span', { 'class': 'sb-cascade-label' }, '📥 Install') ]) @@ -438,6 +440,43 @@ return view.extend({ }); }, + installModule: function(module, button) { + var self = this; + var originalText = button ? button.textContent : ''; + + if (button) { + button.disabled = true; + button.textContent = _('Installing...'); + } + + ui.showModal(_('Installing Module'), [ + E('p', { 'class': 'spinning' }, _('Installing ') + module.name + '...') + ]); + + return API.installAppstoreApp(module.id).then(function(result) { + ui.hideModal(); + if (result && result.success) { + ui.addNotification(null, E('p', module.name + ' ' + _('installed successfully')), 'info'); + return self.refreshData().then(function() { + self.updateModulesGrid(); + }); + } else { + ui.addNotification(null, E('p', _('Installation failed: ') + (result.error || result.details || _('Unknown error'))), 'error'); + if (button) { + button.disabled = false; + button.textContent = originalText; + } + } + }).catch(function(err) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Installation error: ') + err.message), 'error'); + if (button) { + button.disabled = false; + button.textContent = originalText; + } + }); + }, + // DEPRECATED: Keeping for backward compatibility startModule: function(module) { return this.enableModule(module);