wip: peer make work with qr code
This commit is contained in:
@@ -171,7 +171,6 @@ services:
|
|||||||
- "8082:8080"
|
- "8082:8080"
|
||||||
environment:
|
environment:
|
||||||
- FG_PUBLIC_PATH=/files-ui
|
- FG_PUBLIC_PATH=/files-ui
|
||||||
platform: linux/amd64
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
cell-network:
|
cell-network:
|
||||||
|
|||||||
+2
-1
@@ -18,7 +18,8 @@
|
|||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"postcss": "^8.4.32"
|
"postcss": "^8.4.32",
|
||||||
|
"qrcode": "^1.5.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.30.1",
|
"@eslint/js": "^9.30.1",
|
||||||
|
|||||||
+76
-195
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
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 { peerAPI, wireguardAPI } from '../services/api';
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
|
||||||
function Peers() {
|
function Peers() {
|
||||||
const [peers, setPeers] = useState([]);
|
const [peers, setPeers] = useState([]);
|
||||||
@@ -27,22 +28,6 @@ function Peers() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPeers();
|
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 () => {
|
const fetchPeers = async () => {
|
||||||
@@ -172,19 +157,29 @@ function Peers() {
|
|||||||
const getServerConfig = async () => {
|
const getServerConfig = async () => {
|
||||||
try {
|
try {
|
||||||
// Try to get server configuration from API
|
// 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');
|
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) {
|
if (response.ok) {
|
||||||
const config = await response.json();
|
const config = await response.json();
|
||||||
|
console.log('Server config from API:', config);
|
||||||
return {
|
return {
|
||||||
public_key: config.public_key || "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
public_key: config.public_key || "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
||||||
endpoint: config.endpoint || "YOUR_SERVER_IP:51820"
|
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) {
|
} catch (error) {
|
||||||
console.warn('Could not get server config:', error);
|
console.warn('Could not get server config:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return default values
|
// Return default values
|
||||||
|
console.log('Using fallback server config');
|
||||||
return {
|
return {
|
||||||
public_key: "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
public_key: "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
||||||
endpoint: "YOUR_SERVER_IP:51820"
|
endpoint: "YOUR_SERVER_IP:51820"
|
||||||
@@ -210,7 +205,9 @@ function Peers() {
|
|||||||
setSelectedPeer(peer);
|
setSelectedPeer(peer);
|
||||||
try {
|
try {
|
||||||
// Get server configuration first
|
// Get server configuration first
|
||||||
|
console.log('Getting server config for peer:', peer.name);
|
||||||
const serverConfig = await getServerConfig();
|
const serverConfig = await getServerConfig();
|
||||||
|
console.log('Server config received:', serverConfig);
|
||||||
|
|
||||||
// Create peer with server config
|
// Create peer with server config
|
||||||
const peerWithServerConfig = {
|
const peerWithServerConfig = {
|
||||||
@@ -218,6 +215,7 @@ function Peers() {
|
|||||||
server_public_key: serverConfig.public_key,
|
server_public_key: serverConfig.public_key,
|
||||||
server_endpoint: serverConfig.endpoint
|
server_endpoint: serverConfig.endpoint
|
||||||
};
|
};
|
||||||
|
console.log('Peer with server config:', peerWithServerConfig);
|
||||||
|
|
||||||
// Try to get existing config first
|
// Try to get existing config first
|
||||||
const response = await wireguardAPI.getPeerConfig({ name: peer.name });
|
const response = await wireguardAPI.getPeerConfig({ name: peer.name });
|
||||||
@@ -232,10 +230,12 @@ function Peers() {
|
|||||||
|
|
||||||
// Generate QR code for the config using QR-specific format
|
// Generate QR code for the config using QR-specific format
|
||||||
try {
|
try {
|
||||||
const qrConfig = generateQRConfig(peerWithServerConfig);
|
console.log('Generating QR config for peer:', peer.name);
|
||||||
console.log('QR Config for peer:', peer.name);
|
console.log('Using config from API for QR code:', config);
|
||||||
console.log('QR Config content:', qrConfig);
|
|
||||||
const qrDataUrl = await generateQRCode(qrConfig);
|
// 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);
|
setQrCodeDataUrl(qrDataUrl);
|
||||||
} catch (qrError) {
|
} catch (qrError) {
|
||||||
console.error('Failed to generate QR code:', qrError);
|
console.error('Failed to generate QR code:', qrError);
|
||||||
@@ -315,176 +315,24 @@ AllowedIPs = 0.0.0.0/0
|
|||||||
PersistentKeepalive = 25`;
|
PersistentKeepalive = 25`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateQRCode = (text) => {
|
const generateQRCode = async (text) => {
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
const tryGenerate = (attempts = 0) => {
|
const qrDataUrl = await QRCode.toDataURL(text, {
|
||||||
try {
|
width: 256,
|
||||||
// Check if QRCode library is available
|
margin: 2,
|
||||||
if (typeof QRCode !== 'undefined') {
|
color: {
|
||||||
console.log('Using QRCode library for QR generation');
|
dark: '#000000',
|
||||||
QRCode.toDataURL(text, {
|
light: '#FFFFFF'
|
||||||
width: 256,
|
},
|
||||||
margin: 2,
|
errorCorrectionLevel: 'M'
|
||||||
color: {
|
});
|
||||||
dark: '#000000',
|
return qrDataUrl;
|
||||||
light: '#FFFFFF'
|
} catch (error) {
|
||||||
},
|
console.error('QR Code generation error:', error);
|
||||||
errorCorrectionLevel: 'M'
|
throw error;
|
||||||
}, (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 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) => {
|
const handleEditPeer = (peer) => {
|
||||||
setSelectedPeer(peer);
|
setSelectedPeer(peer);
|
||||||
@@ -669,7 +517,9 @@ PersistentKeepalive = 25`;
|
|||||||
className="text-green-600 hover:text-green-900"
|
className="text-green-600 hover:text-green-900"
|
||||||
title="Show QR Code"
|
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>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEditPeer(peer)}
|
onClick={() => handleEditPeer(peer)}
|
||||||
@@ -697,7 +547,17 @@ PersistentKeepalive = 25`;
|
|||||||
|
|
||||||
{/* Add Peer Modal */}
|
{/* Add Peer Modal */}
|
||||||
{showAddModal && (
|
{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="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<div className="flex items-center mb-4">
|
<div className="flex items-center mb-4">
|
||||||
@@ -850,7 +710,15 @@ PersistentKeepalive = 25`;
|
|||||||
|
|
||||||
{/* View Peer Modal */}
|
{/* View Peer Modal */}
|
||||||
{showViewModal && selectedPeer && (
|
{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="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="mt-3">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
@@ -935,7 +803,9 @@ PersistentKeepalive = 25`;
|
|||||||
{/* QR Code Section */}
|
{/* QR Code Section */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2 flex items-center">
|
<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
|
QR Code
|
||||||
</label>
|
</label>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
@@ -971,7 +841,9 @@ PersistentKeepalive = 25`;
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="p-8 bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg">
|
<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">
|
<p className="text-sm text-gray-500">
|
||||||
QR Code will appear here
|
QR Code will appear here
|
||||||
</p>
|
</p>
|
||||||
@@ -1007,7 +879,16 @@ PersistentKeepalive = 25`;
|
|||||||
|
|
||||||
{/* Edit Peer Modal */}
|
{/* Edit Peer Modal */}
|
||||||
{showEditModal && selectedPeer && (
|
{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="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="mt-3">
|
||||||
<div className="flex items-center mb-4">
|
<div className="flex items-center mb-4">
|
||||||
|
|||||||
@@ -291,7 +291,15 @@ function Vault() {
|
|||||||
|
|
||||||
{/* Generate Certificate Modal */}
|
{/* Generate Certificate Modal */}
|
||||||
{showAddCertModal && (
|
{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="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Generate Certificate</h3>
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Generate Certificate</h3>
|
||||||
@@ -375,7 +383,15 @@ function Vault() {
|
|||||||
|
|
||||||
{/* Add Trusted Key Modal */}
|
{/* Add Trusted Key Modal */}
|
||||||
{showAddKeyModal && (
|
{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="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">Add Trusted Key</h3>
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Add Trusted Key</h3>
|
||||||
|
|||||||
@@ -329,7 +329,15 @@ function WireGuard() {
|
|||||||
|
|
||||||
{/* Peer Configuration Modal */}
|
{/* Peer Configuration Modal */}
|
||||||
{showPeerConfig && selectedPeer && (
|
{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="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="mt-3">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user