secubox-openwrt/package/secubox/luci-app-service-registry/htdocs/luci-static/resources/view/service-registry/landing.js
CyberMind-FR 9acab29c34 feat(v0.17): P2P Mesh Recovery, MITM Analytics, Swiss Army Knife
Major features:
- P2P Mesh distributed recovery infrastructure with blockchain catalog
- MITM analytics proxy for external access monitoring (IP, country, scans)
- SecuBox Swiss unified CLI tool for management & recovery
- Python remote management console (secubox-console)
- Multi-theme landing page generator (mirrorbox, cyberpunk, minimal, terminal, light)
- Service Registry enhancements with health check and network diagnostics
- Services page modernization with Service Registry API integration

New components:
- secubox-swiss: Swiss Army Knife unified management tool
- secubox-mesh: P2P mesh networking and sync
- secubox-recover: Snapshot, profiles, rollback, reborn scripts
- secubox-console: Python remote management app
- secubox_analytics.py: MITM traffic analysis addon

Fixes:
- Service Registry ACL permissions for secubox services page
- Port status display (firewall_open detection)
- RPC response handling for list_services

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 19:46:28 +01:00

401 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
'require view';
'require dom';
'require ui';
'require form';
'require fs';
'require service-registry/api as api';
return view.extend({
title: _('Landing Page'),
load: function() {
return Promise.all([
api.getLandingConfig(),
api.getPublishedServices()
]);
},
render: function(data) {
var self = this;
var config = data[0] || {};
var services = data[1] || [];
// Category icons
var categoryIcons = {
'web': '🌐',
'network': '📡',
'security': '🛡️',
'storage': '💾',
'media': '🎬',
'communication': '💬',
'development': '🔧',
'monitoring': '📊',
'ai': '🤖',
'database': '🗄️',
'vpn': '🔒',
'dns': '🌍',
'proxy': '🔀',
'default': '📦'
};
var container = E('div', { 'class': 'service-landing-page' }, [
E('style', {}, this.getStyles()),
// Hero Section
E('div', { 'class': 'landing-hero' }, [
E('div', { 'class': 'hero-icon' }, '🚀'),
E('h1', {}, 'Service Landing Page'),
E('p', { 'class': 'hero-desc' },
'Configure the public landing page that displays all published services with QR codes'
)
]),
// Status Cards
E('div', { 'class': 'status-grid' }, [
E('div', { 'class': 'status-card' + (config.exists ? ' active' : ' inactive') }, [
E('div', { 'class': 'status-icon' }, config.exists ? '✅' : '❌'),
E('div', { 'class': 'status-info' }, [
E('div', { 'class': 'status-label' }, 'Page Status'),
E('div', { 'class': 'status-value' }, config.exists ? 'Generated' : 'Not Generated')
])
]),
E('div', { 'class': 'status-card' }, [
E('div', { 'class': 'status-icon' }, '📡'),
E('div', { 'class': 'status-info' }, [
E('div', { 'class': 'status-label' }, 'Published Services'),
E('div', { 'class': 'status-value' }, String(services.length))
])
]),
E('div', { 'class': 'status-card' }, [
E('div', { 'class': 'status-icon' }, '🕐'),
E('div', { 'class': 'status-info' }, [
E('div', { 'class': 'status-label' }, 'Last Updated'),
E('div', { 'class': 'status-value' },
config.modified ? new Date(config.modified * 1000).toLocaleDateString() : 'Never'
)
])
]),
E('div', { 'class': 'status-card' }, [
E('div', { 'class': 'status-icon' }, '🔄'),
E('div', { 'class': 'status-info' }, [
E('div', { 'class': 'status-label' }, 'Auto-Regenerate'),
E('div', { 'class': 'status-value' }, config.auto_regen ? 'Enabled' : 'Disabled')
])
])
]),
// Actions Section
E('div', { 'class': 'actions-section' }, [
E('h2', {}, ['⚡', ' Quick Actions']),
E('div', { 'class': 'actions-grid' }, [
E('button', {
'class': 'action-btn primary',
'click': ui.createHandlerFn(self, 'handleRegenerate')
}, ['🔄', ' Regenerate Page']),
config.exists ? E('a', {
'class': 'action-btn',
'href': '/secubox-services.html',
'target': '_blank'
}, ['🌐', ' View Live Page']) : '',
config.exists ? E('button', {
'class': 'action-btn',
'click': ui.createHandlerFn(self, 'handlePreview')
}, ['👁️', ' Preview']) : '',
E('button', {
'class': 'action-btn',
'click': function() { window.location.href = L.url('admin/services/service-registry/publish'); }
}, ['', ' Publish Service'])
])
]),
// Services Preview
services.length > 0 ? E('div', { 'class': 'services-section' }, [
E('h2', {}, ['📋', ' Services on Landing Page (', String(services.length), ')']),
E('div', { 'class': 'services-grid' },
services.map(function(svc) {
var urls = svc.urls || {};
var catIcon = categoryIcons[svc.category] || categoryIcons['default'];
var isRunning = svc.status === 'running';
return E('div', { 'class': 'service-card' }, [
E('div', { 'class': 'service-header' }, [
E('span', { 'class': 'service-icon' }, catIcon),
E('span', { 'class': 'service-status ' + (isRunning ? 'running' : 'stopped') },
isRunning ? '● Running' : '○ Stopped'
)
]),
E('h3', { 'class': 'service-name' }, svc.name || svc.id),
E('div', { 'class': 'service-category' }, svc.category || 'Uncategorized'),
E('div', { 'class': 'service-urls' }, [
urls.clearnet ? E('div', { 'class': 'url-item clearnet' }, [
E('span', { 'class': 'url-icon' }, '🌐'),
E('a', { 'href': urls.clearnet, 'target': '_blank' },
urls.clearnet.replace(/^https?:\/\//, '').substring(0, 25) + '...'
)
]) : '',
urls.onion ? E('div', { 'class': 'url-item onion' }, [
E('span', { 'class': 'url-icon' }, '🧅'),
E('span', { 'class': 'onion-url' },
urls.onion.substring(0, 20) + '....onion'
)
]) : ''
])
]);
})
)
]) : E('div', { 'class': 'empty-state' }, [
E('div', { 'class': 'empty-icon' }, '📭'),
E('h3', {}, 'No Published Services'),
E('p', {}, 'Publish some services to display them on the landing page'),
E('a', {
'class': 'action-btn primary',
'href': L.url('admin/services/service-registry/publish')
}, ['', ' Publish First Service'])
]),
// Features Section
E('div', { 'class': 'features-section' }, [
E('h2', {}, ['✨', ' Landing Page Features']),
E('div', { 'class': 'features-grid' }, [
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '📱'),
E('span', {}, 'Responsive Design')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '📷'),
E('span', {}, 'QR Codes')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '📋'),
E('span', {}, 'Copy to Clipboard')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '🔄'),
E('span', {}, 'Live Status')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '🌙'),
E('span', {}, 'Dark Mode')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '🔗'),
E('span', {}, 'Share Buttons')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '🧅'),
E('span', {}, 'Onion URLs')
]),
E('div', { 'class': 'feature-item' }, [
E('span', { 'class': 'feature-icon' }, '🔐'),
E('span', {}, 'Self-Hosted')
])
])
]),
// Settings Section
E('div', { 'class': 'settings-section' }, [
E('h2', {}, ['⚙️', ' Settings']),
E('div', { 'class': 'settings-grid' }, [
E('div', { 'class': 'setting-item' }, [
E('div', { 'class': 'setting-label' }, [
E('span', {}, '🎨'),
E('span', {}, 'Theme')
]),
E('div', { 'class': 'theme-selector' }, [
E('select', {
'class': 'theme-select',
'change': function(e) { self.handleThemeChange(e.target.value); }
}, [
E('option', { 'value': 'mirrorbox', 'selected': (config.theme || 'mirrorbox') === 'mirrorbox' }, 'MirrorBox (Glassmorphism)'),
E('option', { 'value': 'cyberpunk', 'selected': config.theme === 'cyberpunk' }, 'Cyberpunk (Neon)'),
E('option', { 'value': 'minimal', 'selected': config.theme === 'minimal' }, 'Minimal Dark'),
E('option', { 'value': 'terminal', 'selected': config.theme === 'terminal' }, 'Terminal (Matrix)'),
E('option', { 'value': 'light', 'selected': config.theme === 'light' }, 'Clean Light')
])
])
]),
E('div', { 'class': 'setting-item' }, [
E('div', { 'class': 'setting-label' }, [
E('span', {}, '🔄'),
E('span', {}, 'Auto-Regenerate')
]),
E('label', { 'class': 'toggle-switch' }, [
E('input', {
'type': 'checkbox',
'checked': config.auto_regen,
'change': function(e) { self.toggleAutoRegen(e.target.checked); }
}),
E('span', { 'class': 'toggle-slider' })
])
]),
E('div', { 'class': 'setting-item' }, [
E('div', { 'class': 'setting-label' }, [
E('span', {}, '📁'),
E('span', {}, 'Output Path')
]),
E('code', { 'class': 'setting-value' }, config.path || '/www/secubox-services.html')
])
])
])
]);
return container;
},
handleRegenerate: function() {
ui.showModal(_('Regenerating'), [
E('p', { 'class': 'spinning' }, _('🔄 Regenerating landing page...'))
]);
return api.generateLandingPage().then(function(result) {
ui.hideModal();
if (result.success) {
ui.addNotification(null, E('p', '✅ ' + _('Landing page regenerated successfully')), 'info');
window.location.reload();
} else {
ui.addNotification(null, E('p', '❌ ' + _('Failed to regenerate: ') + (result.error || '')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', '❌ ' + _('Error: ') + err.message), 'error');
});
},
handlePreview: function() {
ui.showModal(_('🖼️ Landing Page Preview'), [
E('div', { 'style': 'text-align: center;' }, [
E('iframe', {
'src': '/secubox-services.html',
'style': 'width: 100%; height: 500px; border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; background: #1a1a2e;'
})
]),
E('div', { 'class': 'right', 'style': 'margin-top: 15px; display: flex; gap: 10px; justify-content: flex-end;' }, [
E('a', {
'class': 'action-btn',
'href': '/secubox-services.html',
'target': '_blank'
}, ['🌐', ' Open in New Tab']),
E('button', { 'class': 'action-btn', 'click': ui.hideModal }, ['✕', ' Close'])
])
], 'wide');
},
toggleAutoRegen: function(enabled) {
// Save setting via API
ui.addNotification(null, E('p', (enabled ? '✅' : '❌') + ' Auto-regenerate ' + (enabled ? 'enabled' : 'disabled')), 'info');
},
handleThemeChange: function(theme) {
var self = this;
ui.showModal(_('Applying Theme'), [
E('p', { 'class': 'spinning' }, _('🎨 Applying theme: ' + theme + '...'))
]);
return api.setLandingTheme(theme).then(function(result) {
if (result.success) {
return api.generateLandingPage();
}
throw new Error(result.error || 'Failed to set theme');
}).then(function(result) {
ui.hideModal();
if (result.success) {
ui.addNotification(null, E('p', '✅ ' + _('Theme applied: ') + theme), 'info');
window.location.reload();
} else {
ui.addNotification(null, E('p', '❌ ' + _('Failed to regenerate page')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', '❌ ' + _('Error: ') + err.message), 'error');
});
},
getStyles: function() {
return [
'.service-landing-page { font-family: system-ui, -apple-system, sans-serif; color: #e0e0e0; background: linear-gradient(135deg, #0a0a1a 0%, #1a1a2e 100%); min-height: 100vh; padding: 20px; margin: -20px; }',
// Hero
'.landing-hero { text-align: center; padding: 40px 20px; margin-bottom: 30px; }',
'.hero-icon { font-size: 64px; margin-bottom: 15px; }',
'.landing-hero h1 { font-size: 32px; margin: 0 0 10px; background: linear-gradient(135deg, #3498db, #9b59b6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
'.hero-desc { color: #888; font-size: 16px; max-width: 500px; margin: 0 auto; }',
// Status Cards
'.status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 30px; }',
'.status-card { display: flex; align-items: center; gap: 15px; padding: 20px; background: rgba(30,30,50,0.6); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; }',
'.status-card.active { border-color: rgba(46,204,113,0.5); }',
'.status-card.inactive { border-color: rgba(231,76,60,0.3); }',
'.status-icon { font-size: 32px; }',
'.status-label { font-size: 12px; color: #888; }',
'.status-value { font-size: 18px; font-weight: 600; color: #fff; }',
// Actions
'.actions-section { margin-bottom: 30px; }',
'.actions-section h2 { display: flex; align-items: center; gap: 10px; font-size: 20px; margin: 0 0 15px; color: #fff; }',
'.actions-grid { display: flex; gap: 10px; flex-wrap: wrap; }',
'.action-btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 20px; border: 1px solid rgba(255,255,255,0.2); border-radius: 10px; background: rgba(255,255,255,0.05); color: #fff; cursor: pointer; transition: all 0.2s; text-decoration: none; font-size: 14px; }',
'.action-btn:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }',
'.action-btn.primary { background: linear-gradient(135deg, #3498db, #2980b9); border-color: #3498db; }',
'.action-btn.primary:hover { background: linear-gradient(135deg, #2980b9, #1a5276); }',
// Services
'.services-section { margin-bottom: 30px; }',
'.services-section h2 { display: flex; align-items: center; gap: 10px; font-size: 20px; margin: 0 0 15px; color: #fff; }',
'.services-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 15px; }',
'.service-card { background: rgba(30,30,50,0.6); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; padding: 20px; transition: all 0.2s; }',
'.service-card:hover { border-color: rgba(52,152,219,0.5); transform: translateY(-3px); }',
'.service-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }',
'.service-icon { font-size: 28px; }',
'.service-status { font-size: 12px; padding: 4px 10px; border-radius: 12px; }',
'.service-status.running { background: rgba(46,204,113,0.2); color: #2ecc71; }',
'.service-status.stopped { background: rgba(231,76,60,0.2); color: #e74c3c; }',
'.service-name { font-size: 18px; font-weight: 600; margin: 0 0 5px; color: #fff; }',
'.service-category { font-size: 12px; color: #888; margin-bottom: 15px; }',
'.service-urls { display: flex; flex-direction: column; gap: 8px; }',
'.url-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 8px 12px; background: rgba(0,0,0,0.3); border-radius: 8px; }',
'.url-icon { font-size: 14px; }',
'.url-item a { color: #3498db; text-decoration: none; }',
'.url-item a:hover { text-decoration: underline; }',
'.onion-url { color: #9b59b6; font-family: monospace; font-size: 11px; }',
// Empty State
'.empty-state { text-align: center; padding: 60px 20px; background: rgba(30,30,50,0.4); border-radius: 16px; }',
'.empty-icon { font-size: 64px; opacity: 0.5; margin-bottom: 20px; }',
'.empty-state h3 { font-size: 24px; margin: 0 0 10px; color: #fff; }',
'.empty-state p { color: #888; margin: 0 0 20px; }',
// Features
'.features-section { margin-bottom: 30px; padding: 25px; background: rgba(30,30,50,0.4); border-radius: 16px; }',
'.features-section h2 { display: flex; align-items: center; gap: 10px; font-size: 20px; margin: 0 0 20px; color: #fff; }',
'.features-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }',
'.feature-item { display: flex; align-items: center; gap: 10px; padding: 12px 15px; background: rgba(0,0,0,0.3); border-radius: 10px; font-size: 13px; }',
'.feature-icon { font-size: 18px; }',
// Settings
'.settings-section { padding: 25px; background: rgba(30,30,50,0.4); border-radius: 16px; }',
'.settings-section h2 { display: flex; align-items: center; gap: 10px; font-size: 20px; margin: 0 0 20px; color: #fff; }',
'.settings-grid { display: flex; flex-direction: column; gap: 15px; }',
'.setting-item { display: flex; justify-content: space-between; align-items: center; padding: 15px; background: rgba(0,0,0,0.3); border-radius: 10px; }',
'.setting-label { display: flex; align-items: center; gap: 10px; }',
'.setting-value { font-family: monospace; font-size: 12px; padding: 5px 10px; background: rgba(0,0,0,0.3); border-radius: 5px; color: #888; }',
// Toggle Switch
'.toggle-switch { position: relative; width: 50px; height: 26px; }',
'.toggle-switch input { opacity: 0; width: 0; height: 0; }',
'.toggle-slider { position: absolute; inset: 0; background: rgba(255,255,255,0.1); border-radius: 13px; cursor: pointer; transition: 0.3s; }',
'.toggle-slider::before { content: ""; position: absolute; width: 20px; height: 20px; left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: 0.3s; }',
'.toggle-switch input:checked + .toggle-slider { background: #2ecc71; }',
'.toggle-switch input:checked + .toggle-slider::before { transform: translateX(24px); }',
// Theme Selector
'.theme-selector { display: flex; align-items: center; gap: 10px; }',
'.theme-select { padding: 10px 15px; background: rgba(0,0,0,0.4); border: 1px solid rgba(255,255,255,0.2); border-radius: 8px; color: #fff; font-size: 14px; cursor: pointer; min-width: 200px; }',
'.theme-select:hover { border-color: rgba(52,152,219,0.5); }',
'.theme-select:focus { outline: none; border-color: #3498db; }',
'.theme-select option { background: #1a1a2e; color: #fff; padding: 10px; }'
].join('\n');
}
});