diff --git a/docker-compose.yml b/docker-compose.yml index 9e1f78b..a2df860 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -171,7 +171,6 @@ services: - "8082:8080" environment: - FG_PUBLIC_PATH=/files-ui - platform: linux/amd64 networks: cell-network: diff --git a/webui/package.json b/webui/package.json index 39dcb79..1c9d530 100644 --- a/webui/package.json +++ b/webui/package.json @@ -18,7 +18,8 @@ "clsx": "^2.0.0", "tailwindcss": "^3.4.0", "autoprefixer": "^10.4.16", - "postcss": "^8.4.32" + "postcss": "^8.4.32", + "qrcode": "^1.5.3" }, "devDependencies": { "@eslint/js": "^9.30.1", diff --git a/webui/src/pages/Peers.jsx b/webui/src/pages/Peers.jsx index 231d920..c05dc2a 100644 --- a/webui/src/pages/Peers.jsx +++ b/webui/src/pages/Peers.jsx @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react'; -import { Plus, Trash2, Edit, Eye, Wifi, Shield, Copy, Download, Key, Smartphone, QrCode } from 'lucide-react'; +import { Plus, Trash2, Edit, Eye, Wifi, Shield, Copy, Download, Key, Smartphone } from 'lucide-react'; import { peerAPI, wireguardAPI } from '../services/api'; +import QRCode from 'qrcode'; function Peers() { const [peers, setPeers] = useState([]); @@ -27,22 +28,6 @@ function Peers() { useEffect(() => { fetchPeers(); - - // Test QR code library loading - const testQR = () => { - console.log('Testing QR code library...'); - console.log('QRCode available:', typeof QRCode); - console.log('window.QRCode available:', typeof window.QRCode); - - if (typeof QRCode !== 'undefined') { - console.log('✅ QRCode library loaded successfully'); - } else { - console.log('❌ QRCode library not loaded, will use fallback'); - } - }; - - // Test after a short delay to allow library to load - setTimeout(testQR, 1000); }, []); const fetchPeers = async () => { @@ -172,19 +157,29 @@ function Peers() { const getServerConfig = async () => { try { // Try to get server configuration from API + console.log('Fetching server config from:', '/api/wireguard/server-config'); const response = await fetch('/api/wireguard/server-config'); + console.log('Server config response status:', response.status); + console.log('Server config response ok:', response.ok); + if (response.ok) { const config = await response.json(); + console.log('Server config from API:', config); return { public_key: config.public_key || "SERVER_PUBLIC_KEY_PLACEHOLDER", endpoint: config.endpoint || "YOUR_SERVER_IP:51820" }; + } else { + console.error('Failed to get server config, status:', response.status); + const errorText = await response.text(); + console.error('Error response:', errorText); } } catch (error) { console.warn('Could not get server config:', error); } // Return default values + console.log('Using fallback server config'); return { public_key: "SERVER_PUBLIC_KEY_PLACEHOLDER", endpoint: "YOUR_SERVER_IP:51820" @@ -210,7 +205,9 @@ function Peers() { setSelectedPeer(peer); try { // Get server configuration first + console.log('Getting server config for peer:', peer.name); const serverConfig = await getServerConfig(); + console.log('Server config received:', serverConfig); // Create peer with server config const peerWithServerConfig = { @@ -218,6 +215,7 @@ function Peers() { server_public_key: serverConfig.public_key, server_endpoint: serverConfig.endpoint }; + console.log('Peer with server config:', peerWithServerConfig); // Try to get existing config first const response = await wireguardAPI.getPeerConfig({ name: peer.name }); @@ -232,10 +230,12 @@ function Peers() { // Generate QR code for the config using QR-specific format try { - const qrConfig = generateQRConfig(peerWithServerConfig); - console.log('QR Config for peer:', peer.name); - console.log('QR Config content:', qrConfig); - const qrDataUrl = await generateQRCode(qrConfig); + console.log('Generating QR config for peer:', peer.name); + console.log('Using config from API for QR code:', config); + + // Use the same config string that works for the text area + // It already has the correct server data from the API + const qrDataUrl = await generateQRCode(config); setQrCodeDataUrl(qrDataUrl); } catch (qrError) { console.error('Failed to generate QR code:', qrError); @@ -315,176 +315,24 @@ AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25`; }; - const generateQRCode = (text) => { - return new Promise((resolve, reject) => { - const tryGenerate = (attempts = 0) => { - try { - // Check if QRCode library is available - if (typeof QRCode !== 'undefined') { - console.log('Using QRCode library for QR generation'); - QRCode.toDataURL(text, { - width: 256, - margin: 2, - color: { - dark: '#000000', - light: '#FFFFFF' - }, - errorCorrectionLevel: 'M' - }, (err, url) => { - if (err) { - console.error('QR Code generation error:', err); - reject(err); - } else { - console.log('QR Code generated successfully'); - resolve(url); - } - }); - } else if (typeof qrcode !== 'undefined') { - console.log('Using qrcode-generator library for QR generation'); - try { - const qr = qrcode(0, 'M'); - qr.addData(text); - qr.make(); - - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - const size = 256; - const moduleCount = qr.getModuleCount(); - const cellSize = Math.floor(size / moduleCount); - - canvas.width = size; - canvas.height = size; - - // Fill with white background - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(0, 0, size, size); - - // Draw QR code - ctx.fillStyle = '#000000'; - for (let row = 0; row < moduleCount; row++) { - for (let col = 0; col < moduleCount; col++) { - if (qr.isDark(row, col)) { - ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize); - } - } - } - - console.log('QR Code generated with qrcode-generator'); - resolve(canvas.toDataURL()); - } catch (qrError) { - console.error('qrcode-generator error:', qrError); - const fallbackQR = generateFallbackQR(text); - resolve(fallbackQR); - } - } else if (attempts < 10) { - // Wait a bit for the library to load - console.log(`QR libraries not loaded yet, attempt ${attempts + 1}/10`); - setTimeout(() => tryGenerate(attempts + 1), 100); - } else { - // Fallback after 10 attempts - console.warn('QR libraries failed to load, using fallback'); - const fallbackQR = generateFallbackQR(text); - resolve(fallbackQR); - } - } catch (error) { - console.error('QR Code generation error:', error); - reject(error); - } - }; - - tryGenerate(); - }); + const generateQRCode = async (text) => { + try { + const qrDataUrl = await QRCode.toDataURL(text, { + width: 256, + margin: 2, + color: { + dark: '#000000', + light: '#FFFFFF' + }, + errorCorrectionLevel: 'M' + }); + return qrDataUrl; + } catch (error) { + console.error('QR Code generation error:', error); + throw error; + } }; - const generateFallbackQR = (text) => { - // Create a simple but functional QR code using a basic algorithm - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - const size = 256; - - canvas.width = size; - canvas.height = size; - - // Fill with white background - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(0, 0, size, size); - - // Create a simple QR code structure - const moduleSize = 4; - const modules = Math.floor(size / moduleSize); - - // Simple LFSR-based pattern generator for more realistic QR appearance - let lfsr = text.split('').reduce((acc, char) => acc ^ char.charCodeAt(0), 0xACE1); - - // Generate data pattern - for (let x = 0; x < modules; x++) { - for (let y = 0; y < modules; y++) { - // Skip corner marker areas - const isCorner = (x < 7 && y < 7) || (x >= modules - 7 && y < 7) || (x < 7 && y >= modules - 7); - if (isCorner) continue; - - // Skip timing pattern areas - const isTiming = (x === 6) || (y === 6); - if (isTiming) continue; - - // Generate pseudo-random bit using LFSR - lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & 0xB400); - const shouldFill = (lfsr & 1) === 1; - - if (shouldFill) { - ctx.fillStyle = '#000000'; - ctx.fillRect(x * moduleSize, y * moduleSize, moduleSize, moduleSize); - } - } - } - - // Add corner markers (7x7 modules) - const markerSize = 7; - const markerModules = markerSize * moduleSize; - - // Top-left corner marker - ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, markerModules, markerModules); - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(moduleSize, moduleSize, 5 * moduleSize, 5 * moduleSize); - ctx.fillStyle = '#000000'; - ctx.fillRect(2 * moduleSize, 2 * moduleSize, 3 * moduleSize, 3 * moduleSize); - - // Top-right corner marker - ctx.fillStyle = '#000000'; - ctx.fillRect(size - markerModules, 0, markerModules, markerModules); - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(size - 6 * moduleSize, moduleSize, 5 * moduleSize, 5 * moduleSize); - ctx.fillStyle = '#000000'; - ctx.fillRect(size - 5 * moduleSize, 2 * moduleSize, 3 * moduleSize, 3 * moduleSize); - - // Bottom-left corner marker - ctx.fillStyle = '#000000'; - ctx.fillRect(0, size - markerModules, markerModules, markerModules); - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(moduleSize, size - 6 * moduleSize, 5 * moduleSize, 5 * moduleSize); - ctx.fillStyle = '#000000'; - ctx.fillRect(2 * moduleSize, size - 5 * moduleSize, 3 * moduleSize, 3 * moduleSize); - - // Add timing patterns (alternating black/white modules) - ctx.fillStyle = '#000000'; - for (let i = 8; i < modules - 8; i += 2) { - ctx.fillRect(6 * moduleSize, i * moduleSize, moduleSize, moduleSize); - ctx.fillRect(i * moduleSize, 6 * moduleSize, moduleSize, moduleSize); - } - - // Add a simple data area pattern that looks more realistic - ctx.fillStyle = '#000000'; - for (let x = 8; x < modules - 8; x++) { - for (let y = 8; y < modules - 8; y++) { - if ((x + y) % 3 === 0) { - ctx.fillRect(x * moduleSize, y * moduleSize, moduleSize, moduleSize); - } - } - } - - return canvas.toDataURL(); - }; const handleEditPeer = (peer) => { setSelectedPeer(peer); @@ -669,7 +517,9 @@ PersistentKeepalive = 25`; className="text-green-600 hover:text-green-900" title="Show QR Code" > - +
+
+