wip: peer make work with qr code
This commit is contained in:
+76
-195
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user