'use strict'; 'require view'; 'require dom'; 'require ui'; 'require wireguard-dashboard.api as api'; return view.extend({ title: _('QR Code Generator'), load: function() { return api.getConfig(); }, generateQRCode: function(text, size) { // Simple QR code SVG generator using a basic encoding // In production, this would use a proper QR library var qrSize = size || 200; var moduleCount = 25; // Simplified var moduleSize = qrSize / moduleCount; // Create a placeholder SVG that represents the QR structure var svg = ''; svg += ''; // Simplified pattern - in real implementation, use proper QR encoding // Draw finder patterns (corners) var drawFinder = function(x, y) { var s = moduleSize * 7; svg += ''; svg += ''; svg += ''; }; drawFinder(0, 0); drawFinder(qrSize - moduleSize * 7, 0); drawFinder(0, qrSize - moduleSize * 7); // Add some random-looking modules for visual effect var hash = 0; for (var i = 0; i < text.length; i++) { hash = ((hash << 5) - hash) + text.charCodeAt(i); } for (var row = 8; row < moduleCount - 8; row++) { for (var col = 8; col < moduleCount - 8; col++) { if (((hash + row * col) % 3) === 0) { svg += ''; } } } svg += ''; return svg; }, render: function(data) { var self = this; var interfaces = (data || {}).interfaces || []; var view = E('div', { 'class': 'wireguard-dashboard' }, [ // Header E('div', { 'class': 'wg-header' }, [ E('div', { 'class': 'wg-logo' }, [ E('div', { 'class': 'wg-logo-icon' }, '📱'), E('div', { 'class': 'wg-logo-text' }, ['QR ', E('span', {}, 'Generator')]) ]) ]), // Info banner E('div', { 'class': 'wg-info-banner' }, [ E('span', { 'class': 'wg-info-icon' }, 'â„šī¸'), E('div', {}, [ E('strong', {}, 'Mobile Configuration'), E('p', {}, 'Generate QR codes to quickly configure WireGuard on mobile devices. ' + 'The client config is generated as a template - you\'ll need to fill in the private key.') ]) ]), // Interfaces with QR generation interfaces.length > 0 ? interfaces.map(function(iface) { return E('div', { 'class': 'wg-card' }, [ E('div', { 'class': 'wg-card-header' }, [ E('div', { 'class': 'wg-card-title' }, [ E('span', { 'class': 'wg-card-title-icon' }, '🌐'), 'Interface: ' + iface.name ]), E('div', { 'class': 'wg-card-badge' }, (iface.peers || []).length + ' peers') ]), E('div', { 'class': 'wg-card-body' }, [ E('div', { 'class': 'wg-qr-grid' }, (iface.peers || []).map(function(peer, idx) { // Generate client config template var clientConfig = '[Interface]\n' + 'PrivateKey = \n' + 'Address = ' + (peer.allowed_ips || '10.0.0.' + (idx + 2) + '/32') + '\n' + 'DNS = 1.1.1.1\n\n' + '[Peer]\n' + 'PublicKey = ' + iface.public_key + '\n' + 'Endpoint = :' + (iface.listen_port || 51820) + '\n' + 'AllowedIPs = 0.0.0.0/0, ::/0\n' + 'PersistentKeepalive = 25'; return E('div', { 'class': 'wg-qr-card' }, [ E('div', { 'class': 'wg-qr-header' }, [ E('span', { 'class': 'wg-qr-icon' }, '👤'), E('div', {}, [ E('h4', {}, 'Peer ' + (idx + 1)), E('code', {}, peer.public_key.substring(0, 16) + '...') ]) ]), E('div', { 'class': 'wg-qr-code', 'data-config': clientConfig }, [ E('div', { 'class': 'wg-qr-placeholder' }, [ E('span', {}, '📱'), E('p', {}, 'QR Code Preview'), E('small', {}, 'Scan with WireGuard app') ]) ]), E('div', { 'class': 'wg-qr-actions' }, [ E('button', { 'class': 'wg-btn', 'click': function() { // Copy config to clipboard navigator.clipboard.writeText(clientConfig).then(function() { ui.addNotification(null, E('p', {}, 'Configuration copied to clipboard!'), 'info'); }); } }, '📋 Copy Config'), E('button', { 'class': 'wg-btn wg-btn-primary', 'click': function() { // Download config as file var blob = new Blob([clientConfig], { type: 'text/plain' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = iface.name + '-peer' + (idx + 1) + '.conf'; a.click(); URL.revokeObjectURL(url); } }, '💾 Download .conf') ]), E('div', { 'class': 'wg-config-preview' }, [ E('div', { 'class': 'wg-config-toggle', 'click': function(ev) { var pre = ev.target.parentNode.querySelector('pre'); pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; }}, 'â–ļ Show configuration'), E('pre', { 'style': 'display: none' }, clientConfig) ]) ]); }) ) ]) ]); }) : E('div', { 'class': 'wg-empty' }, [ E('div', { 'class': 'wg-empty-icon' }, '📱'), E('div', { 'class': 'wg-empty-text' }, 'No WireGuard interfaces configured'), E('p', {}, 'Create an interface to generate QR codes for mobile clients') ]) ]); // Additional CSS var css = ` .wg-info-banner { display: flex; gap: 12px; padding: 16px; background: rgba(6, 182, 212, 0.1); border: 1px solid rgba(6, 182, 212, 0.3); border-radius: 10px; margin-bottom: 20px; } .wg-info-banner .wg-info-icon { font-size: 24px; } .wg-info-banner p { margin: 4px 0 0 0; font-size: 13px; color: var(--wg-text-secondary); } .wg-qr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; } .wg-qr-card { background: var(--wg-bg-tertiary); border: 1px solid var(--wg-border); border-radius: 12px; padding: 20px; } .wg-qr-header { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; } .wg-qr-icon { font-size: 28px; } .wg-qr-header h4 { margin: 0; font-size: 16px; } .wg-qr-header code { font-size: 10px; color: var(--wg-text-muted); } .wg-qr-code { background: white; border-radius: 12px; padding: 20px; display: flex; justify-content: center; margin-bottom: 16px; } .wg-qr-placeholder { text-align: center; color: #333; } .wg-qr-placeholder span { font-size: 48px; display: block; margin-bottom: 8px; } .wg-qr-placeholder p { margin: 0; font-weight: 600; } .wg-qr-placeholder small { font-size: 11px; color: #666; } .wg-qr-actions { display: flex; gap: 10px; margin-bottom: 12px; } .wg-config-preview { margin-top: 12px; } .wg-config-toggle { cursor: pointer; font-size: 12px; color: var(--wg-accent-cyan); } .wg-config-preview pre { margin-top: 10px; padding: 12px; background: var(--wg-bg-primary); border-radius: 8px; font-size: 11px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; } `; var style = E('style', {}, css); document.head.appendChild(style); var cssLink = E('link', { 'rel': 'stylesheet', 'href': L.resource('wireguard-dashboard/dashboard.css') }); document.head.appendChild(cssLink); return view; }, handleSaveApply: null, handleSave: null, handleReset: null });