wip: peer make work with qr code

This commit is contained in:
Cloud
2025-09-13 12:08:28 -05:00
parent 4f65f95ac9
commit 5bd7443681
5 changed files with 105 additions and 200 deletions
+76 -195
View File
@@ -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"
>
<QrCode className="h-4 w-4" />
<div className="h-4 w-4 border-2 border-current rounded-sm flex items-center justify-center">
<div className="w-2 h-2 bg-current rounded-sm"></div>
</div>
</button>
<button
onClick={() => handleEditPeer(peer)}
@@ -697,7 +547,17 @@ PersistentKeepalive = 25`;
{/* Add Peer Modal */}
{showAddModal && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowAddModal(false);
setGeneratedKeys(null);
setShowAdvanced(false);
}
}}
>
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div className="mt-3">
<div className="flex items-center mb-4">
@@ -850,7 +710,15 @@ PersistentKeepalive = 25`;
{/* View Peer Modal */}
{showViewModal && selectedPeer && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowViewModal(false);
}
}}
>
<div className="relative top-10 mx-auto p-5 border w-full max-w-4xl shadow-lg rounded-md bg-white">
<div className="mt-3">
<div className="flex items-center justify-between mb-4">
@@ -935,7 +803,9 @@ PersistentKeepalive = 25`;
{/* QR Code Section */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2 flex items-center">
<QrCode className="h-5 w-5 mr-2" />
<div className="h-5 w-5 mr-2 border-2 border-gray-400 rounded-sm flex items-center justify-center">
<div className="w-2 h-2 bg-gray-400 rounded-sm"></div>
</div>
QR Code
</label>
<div className="text-center">
@@ -971,7 +841,9 @@ PersistentKeepalive = 25`;
</div>
) : (
<div className="p-8 bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg">
<QrCode className="h-12 w-12 text-gray-400 mx-auto mb-2" />
<div className="h-12 w-12 border-2 border-gray-400 rounded-sm flex items-center justify-center mx-auto mb-2">
<div className="w-6 h-6 bg-gray-400 rounded-sm"></div>
</div>
<p className="text-sm text-gray-500">
QR Code will appear here
</p>
@@ -1007,7 +879,16 @@ PersistentKeepalive = 25`;
{/* Edit Peer Modal */}
{showEditModal && selectedPeer && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowEditModal(false);
setSelectedPeer(null);
}
}}
>
<div className="relative top-10 mx-auto p-5 border w-full max-w-2xl shadow-lg rounded-md bg-white">
<div className="mt-3">
<div className="flex items-center mb-4">
+18 -2
View File
@@ -291,7 +291,15 @@ function Vault() {
{/* Generate Certificate Modal */}
{showAddCertModal && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowAddCertModal(false);
}
}}
>
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div className="mt-3">
<h3 className="text-lg font-medium text-gray-900 mb-4">Generate Certificate</h3>
@@ -375,7 +383,15 @@ function Vault() {
{/* Add Trusted Key Modal */}
{showAddKeyModal && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowAddKeyModal(false);
}
}}
>
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div className="mt-3">
<h3 className="text-lg font-medium text-gray-900 mb-4">Add Trusted Key</h3>
+9 -1
View File
@@ -329,7 +329,15 @@ function WireGuard() {
{/* Peer Configuration Modal */}
{showPeerConfig && selectedPeer && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
onClick={(e) => {
// Close modal when clicking on backdrop
if (e.target === e.currentTarget) {
setShowPeerConfig(false);
}
}}
>
<div className="relative top-10 mx-auto p-5 border w-full max-w-4xl shadow-lg rounded-md bg-white">
<div className="mt-3">
<div className="flex items-center justify-between mb-4">