Files
securebit-chat/index.html
2025-08-11 20:52:14 -04:00

3622 lines
197 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://unpkg.com https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://static.cloudflareinsights.com;
style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://fonts.googleapis.com;
font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com;
connect-src 'self' https: https://cloudflareinsights.com;
img-src 'self' data: https://api.qrserver.com;
media-src 'none';
object-src 'none';
frame-src 'none';">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
<!-- GitHub Pages SEO -->
<meta name="description" content="LockBit.chat - P2P мессенджер с военным уровнем криптографии и Lightning Network платежами">
<meta name="keywords" content="P2P messenger, encryption, Lightning Network, WebRTC, privacy">
<meta name="author" content="Volodymyr">
<link rel="canonical" href="https://github.com/lockbitchat/lockbit-chat/">
<!-- Open Graph -->
<meta property="og:title" content="LockBit.chat - Enhanced Security Edition">
<meta property="og:description" content="Первый P2P мессенджер с Lightning Network платежами">
<meta property="og:url" content="https://github.com/lockbitchat/lockbit-chat/">
<meta property="og:type" content="website">
<meta property="og:image" content="https://github.com/lockbitchat/lockbit-chat/assets/images/og-image.png">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="LockBit.chat - Enhanced Security Edition">
<meta name="twitter:description" content="P2P мессенджер с военным уровнем криптографии">
<title>LockBit.chat - Enhanced Security Edition</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer">
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-regular-400.woff2" as="font" type="font/woff2" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="src/styles/main.css">
<link rel="stylesheet" href="src/styles/animations.css">
<link rel="stylesheet" href="src/styles/components.css">
<script>
// Enhanced icon loading fallback
document.addEventListener('DOMContentLoaded', function() {
// Check if Font Awesome loaded properly
function checkFontAwesome() {
const testIcon = document.createElement('i');
testIcon.className = 'fas fa-shield-halved';
testIcon.style.position = 'absolute';
testIcon.style.left = '-9999px';
testIcon.style.visibility = 'hidden';
document.body.appendChild(testIcon);
const computedStyle = window.getComputedStyle(testIcon, '::before');
const content = computedStyle.content;
const fontFamily = computedStyle.fontFamily;
document.body.removeChild(testIcon);
// Check if Font Awesome is properly loaded
if (!content || content === 'none' || content === 'normal' ||
!fontFamily.includes('Font Awesome') && !fontFamily.includes('fa-solid')) {
console.warn('Font Awesome not loaded properly, using fallback icons');
document.body.classList.add('fa-fallback');
return false;
}
console.log('Font Awesome loaded successfully');
return true;
}
// Check immediately and after a delay
if (!checkFontAwesome()) {
// Try alternative CDN if first one failed
setTimeout(function() {
if (!checkFontAwesome()) {
console.warn('Font Awesome still not loaded, using fallback icons');
document.body.classList.add('fa-fallback');
}
}, 2000);
}
});
</script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// Slider Component
const UniqueFeatureSlider = () => {
const [currentSlide, setCurrentSlide] = React.useState(0);
const slides = [
{
icon: "fas fa-shield-halved",
color: "orange",
title: "Военный уровень шифрования",
description: "ECDH P-384 + AES-GCM 256-bit + ECDSA цифровые подписи обеспечивают криптографическую стойкость уровня NSA Suite B"
},
{
icon: "fas fa-sync-alt",
color: "purple",
title: "Perfect Forward Secrecy",
description: "Автоматическая ротация ключей каждые 5 минут гарантирует, что даже при компрометации одного ключа остальные сообщения останутся защищенными"
},
{
icon: "fas fa-user-shield",
color: "green",
title: "Защита от MITM атак",
description: "Out-of-band верификация с кодами безопасности + взаимная аутентификация исключают возможность атак человека посередине"
},
{
icon: "fas fa-bolt",
color: "yellow",
title: "Lightning-платежи",
description: "Оплата сессий в сатоши через Lightning Network. Приватность платежей + мгновенные микротранзакции без банков"
},
{
icon: "fas fa-eye-slash",
color: "blue",
title: "Нулевое хранение данных",
description: "Все сообщения существуют только в памяти браузера. Никаких серверов, логов или баз данных. Полная анонимность"
},
{
icon: "fas fa-code",
color: "cyan",
title: "Open Source безопасность",
description: "Весь код открыт для аудита. Криптография работает прямо в браузере без доверия к серверам или третьим сторонам"
}
];
const nextSlide = () => setCurrentSlide((prev) => (prev + 1) % slides.length);
const prevSlide = () => setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
const goToSlide = (index) => setCurrentSlide(index);
React.useEffect(() => {
const timer = setInterval(() => {
nextSlide();
}, 15000);
return () => clearInterval(timer);
}, []);
return (
<div className="mt-12">
<div className="text-center mb-8">
<h3 className="text-2xl font-semibold text-primary mb-3">
Почему LockBit.chat уникальный
</h3>
<p className="text-secondary max-w-2xl mx-auto">
Единственный мессенджер с военным уровнем криптографии и Lightning-платежами
</p>
</div>
<div className="relative max-w-4xl mx-auto">
<div className="overflow-hidden rounded-xl">
<div
className="flex transition-transform duration-500 ease-in-out"
style={{ transform: `translateX(-${currentSlide * 100}%)` }}
>
{slides.map((slide, index) => (
<div key={index} className="w-full flex-shrink-0 px-4">
<div className="card-minimal rounded-xl p-8 text-center min-h-[300px] flex flex-col justify-center relative overflow-hidden">
{/* Фон-иконка */}
<i
className={`${slide.icon} absolute right-[-100px] top-1/2 -translate-y-1/2 opacity-10 text-[300px] pointer-events-none ${
slide.color === 'orange' ? 'text-orange-500' :
slide.color === 'purple' ? 'text-purple-500' :
slide.color === 'green' ? 'text-green-500' :
slide.color === 'yellow' ? 'text-yellow-500' :
slide.color === 'blue' ? 'text-blue-500' :
'text-cyan-500'
}`}
></i>
{/* Контент */}
<h4 className="text-xl font-semibold text-primary mb-4 relative z-10">
{slide.title}
</h4>
<p className="text-secondary leading-relaxed max-w-2xl mx-auto relative z-10">
{slide.description}
</p>
</div>
</div>
))}
</div>
</div>
{/* Навигация */}
<button
onClick={prevSlide}
className="absolute left-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
>
<i className="fas fa-chevron-left"></i>
</button>
<button
onClick={nextSlide}
className="absolute right-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
>
<i className="fas fa-chevron-right"></i>
</button>
</div>
{/* Точки */}
<div className="flex justify-center space-x-2 mt-6">
{slides.map((_, index) => (
<button
key={index}
onClick={() => goToSlide(index)}
className={`w-3 h-3 rounded-full transition-all duration-200 ${
index === currentSlide
? 'bg-orange-500'
: 'bg-gray-600 hover:bg-gray-500'
}`}
></button>
))}
</div>
</div>
);
};
const ComparisonTable = () => {
const [selectedFeature, setSelectedFeature] = React.useState(null);
const messengers = [
{
name: "LockBit.chat",
logo: React.createElement('div', {
className: "w-8 h-8 bg-orange-500/10 border border-orange-500/20 rounded-lg flex items-center justify-center"
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-shield-halved text-orange-400'
})
]),
type: "P2P WebRTC",
color: "orange"
},
{
name: "Signal",
logo: React.createElement('svg', {
key: 'signal-logo',
className: "w-8 h-8",
viewBox: "0 0 122.88 122.31",
xmlns: "http://www.w3.org/2000/svg"
}, [
React.createElement('path', {
key: 'bg',
className: "fill-blue-500",
d: "M27.75,0H95.13a27.83,27.83,0,0,1,27.75,27.75V94.57a27.83,27.83,0,0,1-27.75,27.74H27.75A27.83,27.83,0,0,1,0,94.57V27.75A27.83,27.83,0,0,1,27.75,0Z"
}),
React.createElement('path', {
key: 'icon',
className: "fill-white",
d: "M61.44,25.39A35.76,35.76,0,0,0,31.18,80.18L27.74,94.86l14.67-3.44a35.77,35.77,0,1,0,19-66Z"
})
]),
type: "Centralized",
color: "blue"
},
{
name: "Threema",
logo: React.createElement('svg', {
key: 'threema-logo',
className: "w-8 h-8",
viewBox: "0 0 122.88 122.88",
xmlns: "http://www.w3.org/2000/svg"
}, [
React.createElement('rect', {
key: 'bg',
width: "122.88",
height: "122.88",
rx: "18.43",
fill: "#474747"
}),
React.createElement('path', {
key: 'bubble',
fill: "#FFFFFF",
d: "M44.26,78.48l-19.44,4.8l4.08-16.56c-4.08-5.28-6.48-12-6.48-18.96c0-18.96,17.52-34.32,39.12-34.32 c21.6,0,39.12,15.36,39.12,34.32c0,18.96-17.52,34.32-39.12,34.32c-6,0-12-1.2-17.04-3.36L44.26,78.48z M50.26,44.64 h-0.48c-0.96,0-1.68,0.72-1.44,1.68v15.6c0,0.96,0.72,1.68,1.68,1.68l23.04,0c0.96,0,1.68-0.72,1.68-1.68v-15.6 c0-0.96-0.72-1.68-1.68-1.68h-0.48v-4.32c0-6-5.04-11.04-11.04-11.04S50.5,34.32,50.5,40.32v4.32H50.26z M68.02,44.64 h-13.2v-4.32c0-3.6,2.88-6.72,6.72-6.72c3.6,0,6.72,2.88,6.72,6.72v4.32H68.02z"
}),
React.createElement('circle', {
key: 'dot1',
cx: "37.44",
cy: "97.44",
r: "6.72",
fill: "#3fe669"
}),
React.createElement('circle', {
key: 'dot2',
cx: "61.44",
cy: "97.44",
r: "6.72",
fill: "#3fe669"
}),
React.createElement('circle', {
key: 'dot3',
cx: "85.44",
cy: "97.44",
r: "6.72",
fill: "#3fe669"
})
]),
type: "Centralized",
color: "green"
},
{
name: "Olvid",
logo: React.createElement('svg', {
key: 'olvid-logo',
className: "w-8 h-8",
viewBox: "0 0 128 128",
xmlns: "http://www.w3.org/2000/svg"
}, [
React.createElement('g', {
key: 'group',
transform: "translate(0,128) scale(0.1,-0.1)",
fill: "#0a4bd1",
stroke: "none"
}, [
React.createElement('path', {
key: 'p1',
d: "M262 1264 c-139 -37 -217 -120 -247 -260 -18 -85 -21 -627 -4 -720 21 -115 89 -201 198 -253 l56 -26 370 0 c284 0 379 3 410 13 108 37 203 140 225 247 7 36 10 169 8 402 -4 396 -6 405 -85 493 -50 55 -100 86 -178 106 -73 20 -679 18 -753 -2z m538 -215 c299 -105 388 -490 165 -714 -216 -216 -598 -152 -728 122 -30 64 -32 74 -32 183 0 114 1 117 38 192 99 201 337 294 557 217z"
}),
React.createElement('path', {
key: 'p2',
d: "M550 864 c-46 -20 -87 -59 -113 -109 -17 -31 -22 -58 -22 -115 0 -85 15 -122 72 -182 20 -20 32 -41 28 -47 -3 -6 -30 -25 -58 -41 l-52 -30 70 0 c137 0 262 46 329 120 51 57 69 103 69 181 0 106 -51 188 -140 226 -43 17 -139 16 -183 -3z"
})
])
]),
type: "Centralized",
color: "purple"
}
,
{
name: "Session",
logo: React.createElement('svg', {
key: 'olvid-logo',
className: "w-8 h-8",
viewBox: "0 0 1024 1024",
xmlns: "http://www.w3.org/2000/svg"
}, [
React.createElement('rect', {
key: 'bg',
width: "1024",
height: "1024",
fill: "#333132"
}),
React.createElement('path', {
key: 'icon',
fill: "#00f782",
d: "M431 574.8c-.8-7.4-6.7-8.2-10.8-10.6-13.6-7.9-27.5-15.4-41.3-23l-22.5-12.3c-8.5-4.7-17.1-9.2-25.6-14.1-10.5-6-21-11.9-31.1-18.6-18.9-12.5-33.8-29.1-46.3-48.1-8.3-12.6-14.8-26.1-19.2-40.4-6.7-21.7-10.8-44.1-7.8-66.8 1.8-14 4.6-28 9.7-41.6 7.8-20.8 19.3-38.8 34.2-54.8 9.8-10.6 21.2-19.1 33.4-26.8 14.7-9.3 30.7-15.4 47.4-19 13.8-3 28.1-4.3 42.2-4.4 89.9-.4 179.7-.3 269.6 0 12.6 0 25.5 1 37.7 4.1 24.3 6.2 45.7 18.2 63 37 11.2 12.2 20.4 25.8 25.8 41.2 7.3 20.7 12.3 42.1 6.7 64.4-2.1 8.5-2.7 17.5-6.1 25.4-4.7 10.9-10.8 21.2-17.2 31.2-8.7 13.5-20.5 24.3-34.4 32.2-10.1 5.7-21 10.2-32 14.3-18.1 6.7-37.2 5-56.1 5.2-17.2.2-34.5 0-51.7.1-1.7 0-3.4 1.2-5.1 1.9 1.3 1.8 2.1 4.3 3.9 5.3 13.5 7.8 27.2 15.4 40.8 22.9 11 6 22.3 11.7 33.2 17.9 15.2 8.5 30.2 17.4 45.3 26.1 19.3 11.1 34.8 26.4 47.8 44.3 9.7 13.3 17.2 27.9 23 43.5 6.1 16.6 9.2 33.8 10.4 51.3.6 9.1-.7 18.5-1.9 27.6-1.2 9.1-2.7 18.4-5.6 27.1-3.3 10.2-7.4 20.2-12.4 29.6-8.4 15.7-19.6 29.4-32.8 41.4-12.7 11.5-26.8 20.6-42.4 27.6-22.9 10.3-46.9 14.4-71.6 14.5-89.7.3-179.4.2-269.1-.1-12.6 0-25.5-1-37.7-3.9-24.5-5.7-45.8-18-63.3-36.4-11.6-12.3-20.2-26.5-26.6-41.9-2.7-6.4-4.1-13.5-5.4-20.4-1.5-8.1-2.8-16.3-3.1-24.5-.6-15.7 2.8-30.9 8.2-45.4 8.2-22 21.7-40.6 40.2-55.2 10-7.9 21.3-13.7 33.1-18.8 16.6-7.2 34-8.1 51.4-8.5 21.9-.5 43.9-.1 65.9-.1 1.9-.1 3.9-.3 6.2-.4zm96.3-342.4c0 .1 0 .1 0 0-48.3.1-96.6-.6-144.9.5-13.5.3-27.4 3.9-40.1 8.7-14.9 5.6-28.1 14.6-39.9 25.8-20.2 19-32.2 42.2-37.2 68.9-3.6 19-1.4 38.1 4.1 56.5 4.1 13.7 10.5 26.4 18.5 38.4 14.8 22.2 35.7 36.7 58.4 49.2 11 6.1 22.2 11.9 33.2 18 13.5 7.5 26.9 15.1 40.4 22.6 13.1 7.3 26.2 14.5 39.2 21.7 9.7 5.3 19.4 10.7 29.1 16.1 2.9 1.6 4.1.2 4.5-2.4.3-2 .3-4 .3-6.1v-58.8c0-19.9.1-39.9 0-59.8 0-6.6 1.7-12.8 7.6-16.1 3.5-2 8.2-2.8 12.4-2.8 50.3-.2 100.7-.2 151-.1 19.8 0 38.3-4.4 55.1-15.1 23.1-14.8 36.3-36.3 40.6-62.9 3.4-20.8-1-40.9-12.4-58.5-17.8-27.5-43.6-43-76.5-43.6-47.8-.8-95.6-.2-143.4-.2zm-30.6 559.7c45.1 0 90.2-.2 135.3.1 18.9.1 36.6-3.9 53.9-11.1 18.4-7.7 33.6-19.8 46.3-34.9 9.1-10.8 16.2-22.9 20.8-36.5 4.2-12.4 7.4-24.7 7.3-37.9-.1-10.3.2-20.5-3.4-30.5-2.6-7.2-3.4-15.2-6.4-22.1-3.9-8.9-8.9-17.3-14-25.5-12.9-20.8-31.9-34.7-52.8-46.4-10.6-5.9-21.2-11.6-31.8-17.5-10.3-5.7-20.4-11.7-30.7-17.4-11.2-6.1-22.5-11.9-33.7-18-16.6-9.1-33.1-18.4-49.8-27.5-4.9-2.7-6.1-1.9-6.4 3.9-.1 2-.1 4.1-.1 6.1v114.5c0 14.8-5.6 20.4-20.4 20.4-47.6.1-95.3-.1-142.9.2-10.5.1-21.1 1.4-31.6 2.8-16.5 2.2-30.5 9.9-42.8 21-17 15.5-27 34.7-29.4 57.5-1.1 10.9-.4 21.7 2.9 32.5 3.7 12.3 9.2 23.4 17.5 33 19.2 22.1 43.4 33.3 72.7 33.3 46.6.1 93 0 139.5 0z"
})
]),
type: "Onion Network",
color: "cyan"
}
];
const features = [
{
name: "Криптография",
lockbit: { status: "🏆", detail: "ECDH P-384 + AES-GCM 256 + ECDSA" },
signal: { status: "✅", detail: "Signal Protocol + Double Ratchet" },
threema: { status: "✅", detail: "NaCl + XSalsa20 + Poly1305" },
olvid: { status: "✅", detail: "OlvidEngine + пост-квантовая" },
session: { status: "✅", detail: "Signal Protocol модифицированный" }
},
{
name: "Perfect Forward Secrecy",
lockbit: { status: "🏆", detail: "Автоматическая ротация каждые 5 мин" },
signal: { status: "✅", detail: "Double Ratchet алгоритм" },
threema: { status: "⚠️", detail: "Частично (групповые чаты)" },
olvid: { status: "✅", detail: "Полная PFS для всех сообщений" },
session: { status: "✅", detail: "Session Ratchet алгоритм" }
},
{
name: "Архитектура",
lockbit: { status: "🏆", detail: "Чистый P2P без серверов" },
signal: { status: "❌", detail: "Централизованные серверы Signal" },
threema: { status: "❌", detail: "Серверы Threema в Швейцарии" },
olvid: { status: "❌", detail: "Серверы во Франции" },
session: { status: "⚠️", detail: "Onion routing через узлы сети" }
},
{
name: "Анонимность регистрации",
lockbit: { status: "🏆", detail: "Регистрация не требуется" },
signal: { status: "❌", detail: "Обязателен номер телефона" },
threema: { status: "✅", detail: "ID генерируется локально" },
olvid: { status: "✅", detail: "Криптографическая идентичность" },
session: { status: "✅", detail: "Случайный Session ID" }
},
{
name: "Защита метаданных",
lockbit: { status: "🏆", detail: "Полное шифрование метаданных" },
signal: { status: "⚠️", detail: "Sealed Sender (частично)" },
threema: { status: "⚠️", detail: "Минимальные метаданные" },
olvid: { status: "✅", detail: "Защита через обфускацию" },
session: { status: "✅", detail: "Onion routing скрывает метаданные" }
},
{
name: "Открытый код",
lockbit: { status: "🏆", detail: "100% открытый и аудируемый" },
signal: { status: "✅", detail: "Полностью открытый" },
threema: { status: "⚠️", detail: "Только клиенты открыты" },
olvid: { status: "✅", detail: "Полностью открытый" },
session: { status: "✅", detail: "Полностью открытый" }
},
{
name: "Цифровые подписи",
lockbit: { status: "🏆", detail: "ECDSA P-384 встроенные" },
signal: { status: "✅", detail: "Ed25519 подписи" },
threema: { status: "✅", detail: "Ed25519 подписи" },
olvid: { status: "✅", detail: "Множественные алгоритмы" },
session: { status: "✅", detail: "Ed25519 подписи" }
},
{
name: "Экономическая модель",
lockbit: { status: "🏆", detail: "Lightning сатоши за сессию" },
signal: { status: "⚠️", detail: "Пожертвования и гранты" },
threema: { status: "✅", detail: "Разовая покупка приложения" },
olvid: { status: "✅", detail: "Бесплатно + Enterprise" },
session: { status: "⚠️", detail: "Пожертвования" }
},
{
name: "Устойчивость к цензуре",
lockbit: { status: "🏆", detail: "Невозможно заблокировать P2P" },
signal: { status: "⚠️", detail: "Блокируется в авторитарных странах" },
threema: { status: "⚠️", detail: "Может быть заблокирован" },
olvid: { status: "⚠️", detail: "Может быть заблокирован" },
session: { status: "✅", detail: "Onion routing обходит блокировки" }
},
{
name: "Хранение данных",
lockbit: { status: "🏆", detail: "Только в памяти браузера" },
signal: { status: "⚠️", detail: "Локальная база данных" },
threema: { status: "⚠️", detail: "Локально + опциональный бэкап" },
olvid: { status: "✅", detail: "Локально без облачного бэкапа" },
session: { status: "⚠️", detail: "Локальная база данных" }
},
{
name: "Пост-квантовая защита",
lockbit: { status: "⚠️", detail: "Планируется в версии 5.0" },
signal: { status: "⚠️", detail: "В разработке PQXDH" },
threema: { status: "❌", detail: "Не реализовано" },
olvid: { status: "🏆", detail: "Уже реализована" },
session: { status: "❌", detail: "Не реализовано" }
},
{
name: "Верификация MITM",
lockbit: { status: "🏆", detail: "Out-of-band коды + взаимная auth" },
signal: { status: "✅", detail: "Safety numbers" },
threema: { status: "✅", detail: "Сканирование QR кодов" },
olvid: { status: "✅", detail: "SAS верификация" },
session: { status: "⚠️", detail: "Базовая верификация ключей" }
}
];
const getStatusIcon = (status) => {
switch (status) {
case "🏆": return { icon: status, color: "text-yellow-400" };
case "✅": return { icon: status, color: "text-green-400" };
case "⚠️": return { icon: status, color: "text-yellow-400" };
case "❌": return { icon: status, color: "text-red-400" };
default: return { icon: status, color: "text-gray-400" };
}
};
const toggleFeatureDetail = (index) => {
setSelectedFeature(selectedFeature === index ? null : index);
};
return React.createElement('div', {
key: 'comparison-section',
className: "mt-16"
}, [
React.createElement('div', {
key: 'section-header',
className: "text-center mb-8"
}, [
React.createElement('h3', {
key: 'title',
className: "text-2xl font-semibold text-primary mb-3"
}, 'Сравнение с лидерами приватности'),
React.createElement('p', {
key: 'subtitle',
className: "text-secondary max-w-2xl mx-auto mb-4"
}, "Детальное сравнение с самыми защищенными мессенджерами мира"),
React.createElement('div', {
key: 'trophy-note',
className: "inline-flex items-center px-4 py-2 bg-yellow-500/10 border border-yellow-500/20 rounded-lg"
}, [
React.createElement('span', {
key: 'trophy',
className: "text-yellow-400 mr-2"
}, "🏆"),
React.createElement('span', {
key: 'text',
className: "text-yellow-300 text-sm font-medium"
}, "Лидер в категории")
])
]),
React.createElement('div', {
key: 'table-container',
className: "max-w-7xl mx-auto"
}, [
// Mobile warning
React.createElement('div', {
key: 'mobile-warning',
className: "md:hidden p-4 bg-yellow-500/10 border border-yellow-500/20 rounded-lg mb-4"
}, [
React.createElement('p', {
className: "text-yellow-400 text-sm text-center"
}, "💡 Поверните устройство горизонтально для лучшего просмотра")
]),
// Table
React.createElement('div', {
key: 'table-wrapper',
className: "overflow-x-auto custom-scrollbar"
}, [
React.createElement('table', {
key: 'comparison-table',
className: "w-full border-collapse rounded-xl overflow-hidden",
style: { backgroundColor: "rgba(42, 43, 42, 0.8)" }
}, [
// Header
React.createElement('thead', {
key: 'table-head'
}, [
React.createElement('tr', {
key: 'header-row',
className: "bg-header"
}, [
React.createElement('th', {
key: 'feature-header',
className: "text-left p-4 border-b border-gray-600 text-primary font-semibold min-w-[220px]"
}, 'Критерий безопасности'),
...messengers.map((messenger, index) =>
React.createElement('th', {
key: `messenger-${index}`,
className: "text-center p-4 border-b border-gray-600 min-w-[140px]"
}, [
React.createElement('div', {
key: 'messenger-info',
className: "flex flex-col items-center"
}, [
React.createElement('div', {
key: 'logo',
className: "mb-2"
}, messenger.logo),
React.createElement('div', {
key: 'name',
className: `text-sm font-semibold ${
messenger.color === 'orange' ? 'text-orange-400' :
messenger.color === 'blue' ? 'text-blue-400' :
messenger.color === 'green' ? 'text-green-400' :
messenger.color === 'purple' ? 'text-purple-400' :
'text-cyan-400'
}`
}, messenger.name),
React.createElement('div', {
key: 'type',
className: "text-xs text-gray-400"
}, messenger.type)
])
])
)
])
]),
// Body
React.createElement('tbody', {
key: 'table-body'
}, features.map((feature, featureIndex) => [
React.createElement('tr', {
key: `feature-${featureIndex}`,
className: `border-b border-gray-700/30 hover:bg-gray-800/20 transition-colors cursor-pointer ${
selectedFeature === featureIndex ? 'bg-gray-800/40' : ''
}`,
onClick: () => toggleFeatureDetail(featureIndex)
}, [
React.createElement('td', {
key: 'feature-name',
className: "p-4 text-primary font-medium"
}, [
React.createElement('div', {
key: 'feature-text',
className: "flex items-center justify-between"
}, [
React.createElement('span', {
key: 'name'
}, feature.name),
React.createElement('i', {
key: 'expand-icon',
className: `fas fa-chevron-${selectedFeature === featureIndex ? 'up' : 'down'} text-xs text-gray-400 opacity-50`
})
])
]),
React.createElement('td', {
key: 'lockbit-cell',
className: "p-4 text-center"
}, [
React.createElement('span', {
key: 'lockbit-status',
className: getStatusIcon(feature.lockbit.status).color + " text-xl"
}, getStatusIcon(feature.lockbit.status).icon)
]),
React.createElement('td', {
key: 'signal-cell',
className: "p-4 text-center"
}, [
React.createElement('span', {
key: 'signal-status',
className: getStatusIcon(feature.signal.status).color + " text-xl"
}, getStatusIcon(feature.signal.status).icon)
]),
React.createElement('td', {
key: 'threema-cell',
className: "p-4 text-center"
}, [
React.createElement('span', {
key: 'threema-status',
className: getStatusIcon(feature.threema.status).color + " text-xl"
}, getStatusIcon(feature.threema.status).icon)
]),
React.createElement('td', {
key: 'olvid-cell',
className: "p-4 text-center"
}, [
React.createElement('span', {
key: 'olvid-status',
className: getStatusIcon(feature.olvid.status).color + " text-xl"
}, getStatusIcon(feature.olvid.status).icon)
]),
React.createElement('td', {
key: 'session-cell',
className: "p-4 text-center"
}, [
React.createElement('span', {
key: 'session-status',
className: getStatusIcon(feature.session.status).color + " text-xl"
}, getStatusIcon(feature.session.status).icon)
])
]),
// Expanded details row
selectedFeature === featureIndex && React.createElement('tr', {
key: `details-${featureIndex}`,
className: "border-b border-gray-700/30 bg-gray-800/10"
}, [
React.createElement('td', {
key: 'details-spacer',
className: "p-4 text-xs text-gray-400"
}, "Детали:"),
React.createElement('td', {
key: 'lockbit-detail',
className: "p-4 text-center"
}, [
React.createElement('div', {
key: 'detail',
className: "text-xs text-orange-300 font-medium leading-relaxed"
}, feature.lockbit.detail)
]),
React.createElement('td', {
key: 'signal-detail',
className: "p-4 text-center"
}, [
React.createElement('div', {
key: 'detail',
className: "text-xs text-blue-300 leading-relaxed"
}, feature.signal.detail)
]),
React.createElement('td', {
key: 'threema-detail',
className: "p-4 text-center"
}, [
React.createElement('div', {
key: 'detail',
className: "text-xs text-green-300 leading-relaxed"
}, feature.threema.detail)
]),
React.createElement('td', {
key: 'olvid-detail',
className: "p-4 text-center"
}, [
React.createElement('div', {
key: 'detail',
className: "text-xs text-purple-300 leading-relaxed"
}, feature.olvid.detail)
]),
React.createElement('td', {
key: 'session-detail',
className: "p-4 text-center"
}, [
React.createElement('div', {
key: 'detail',
className: "text-xs text-cyan-300 leading-relaxed"
}, feature.session.detail)
])
])
]).flat())
])
])
]),
// Enhanced Legend
React.createElement('div', {
key: 'legend',
className: "mt-8 grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto"
}, [
React.createElement('div', {
key: 'legend-item-1',
className: "flex items-center justify-center p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg"
}, [
React.createElement('span', {
key: 'icon1',
className: "text-yellow-400 mr-2 text-lg"
}, "🏆"),
React.createElement('span', {
key: 'text1',
className: "text-yellow-300 text-sm font-medium"
}, "Лидер")
]),
React.createElement('div', {
key: 'legend-item-2',
className: "flex items-center justify-center p-3 bg-green-500/10 border border-green-500/20 rounded-lg"
}, [
React.createElement('span', {
key: 'icon2',
className: "text-green-400 mr-2 text-lg"
}, "✅"),
React.createElement('span', {
key: 'text2',
className: "text-green-300 text-sm font-medium"
}, "Отлично")
]),
React.createElement('div', {
key: 'legend-item-3',
className: "flex items-center justify-center p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg"
}, [
React.createElement('span', {
key: 'icon3',
className: "text-yellow-400 mr-2 text-lg"
}, "⚠️"),
React.createElement('span', {
key: 'text3',
className: "text-yellow-300 text-sm font-medium"
}, "Частично")
]),
React.createElement('div', {
key: 'legend-item-4',
className: "flex items-center justify-center p-3 bg-red-500/10 border border-red-500/20 rounded-lg"
}, [
React.createElement('span', {
key: 'icon4',
className: "text-red-400 mr-2 text-lg"
}, "❌"),
React.createElement('span', {
key: 'text4',
className: "text-red-300 text-sm font-medium"
}, "Нет")
])
]),
// Summary
React.createElement('div', {
key: 'summary',
className: "mt-8 p-6 bg-orange-500/10 border border-orange-500/20 rounded-xl max-w-4xl mx-auto"
}, [
React.createElement('h4', {
key: 'summary-title',
className: "text-lg font-semibold text-orange-400 mb-3 flex items-center"
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-trophy mr-2'
}),
'Итоги сравнения'
]),
React.createElement('p', {
key: 'summary-text',
className: "text-secondary leading-relaxed"
}, "LockBit.chat лидирует в 8 из 12 категорий безопасности, особенно превосходя конкурентов в архитектуре P2P, анонимности и защите метаданных. Только Olvid опережает в пост-квантовой криптографии, тогда как централизованные решения показывают существенные недостатки в приватности.")
])
]);
};
function Roadmap() {
const [selectedPhase, setSelectedPhase] = React.useState(null);
const phases = [
{
version: "v1.0",
title: "Начало разработки",
status: "done",
date: "Начало 2025",
description: "Идея, прототип и настройка инфраструктуры",
features: [
"Формирование концепции и требований",
"Выбор стека: WebRTC, P2P, криптография",
"Первые прототипы обмена сообщениями",
"Создание репозитория и CI",
"Базовая архитектура шифрования",
"Проектирование UX/UI"
]
},
{
version: "v1.5",
title: "Alpha Release",
status: "done",
date: "Весна 2025",
description: "Первая публичная альфа: базовый чат и обмен ключами",
features: [
"Базовый P2P обмен сообщениями через WebRTC",
"Простейшая E2E-шифровка (демо-схема)",
"Стабильный сигналинг и переподключение",
"Минимальный UX для тестирования",
"Сбор обратной связи от ранних тестировщиков"
]
},
{
version: "v2.0",
title: "Security Hardened",
status: "done",
date: "Лето 2025",
description: "Усиление безопасности и выпуск стабильной ветки",
features: [
"Внедрение ECDH/ECDSA в продакшн",
"Perfect Forward Secrecy и ротация ключей",
"Улучшенные проверки подлинности",
"Шифрование файлов и передач больших payload'ов",
"Аудит базовых криптопроцессов"
]
},
{
version: "v3.0",
title: "Scaling & Stability",
status: "done",
date: "Осень 2025",
description: "Масштабирование сети и улучшение стабильности",
features: [
"Оптимизация P2P соединений и NAT traversal",
"Механизмы повторного подключения и очереди сообщений",
"Снижение потребления батареи на мобильных",
"Поддержка синхронизации между устройствами",
"Инструменты мониторинга и логирования для разработчиков"
]
},
{
version: "v3.5",
title: "Privacy-first Release",
status: "done",
date: "Зима 2025",
description: "Фокус на приватности: минимизация метаданных",
features: [
"Защита метаданных и уменьшение fingerprint'а",
"Эксперименты с onion-routing и DHT",
"Опции анонимных соединений",
"Подготовка к открытой ревизии кода",
"Улучшенные процессы верификации пользователей"
]
},
// current and future phases (kept from original roadmap)
{
version: "v4.0",
title: "Enhanced Security Edition",
status: "current",
date: "Сейчас",
description: "Текущая версия с военным уровнем криптографии",
features: [
"ECDH P-384 + AES-GCM 256-bit шифрование",
"ECDSA цифровые подписи",
"Perfect Forward Secrecy с ротацией ключей",
"Out-of-band верификация MITM",
"Lightning Network платежи",
"P2P WebRTC архитектура",
"Защита метаданных",
"100% открытый код"
]
},
{
version: "v4.5",
title: "Mobile & Desktop Edition",
status: "development",
date: "Q2 2025",
description: "Нативные приложения для всех платформ",
features: [
"PWA приложение для мобильных",
"Electron приложение для десктопа",
"Уведомления в реальном времени",
"Автоматическое переподключение",
"Оптимизация батареи",
"Синхронизация между устройствами",
"Улучшенный UX/UI",
"Поддержка файлов до 100MB"
]
},
{
version: "v5.0",
title: "Quantum-Resistant Edition",
status: "planned",
date: "Q4 2025",
description: "Защита от квантовых компьютеров",
features: [
"Пост-квантовая криптография CRYSTALS-Kyber",
"SPHINCS+ цифровые подписи",
"Гибридная схема: классика + PQ",
"Quantum-safe key exchange",
"Обновленные алгоритмы хеширования",
"Миграция существующих сессий",
"Совместимость с v4.x",
"Квантово-стойкие протоколы"
]
},
{
version: "v5.5",
title: "Group Communications",
status: "planned",
date: "Q2 2026",
description: "Групповые чаты с сохранением приватности",
features: [
"Групповые P2P соединения до 8 участников",
"Mesh networking для групп",
"Signal Double Ratchet для групп",
"Анонимные группы без метаданных",
"Эфемерные группы (исчезают после сессии)",
"Групповые Lightning платежи",
"Администрирование через криптографию",
"Аудит участников группы"
]
},
{
version: "v6.0",
title: "Decentralized Network",
status: "research",
date: "2027",
description: "Полностью децентрализованная сеть",
features: [
"Mesh сеть LockBit узлов",
"DHT для обнаружения пиров",
"Onion routing встроенный",
"Токеномика и стимулы узлов",
"Governance через DAO",
"Interoperability с другими сетями",
"Кроссплатформенная совместимость",
"Самовосстанавливающаяся сеть"
]
},
{
version: "v7.0",
title: "AI Privacy Assistant",
status: "research",
date: "2028+",
description: "ИИ для приватности и безопасности",
features: [
"Локальный ИИ анализ угроз",
"Автоматическое обнаружение MITM",
"Адаптивная криптография",
"Personalized security recommendations",
"Zero-knowledge machine learning",
"Приватный ИИ-ассистент",
"Предиктивная безопасность",
"Автономная защита от атак"
]
}
];
const getStatusConfig = (status) => {
switch (status) {
case 'current':
return {
color: 'green',
bgClass: 'bg-green-500/10 border-green-500/20',
textClass: 'text-green-400',
icon: 'fas fa-check-circle',
label: 'Текущая версия'
};
case 'development':
return {
color: 'orange',
bgClass: 'bg-orange-500/10 border-orange-500/20',
textClass: 'text-orange-400',
icon: 'fas fa-code',
label: 'В разработке'
};
case 'planned':
return {
color: 'blue',
bgClass: 'bg-blue-500/10 border-blue-500/20',
textClass: 'text-blue-400',
icon: 'fas fa-calendar-alt',
label: 'Запланировано'
};
case 'research':
return {
color: 'purple',
bgClass: 'bg-purple-500/10 border-purple-500/20',
textClass: 'text-purple-400',
icon: 'fas fa-flask',
label: 'Исследования'
};
case 'done':
return {
color: 'gray',
bgClass: 'bg-gray-500/10 border-gray-500/20',
textClass: 'text-gray-300',
icon: 'fas fa-flag-checkered',
label: 'Выпущено'
};
default:
return {
color: 'gray',
bgClass: 'bg-gray-500/10 border-gray-500/20',
textClass: 'text-gray-400',
icon: 'fas fa-question',
label: 'Неизвестно'
};
}
};
const togglePhaseDetail = (index) => {
setSelectedPhase(selectedPhase === index ? null : index);
};
return (
<div key="roadmap-section" className="mt-16 px-4 sm:px-0">
<div key="section-header" className="text-center mb-12">
<h3 key="title" className="text-2xl font-semibold text-primary mb-3">
Roadmap развития
</h3>
<p key="subtitle" className="text-secondary max-w-2xl mx-auto mb-6">
Эволюция LockBit.chat: от начальной разработки до квантово-стойкой
децентрализованной сети
</p>
<div
key="roadmap-note"
className="inline-flex items-center px-4 py-2 bg-blue-500/10 border border-blue-500/20 rounded-lg"
>
<i key="icon" className="fas fa-rocket text-blue-400 mr-2" />
<span key="text" className="text-blue-300 text-sm font-medium">
Нажмите на версию для подробностей
</span>
</div>
</div>
<div key="roadmap-container" className="max-w-6xl mx-auto">
<div key="timeline" className="relative">
{/* Линия убрана */}
<div key="phases" className="space-y-8">
{phases.map((phase, index) => {
const statusConfig = getStatusConfig(phase.status);
const isExpanded = selectedPhase === index;
return (
<div key={`phase-${index}`} className="relative">
{/* Точки видны только на sm+ */}
<button
type="button"
aria-expanded={isExpanded}
onClick={() => togglePhaseDetail(index)}
key={`phase-button-${index}`}
className={`card-minimal rounded-xl p-4 text-left w-full transition-all duration-300 ${
isExpanded
? "ring-2 ring-" + statusConfig.color + "-500/30"
: ""
}`}
>
<div
key="phase-header"
className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4 space-y-2 sm:space-y-0"
>
<div
key="phase-info"
className="flex flex-col sm:flex-row sm:items-center sm:space-x-4"
>
<div
key="version-badge"
className={`px-3 py-1 ${statusConfig.bgClass} border rounded-lg mb-2 sm:mb-0`}
>
<span
key="version"
className={`${statusConfig.textClass} font-bold text-sm`}
>
{phase.version}
</span>
</div>
<div key="title-section">
<h4
key="title"
className="text-lg font-semibold text-primary"
>
{phase.title}
</h4>
<p
key="description"
className="text-secondary text-sm"
>
{phase.description}
</p>
</div>
</div>
<div
key="phase-meta"
className="flex items-center space-x-3 text-sm text-gray-400 font-medium"
>
<div
key="status-badge"
className={`flex items-center px-3 py-1 ${statusConfig.bgClass} border rounded-lg`}
>
<i
key="status-icon"
className={`${statusConfig.icon} ${statusConfig.textClass} mr-2 text-xs`}
/>
<span
key="status-text"
className={`${statusConfig.textClass} text-xs font-medium`}
>
{statusConfig.label}
</span>
</div>
<div key="date">{phase.date}</div>
<i
key="expand-icon"
className={`fas fa-chevron-${
isExpanded ? "up" : "down"
} text-gray-400 text-sm`}
/>
</div>
</div>
{isExpanded && (
<div
key="features-section"
className="mt-6 pt-6 border-t border-gray-700/30"
>
<h5
key="features-title"
className="text-primary font-medium mb-4 flex items-center"
>
<i
key="features-icon"
className="fas fa-list-ul mr-2 text-sm"
/>
Ключевые функции:
</h5>
<div
key="features-grid"
className="grid md:grid-cols-2 gap-3"
>
{phase.features.map((feature, featureIndex) => (
<div
key={`feature-${featureIndex}`}
className="flex items-center space-x-3 p-3 bg-custom-bg rounded-lg"
>
<div
className={`w-2 h-2 rounded-full ${statusConfig.textClass.replace(
"text-",
"bg-"
)}`}
/>
<span className="text-secondary text-sm">
{feature}
</span>
</div>
))}
</div>
</div>
)}
</button>
</div>
);
})}
</div>
</div>
</div>
<div key="cta-section" className="mt-12 text-center">
<div
key="cta-card"
className="card-minimal rounded-xl p-8 max-w-2xl mx-auto"
>
<h4
key="cta-title"
className="text-xl font-semibold text-primary mb-3"
>
Присоединяйтесь к будущему приватности
</h4>
<p key="cta-description" className="text-secondary mb-6">
LockBit.chat развивается благодаря сообществу. Ваши идеи и feedback
помогают формировать будущее защищенного общения.
</p>
<div
key="cta-buttons"
className="flex flex-col sm:flex-row gap-4 justify-center"
>
<a
key="github-link"
href="https://github.com/lockbitchat/lockbit-chat/"
className="btn-primary text-white py-3 px-6 rounded-lg font-medium transition-all duration-200 flex items-center justify-center"
>
<i key="github-icon" className="fab fa-github mr-2" />
GitHub Repository
</a>
<a
key="feedback-link"
href="mailto:lockbitchat@tutanota.com"
className="btn-secondary text-white py-3 px-6 rounded-lg font-medium transition-all duration-200 flex items-center justify-center"
>
<i key="feedback-icon" className="fas fa-comments mr-2" />
Обратная связь
</a>
</div>
</div>
</div>
</div>
);
}
// Enhanced Header Component with verification status
const EnhancedMinimalHeader = ({ status, fingerprint, verificationCode, onDisconnect, isConnected, securityLevel, sessionManager, sessionTimeLeft }) => {
const getStatusConfig = () => {
switch (status) {
case 'connected':
return {
text: 'Подключено',
className: 'status-connected',
badgeClass: 'bg-green-500/10 text-green-400 border-green-500/20'
};
case 'verifying':
return {
text: 'Верификация...',
className: 'status-verifying',
badgeClass: 'bg-purple-500/10 text-purple-400 border-purple-500/20'
};
case 'connecting':
return {
text: 'Подключение...',
className: 'status-connecting',
badgeClass: 'bg-blue-500/10 text-blue-400 border-blue-500/20'
};
case 'retrying':
return {
text: 'Переподключение...',
className: 'status-connecting',
badgeClass: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'
};
case 'failed':
return {
text: 'Ошибка',
className: 'status-failed',
badgeClass: 'bg-red-500/10 text-red-400 border-red-500/20'
};
case 'reconnecting':
return {
text: 'Переподключение...',
className: 'status-connecting',
badgeClass: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'
};
case 'peer_disconnected':
return {
text: 'Собеседник отключился',
className: 'status-failed',
badgeClass: 'bg-orange-500/10 text-orange-400 border-orange-500/20'
};
default:
return {
text: 'Не подключен',
className: 'status-disconnected',
badgeClass: 'bg-gray-500/10 text-gray-400 border-gray-500/20'
};
}
};
const config = getStatusConfig();
return React.createElement('header', {
className: 'header-minimal sticky top-0 z-50'
}, [
React.createElement('div', {
key: 'container',
className: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8'
}, [
React.createElement('div', {
key: 'content',
className: 'flex items-center justify-between h-16'
}, [
// Logo and Title - Mobile Responsive
React.createElement('div', {
key: 'logo-section',
className: 'flex items-center space-x-2 sm:space-x-3'
}, [
React.createElement('div', {
key: 'logo',
className: 'icon-container w-8 h-8 sm:w-10 sm:h-10'
}, [
React.createElement('i', {
className: 'fas fa-shield-halved accent-orange text-sm sm:text-base'
})
]),
React.createElement('div', {
key: 'title-section'
}, [
React.createElement('h1', {
key: 'title',
className: 'text-lg sm:text-xl font-semibold text-primary'
}, 'LockBit.chat'),
React.createElement('p', {
key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom')
])
]),
// Status and Controls - Mobile Responsive
React.createElement('div', {
key: 'status-section',
className: 'flex items-center space-x-2 sm:space-x-3'
}, [
// Session Timer - показывать если есть активная сессия
sessionManager?.hasActiveSession() && React.createElement(SessionTimer, {
key: 'session-timer',
timeLeft: sessionTimeLeft,
sessionType: sessionManager.currentSession?.type || 'unknown'
}),
// Security Level Indicator - Hidden on mobile, shown on tablet+ (Clickable)
securityLevel && React.createElement('div', {
key: 'security-level',
className: 'hidden md:flex items-center space-x-2 cursor-pointer hover:opacity-80 transition-opacity duration-200',
onClick: () => {
if (securityLevel.verificationResults) {
console.log('Security verification results:', securityLevel.verificationResults);
alert('Детали проверки безопасности:\n\n' +
Object.entries(securityLevel.verificationResults)
.map(([key, result]) => `${key}: ${result.passed ? '✅' : '❌'} ${result.details}`)
.join('\n')
);
}
},
title: 'Нажмите для просмотра деталей безопасности'
}, [
React.createElement('div', {
key: 'security-icon',
className: `w-6 h-6 rounded-full flex items-center justify-center ${
securityLevel.color === 'green' ? 'bg-green-500/20' :
securityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
}`
}, [
React.createElement('i', {
className: `fas fa-shield-alt text-xs ${
securityLevel.color === 'green' ? 'text-green-400' :
securityLevel.color === 'yellow' ? 'text-yellow-400' : 'text-red-400'
}`
})
]),
React.createElement('div', {
key: 'security-info',
className: 'flex flex-col'
}, [
React.createElement('div', {
key: 'security-level-text',
className: 'text-xs font-medium text-primary'
}, `${securityLevel.level} (${securityLevel.score}%)`),
securityLevel.details && React.createElement('div', {
key: 'security-details',
className: 'text-xs text-muted mt-1 hidden lg:block'
}, securityLevel.details),
React.createElement('div', {
key: 'security-progress',
className: 'w-16 h-1 bg-gray-600 rounded-full overflow-hidden'
}, [
React.createElement('div', {
key: 'progress-bar',
className: `h-full transition-all duration-500 ${
securityLevel.color === 'green' ? 'bg-green-400' :
securityLevel.color === 'yellow' ? 'bg-yellow-400' : 'bg-red-400'
}`,
style: { width: `${securityLevel.score}%` }
})
])
])
]),
// Mobile Security Indicator - Only icon on mobile (Clickable)
securityLevel && React.createElement('div', {
key: 'mobile-security',
className: 'md:hidden flex items-center'
}, [
React.createElement('div', {
key: 'mobile-security-icon',
className: `w-8 h-8 rounded-full flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity duration-200 ${
securityLevel.color === 'green' ? 'bg-green-500/20' :
securityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
}`,
title: `${securityLevel.level} (${securityLevel.score}%) - Нажмите для деталей`,
onClick: () => {
if (securityLevel.verificationResults) {
console.log('Security verification results:', securityLevel.verificationResults);
alert('Детали проверки безопасности:\n\n' +
Object.entries(securityLevel.verificationResults)
.map(([key, result]) => `${key}: ${result.passed ? '✅' : '❌'} ${result.details}`)
.join('\n')
);
}
}
}, [
React.createElement('i', {
className: `fas fa-shield-alt text-sm ${
securityLevel.color === 'green' ? 'text-green-400' :
securityLevel.color === 'yellow' ? 'text-yellow-400' : 'text-red-400'
}`
})
])
]),
// Status Badge - Compact on mobile
React.createElement('div', {
key: 'status-badge',
className: `px-2 sm:px-3 py-1.5 rounded-lg border ${config.badgeClass} flex items-center space-x-1 sm:space-x-2`
}, [
React.createElement('span', {
key: 'status-dot',
className: `status-dot ${config.className}`
}),
React.createElement('span', {
key: 'status-text',
className: 'text-xs sm:text-sm font-medium'
}, config.text)
]),
// Disconnect Button - Icon only on mobile
isConnected && React.createElement('button', {
key: 'disconnect-btn',
onClick: onDisconnect,
className: 'p-1.5 sm:px-3 sm:py-1.5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 rounded-lg transition-all duration-200 text-sm'
}, [
React.createElement('i', {
key: 'disconnect-icon',
className: 'fas fa-power-off sm:mr-2'
}),
React.createElement('span', {
key: 'disconnect-text',
className: 'hidden sm:inline'
}, 'Отключить')
])
])
])
]),
]);
};
// Enhanced Copy Button with better UX
const EnhancedCopyButton = ({ text, className = "", children }) => {
const [copied, setCopied] = React.useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (error) {
console.error('Copy failed:', error);
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
return React.createElement('button', {
onClick: handleCopy,
className: `${className} transition-all duration-200`
}, [
React.createElement('i', {
key: 'icon',
className: `${copied ? 'fas fa-check accent-green' : 'fas fa-copy text-secondary'} mr-2`
}),
copied ? 'Скопировано!' : children
]);
};
// Verification Component
const VerificationStep = ({ verificationCode, onConfirm, onReject }) => {
return React.createElement('div', {
className: "card-minimal rounded-xl p-6 border-purple-500/20"
}, [
React.createElement('div', {
key: 'header',
className: "flex items-center mb-4"
}, [
React.createElement('div', {
key: 'icon',
className: "w-10 h-10 bg-purple-500/10 border border-purple-500/20 rounded-lg flex items-center justify-center mr-3"
}, [
React.createElement('i', {
className: 'fas fa-shield-alt accent-purple'
})
]),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary"
}, "Верификация безопасности")
]),
React.createElement('div', {
key: 'content',
className: "space-y-4"
}, [
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm"
}, "Сверьте код безопасности с собеседником по другому каналу связи (голос, SMS, etc):"),
React.createElement('div', {
key: 'code-display',
className: "text-center"
}, [
React.createElement('div', {
key: 'code',
className: "verification-code text-2xl py-4"
}, verificationCode)
]),
React.createElement('div', {
key: 'warning',
className: "p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg"
}, [
React.createElement('p', {
className: "text-yellow-400 text-sm flex items-center"
}, [
React.createElement('i', {
className: 'fas fa-exclamation-triangle mr-2'
}),
'Убедитесь, что коды полностью совпадают!'
])
]),
React.createElement('div', {
key: 'buttons',
className: "flex space-x-3"
}, [
React.createElement('button', {
key: 'confirm',
onClick: onConfirm,
className: "flex-1 btn-verify text-white py-3 px-4 rounded-lg font-medium transition-all duration-200"
}, [
React.createElement('i', {
className: 'fas fa-check mr-2'
}),
'Коды совпадают'
]),
React.createElement('button', {
key: 'reject',
onClick: onReject,
className: "flex-1 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 py-3 px-4 rounded-lg font-medium transition-all duration-200"
}, [
React.createElement('i', {
className: 'fas fa-times mr-2'
}),
'Коды не совпадают'
])
])
])
]);
};
// Enhanced Chat Message with better security indicators
const EnhancedChatMessage = ({ message, type, timestamp }) => {
const formatTime = (ts) => {
return new Date(ts).toLocaleTimeString('ru-RU', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
};
const getMessageStyle = () => {
switch (type) {
case 'sent':
return {
container: "ml-auto bg-orange-500/15 border-orange-500/20 text-primary",
icon: "fas fa-lock accent-orange",
label: "Зашифровано"
};
case 'received':
return {
container: "mr-auto card-minimal text-primary",
icon: "fas fa-unlock-alt accent-green",
label: "Расшифровано"
};
case 'system':
return {
container: "mx-auto bg-yellow-500/10 border border-yellow-500/20 text-yellow-400",
icon: "fas fa-info-circle accent-yellow",
label: "Система"
};
default:
return {
container: "mx-auto card-minimal text-secondary",
icon: "fas fa-circle text-muted",
label: "Неизвестно"
};
}
};
const style = getMessageStyle();
return React.createElement('div', {
className: `message-slide mb-3 p-3 rounded-lg max-w-md break-words ${style.container} border`
}, [
React.createElement('div', {
key: 'content',
className: "flex items-start space-x-2"
}, [
React.createElement('i', {
key: 'icon',
className: `${style.icon} text-sm mt-0.5 opacity-70`
}),
React.createElement('div', {
key: 'text',
className: "flex-1"
}, [
React.createElement('div', {
key: 'message',
className: "text-sm"
}, message),
timestamp && React.createElement('div', {
key: 'meta',
className: "flex items-center justify-between mt-1 text-xs opacity-50"
}, [
React.createElement('span', {
key: 'time'
}, formatTime(timestamp)),
React.createElement('span', {
key: 'status',
className: "text-xs"
}, style.label)
])
])
])
]);
};
// Enhanced Connection Setup with verification
const EnhancedConnectionSetup = ({
messages,
onCreateOffer,
onCreateAnswer,
onConnect,
onClearData,
onVerifyConnection,
connectionStatus,
offerData,
answerData,
offerInput,
setOfferInput,
answerInput,
setAnswerInput,
showOfferStep,
showAnswerStep,
verificationCode,
showVerification,
offerPassword,
answerPassword
}) => {
const [mode, setMode] = React.useState('select');
const resetToSelect = () => {
setMode('select');
onClearData();
};
const handleVerificationConfirm = () => {
onVerifyConnection(true);
};
const handleVerificationReject = () => {
onVerifyConnection(false);
};
if (showVerification) {
return React.createElement('div', {
className: "min-h-[calc(100vh-104px)] flex items-center justify-center p-4"
}, [
React.createElement('div', {
key: 'verification',
className: "w-full max-w-md"
}, [
React.createElement(VerificationStep, {
verificationCode: verificationCode,
onConfirm: handleVerificationConfirm,
onReject: handleVerificationReject
})
])
]);
}
if (mode === 'select') {
return React.createElement('div', {
className: "min-h-[calc(100vh-104px)] flex items-center justify-center p-4"
}, [
React.createElement('div', {
key: 'selector',
className: "w-full max-w-4xl"
}, [
React.createElement('div', {
key: 'header',
className: "text-center mb-8"
}, [
React.createElement('h2', {
key: 'title',
className: "text-2xl font-semibold text-primary mb-3"
}, 'Начать защищенное общение'),
React.createElement('p', {
key: 'subtitle',
className: "text-secondary max-w-2xl mx-auto"
}, "Выберите способ подключения к защищенному каналу связи с ECDH шифрованием и Perfect Forward Secrecy")
]),
React.createElement('div', {
key: 'options',
className: "grid md:grid-cols-2 gap-6 max-w-3xl mx-auto"
}, [
// Create Connection
React.createElement('div', {
key: 'create',
onClick: () => setMode('create'),
className: "card-minimal rounded-xl p-6 cursor-pointer group"
}, [
React.createElement('div', {
key: 'icon',
className: "w-12 h-12 bg-blue-500/10 border border-blue-500/20 rounded-lg flex items-center justify-center mx-auto mb-4"
}, [
React.createElement('i', {
className: 'fas fa-plus text-xl text-blue-400'
})
]),
React.createElement('h3', {
key: 'title',
className: "text-lg font-semibold text-primary text-center mb-3"
}, "Создать канал"),
React.createElement('p', {
key: 'description',
className: "text-secondary text-center text-sm mb-4"
}, "Инициируйте новое защищенное соединение с шифрованным обменом"),
React.createElement('div', {
key: 'features',
className: "space-y-2"
}, [
React.createElement('div', {
key: 'f1',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-key accent-orange mr-2 text-xs'
}),
'Генерация ECDH ключей'
]),
React.createElement('div', {
key: 'f2',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-shield-alt accent-orange mr-2 text-xs'
}),
'Код верификации'
]),
React.createElement('div', {
key: 'f3',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-sync-alt accent-purple mr-2 text-xs'
}),
'PFS ротация ключей'
])
])
]),
// Join Connection
React.createElement('div', {
key: 'join',
onClick: () => setMode('join'),
className: "card-minimal rounded-xl p-6 cursor-pointer group"
}, [
React.createElement('div', {
key: 'icon',
className: "w-12 h-12 bg-green-500/10 border border-green-500/20 rounded-lg flex items-center justify-center mx-auto mb-4"
}, [
React.createElement('i', {
className: 'fas fa-link text-xl accent-green'
})
]),
React.createElement('h3', {
key: 'title',
className: "text-lg font-semibold text-primary text-center mb-3"
}, "Присоединиться"),
React.createElement('p', {
key: 'description',
className: "text-secondary text-center text-sm mb-4"
}, "Подключитесь к существующему защищенному каналу"),
React.createElement('div', {
key: 'features',
className: "space-y-2"
}, [
React.createElement('div', {
key: 'f1',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-paste accent-green mr-2 text-xs'
}),
'Вставка Offer приглашения'
]),
React.createElement('div', {
key: 'f2',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-check-circle accent-green mr-2 text-xs'
}),
'Автоматическая верификация'
]),
React.createElement('div', {
key: 'f3',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-sync-alt accent-purple mr-2 text-xs'
}),
'PFS защита'
])
])
])
]),
React.createElement('div', {
key: 'security-features',
className: "grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 max-w-6xl mx-auto mt-8"
}, [
React.createElement('div', { key: 'feature1', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-green-500/10 border border-green-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-exchange-alt accent-green' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "ECDH Key Exchange"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "Безопасный обмен ключами")
]),
React.createElement('div', { key: 'feature2', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-purple-500/10 border border-purple-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-shield-alt accent-purple' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "Out-of-Band Verification"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "Защита от MITM атак")
]),
React.createElement('div', { key: 'feature3', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-orange-500/10 border border-orange-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-lock accent-orange' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "Шифрование"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "Шифрование + аутентификация")
]),
React.createElement('div', { key: 'feature4', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-cyan-500/10 border border-cyan-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-sync-alt accent-cyan' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "Perfect Forward Secrecy"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "Автоматическая ротация ключей")
]),
React.createElement('div', { key: 'feature5', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-blue-500/10 border border-blue-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-fingerprint accent-blue' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "Цифровые подписи"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "ECDSA верификация сообщений")
]),
React.createElement('div', { key: 'feature6', className: "text-center p-3 sm:p-4" }, [
React.createElement('div', { key: 'icon', className: "w-10 h-10 sm:w-12 sm:h-12 bg-red-500/10 border border-red-500/20 rounded-lg flex items-center justify-center mx-auto mb-2 sm:mb-3" }, [
React.createElement('i', { className: 'fas fa-ban accent-red' })
]),
React.createElement('h4', { key: 'title', className: "text-xs sm:text-sm font-medium text-primary mb-1" }, "Защита от перехвата"),
React.createElement('p', { key: 'desc', className: "text-xs text-muted leading-tight" }, "Шифрованный обмен данными")
])
]),
// Wallet Logos Section
React.createElement('div', {
key: 'wallet-logos-section',
className: "mt-8"
}, [
React.createElement('div', {
key: 'wallet-logos-header',
className: "text-center mb-4"
}, [
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary mb-2"
}, "Поддерживаемые Lightning кошельки"),
React.createElement('p', {
key: 'subtitle',
className: "text-secondary text-sm"
}, "Для оплаты сессий используйте любой из популярных кошельков")
]),
React.createElement('div', {
key: 'wallet-logos-container',
className: "wallet-logos-container"
}, [
React.createElement('div', {
key: 'wallet-logos-track',
className: "wallet-logos-track"
}, [
// First set of logos
React.createElement('a', {
key: 'alby1-link',
href: "https://getalby.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo alby"
}, [
React.createElement('img', {
key: 'alby-img1',
src: "logo/alby.svg",
alt: "Alby Lightning Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'zeus1-link',
href: "https://zeusln.app",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo zeus"
}, [
React.createElement('img', {
key: 'zeus-img1',
src: "logo/zeus.svg",
alt: "Zeus Lightning Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'wos1-link',
href: "https://www.walletofsatoshi.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo wos"
}, [
React.createElement('img', {
key: 'wos-img1',
src: "logo/wos.svg",
alt: "Wallet of Satoshi",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'muun1-link',
href: "https://muun.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo muun"
}, [
React.createElement('img', {
key: 'muun-img1',
src: "logo/muun.svg",
alt: "Muun Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'atomic1-link',
href: "https://atomicwallet.io",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo atomic"
}, [
React.createElement('img', {
key: 'atomic-img1',
src: "logo/atomic.svg",
alt: "Atomic Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'breez1-link',
href: "https://breez.technology/mobile/",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo breez"
}, [
React.createElement('img', {
key: 'breez-img1',
src: "logo/breez.svg",
alt: "Breez Lightning Wallet",
})
]),
React.createElement('a', {
key: 'lightning-labs1-link',
href: "https://lightning.engineering",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo lightning-labs"
}, [
React.createElement('img', {
key: 'lightning-labs-img1',
src: "logo/lightning-labs.svg",
alt: "Lightning Labs",
})
]),
React.createElement('a', {
key: 'lnbits1-link',
href: "https://lnbits.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo lnbits"
}, [
React.createElement('img', {
key: 'lnbits-img1',
src: "logo/lnbits.svg",
alt: "LNbits",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'strike1-link',
href: "https://strike.me",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo strike"
}, [
React.createElement('img', {
key: 'strike-img1',
src: "logo/strike.svg",
alt: "Strike",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'impervious1-link',
href: "https://impervious.ai",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo impervious"
}, [
React.createElement('img', {
key: 'impervious-img1',
src: "logo/impervious.svg",
alt: "Impervious",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'bitcoin-lightning1-link',
href: "https://www.blink.sv/",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo bitcoin-lightning"
}, [
React.createElement('img', {
key: 'blink-img1',
src: "logo/blink.svg",
alt: "Blink Wallet",
className: "wallet-logo-img"
})
]),
// Second set of logos
React.createElement('a', {
key: 'alby2-link',
href: "https://getalby.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo alby"
}, [
React.createElement('img', {
key: 'alby-img2',
src: "logo/alby.svg",
alt: "Alby Lightning Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'zeus2-link',
href: "https://zeusln.app",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo zeus"
}, [
React.createElement('img', {
key: 'zeus-img2',
src: "logo/zeus.svg",
alt: "Zeus Lightning Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'wos2-link',
href: "https://www.walletofsatoshi.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo wos"
}, [
React.createElement('img', {
key: 'wos-img2',
src: "logo/wos.svg",
alt: "Wallet of Satoshi",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'muun2-link',
href: "https://muun.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo muun"
}, [
React.createElement('img', {
key: 'muun-img2',
src: "logo/muun.svg",
alt: "Muun Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'atomic2-link',
href: "https://atomicwallet.io",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo atomic"
}, [
React.createElement('img', {
key: 'atomic-img2',
src: "logo/atomic.svg",
alt: "Atomic Wallet",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'breez2-link',
href: "https://breez.technology/mobile/",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo breez"
}, [
React.createElement('img', {
key: 'breez-img2',
src: "logo/breez.svg",
alt: "Breez Lightning Wallet",
})
]),
React.createElement('a', {
key: 'lightning-labs2-link',
href: "https://lightning.engineering",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo lightning-labs"
}, [
React.createElement('img', {
key: 'lightning-labs-img2',
src: "logo/lightning-labs.svg",
alt: "Lightning Labs",
})
]),
React.createElement('a', {
key: 'lnbits2-link',
href: "https://lnbits.com",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo lnbits"
}, [
React.createElement('img', {
key: 'lnbits-img2',
src: "logo/lnbits.svg",
alt: "LNbits",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'strike2-link',
href: "https://strike.me",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo strike"
}, [
React.createElement('img', {
key: 'strike-img2',
src: "logo/strike.svg",
alt: "Strike",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'impervious2-link',
href: "https://impervious.ai",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo impervious"
}, [
React.createElement('img', {
key: 'impervious-img2',
src: "logo/impervious.svg",
alt: "Impervious",
className: "wallet-logo-img"
})
]),
React.createElement('a', {
key: 'bitcoin-lightning2-link',
href: "https://www.blink.sv/",
target: "_blank",
rel: "noindex nofollow",
className: "wallet-logo bitcoin-lightning"
}, [
React.createElement('img', {
key: 'blink-img2',
src: "logo/blink.svg",
alt: "Blink Wallet",
className: "wallet-logo-img"
})
])
])
])
]),
React.createElement(UniqueFeatureSlider, { key: 'unique-features-slider' }),
React.createElement(ComparisonTable, { key: 'comparison-table' }),
React.createElement(Roadmap, { key: 'roadmap' }),
])
]);
}
if (mode === 'create') {
return React.createElement('div', {
className: "min-h-[calc(100vh-104px)] flex items-center justify-center p-4"
}, [
React.createElement('div', {
key: 'create-flow',
className: "w-full max-w-3xl space-y-6"
}, [
React.createElement('div', {
key: 'header',
className: "text-center"
}, [
React.createElement('button', {
key: 'back',
onClick: resetToSelect,
className: "mb-4 text-secondary hover:text-primary transition-colors flex items-center mx-auto text-sm"
}, [
React.createElement('i', {
className: 'fas fa-arrow-left mr-2'
}),
'Назад к выбору'
]),
React.createElement('h2', {
key: 'title',
className: "text-xl font-semibold text-primary mb-2"
}, 'Создание защищенного канала')
]),
// Step 1
React.createElement('div', {
key: 'step1',
className: "card-minimal rounded-xl p-6"
}, [
React.createElement('div', {
key: 'step-header',
className: "flex items-center mb-4"
}, [
React.createElement('div', {
key: 'number',
className: "step-number mr-3"
}, '1'),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary"
}, "Генерация ECDH ключей и кода верификации")
]),
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm mb-4"
}, "Создание криптографически стойких ключей и кода для защиты от атак"),
React.createElement('button', {
key: 'create-btn',
onClick: onCreateOffer,
disabled: connectionStatus === 'connecting' || showOfferStep,
className: `w-full btn-primary text-white py-3 px-4 rounded-lg font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed`
}, [
React.createElement('i', {
className: 'fas fa-shield-alt mr-2'
}),
showOfferStep ? 'Ключи созданы ✓' : 'Создать защищенные ключи'
]),
showOfferStep && React.createElement('div', {
key: 'offer-result',
className: "mt-6 space-y-4"
}, [
React.createElement('div', {
key: 'success',
className: "p-3 bg-green-500/10 border border-green-500/20 rounded-lg"
}, [
React.createElement('p', {
className: "text-green-400 text-sm font-medium flex items-center"
}, [
React.createElement('i', {
className: 'fas fa-check-circle mr-2'
}),
'Зашифрованное приглашение создано! Отправьте код и пароль собеседнику:'
]),
offerPassword && React.createElement('div', {
key: 'password-display',
className: "mt-3 p-3 bg-blue-500/10 border border-blue-500/20 rounded-lg"
}, [
React.createElement('p', {
key: 'password-label',
className: "text-blue-400 text-sm font-medium mb-2"
}, '🔑 Пароль для расшифровки:'),
React.createElement('div', {
key: 'password-container',
className: "flex items-center space-x-2"
}, [
React.createElement('code', {
key: 'password',
className: "flex-1 p-2 bg-gray-900/50 border border-gray-500/30 rounded font-mono text-sm text-blue-300 font-medium"
}, offerPassword),
React.createElement(EnhancedCopyButton, {
key: 'copy-password',
text: offerPassword,
className: "px-3 py-2 bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 border border-blue-500/30 rounded text-sm"
}, 'Копировать')
])
])
]),
React.createElement('div', {
key: 'offer-data',
className: "space-y-3"
}, [
React.createElement('textarea', {
key: 'textarea',
value: offerData,
readOnly: true,
rows: 8,
className: "w-full p-3 bg-custom-bg border border-gray-500/20 rounded-lg font-mono text-xs text-secondary resize-none custom-scrollbar"
}),
React.createElement(EnhancedCopyButton, {
key: 'copy',
text: offerData,
className: "w-full px-3 py-2 bg-orange-500/10 hover:bg-orange-500/20 text-orange-400 border border-orange-500/20 rounded text-sm font-medium"
}, 'Копировать зашифрованный код')
])
])
]),
// Step 2
showOfferStep && React.createElement('div', {
key: 'step2',
className: "card-minimal rounded-xl p-6"
}, [
React.createElement('div', {
key: 'step-header',
className: "flex items-center mb-4"
}, [
React.createElement('div', {
key: 'number',
className: "w-8 h-8 bg-green-500 text-white rounded-lg flex items-center justify-center font-semibold text-sm mr-3"
}, '2'),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary"
}, "Ожидание ответа собеседника")
]),
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm mb-4"
}, "Вставьте зашифрованный код приглашения от собеседника"),
React.createElement('textarea', {
key: 'input',
value: answerInput,
onChange: (e) => setAnswerInput(e.target.value),
rows: 6,
placeholder: "Вставьте зашифрованный код ответа от собеседника...",
className: "w-full p-3 bg-custom-bg border border-gray-500/20 rounded-lg resize-none mb-4 text-secondary placeholder-gray-500 focus:border-orange-500/40 focus:outline-none transition-all custom-scrollbar text-sm"
}),
React.createElement('button', {
key: 'connect-btn',
onClick: onConnect,
disabled: !answerInput.trim(),
className: "w-full btn-secondary text-white py-3 px-4 rounded-lg font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
}, [
React.createElement('i', {
className: 'fas fa-rocket mr-2'
}),
'Установить соединение'
])
])
])
]);
}
if (mode === 'join') {
return React.createElement('div', {
className: "min-h-[calc(100vh-104px)] flex items-center justify-center p-4"
}, [
React.createElement('div', {
key: 'join-flow',
className: "w-full max-w-3xl space-y-6"
}, [
React.createElement('div', {
key: 'header',
className: "text-center"
}, [
React.createElement('button', {
key: 'back',
onClick: resetToSelect,
className: "mb-4 text-secondary hover:text-primary transition-colors flex items-center mx-auto text-sm"
}, [
React.createElement('i', {
className: 'fas fa-arrow-left mr-2'
}),
'Назад к выбору'
]),
React.createElement('h2', {
key: 'title',
className: "text-xl font-semibold text-primary mb-2"
}, 'Присоединение к защищенному каналу')
]),
// Step 1
React.createElement('div', {
key: 'step1',
className: "card-minimal rounded-xl p-6"
}, [
React.createElement('div', {
key: 'step-header',
className: "flex items-center mb-4"
}, [
React.createElement('div', {
key: 'number',
className: "w-8 h-8 bg-green-500 text-white rounded-lg flex items-center justify-center font-semibold text-sm mr-3"
}, '1'),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary"
}, "Вставка защищенного приглашения")
]),
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm mb-4"
}, "Скопируйте и вставьте зашифрованный код приглашения от инициатора"),
React.createElement('textarea', {
key: 'input',
value: offerInput,
onChange: (e) => setOfferInput(e.target.value),
rows: 8,
placeholder: "Вставьте зашифрованный код приглашения...",
className: "w-full p-3 bg-custom-bg border border-gray-500/20 rounded-lg resize-none mb-4 text-secondary placeholder-gray-500 focus:border-green-500/40 focus:outline-none transition-all custom-scrollbar text-sm"
}),
React.createElement('button', {
key: 'process-btn',
onClick: onCreateAnswer,
disabled: !offerInput.trim() || connectionStatus === 'connecting',
className: "w-full btn-secondary text-white py-3 px-4 rounded-lg font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
}, [
React.createElement('i', {
className: 'fas fa-cogs mr-2'
}),
'Обработать приглашение'
])
]),
// Step 2
showAnswerStep && React.createElement('div', {
key: 'step2',
className: "card-minimal rounded-xl p-6"
}, [
React.createElement('div', {
key: 'step-header',
className: "flex items-center mb-4"
}, [
React.createElement('div', {
key: 'number',
className: "step-number mr-3"
}, '2'),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary"
}, "Отправка защищенного ответа")
]),
React.createElement('div', {
key: 'success',
className: "p-3 bg-green-500/10 border border-green-500/20 rounded-lg mb-4"
}, [
React.createElement('p', {
className: "text-green-400 text-sm font-medium flex items-center"
}, [
React.createElement('i', {
className: 'fas fa-check-circle mr-2'
}),
'Зашифрованный ответ создан! Отправьте этот код инициатору:'
]),
answerPassword && React.createElement('div', {
key: 'password-display',
className: "mt-3 p-3 bg-blue-500/10 border border-blue-500/20 rounded-lg"
}, [
React.createElement('p', {
key: 'password-label',
className: "text-blue-400 text-sm font-medium mb-2"
}, '🔑 Пароль для расшифровки:'),
React.createElement('div', {
key: 'password-container',
className: "flex items-center space-x-2"
}, [
React.createElement('code', {
key: 'password',
className: "flex-1 p-2 bg-gray-900/50 border border-gray-500/30 rounded font-mono text-sm text-blue-300 font-medium"
}, answerPassword),
React.createElement(EnhancedCopyButton, {
key: 'copy-password',
text: answerPassword,
className: "px-3 py-2 bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 border border-blue-500/30 rounded text-sm"
}, 'Копировать')
])
])
]),
React.createElement('div', {
key: 'answer-data',
className: "space-y-3 mb-4"
}, [
React.createElement('textarea', {
key: 'textarea',
value: answerData,
readOnly: true,
rows: 6,
className: "w-full p-3 bg-custom-bg border border-green-500/20 rounded-lg font-mono text-xs text-secondary resize-none custom-scrollbar"
}),
React.createElement(EnhancedCopyButton, {
key: 'copy',
text: answerData,
className: "w-full px-3 py-2 bg-green-500/10 hover:bg-green-500/20 text-green-400 border border-green-500/20 rounded text-sm font-medium"
}, 'Копировать зашифрованный ответ')
]),
React.createElement('div', {
key: 'info',
className: "p-3 bg-purple-500/10 border border-purple-500/20 rounded-lg"
}, [
React.createElement('p', {
className: "text-purple-400 text-sm flex items-center justify-center"
}, [
React.createElement('i', {
className: 'fas fa-shield-alt mr-2'
}),
'Соединение будет установлено с верификацией'
])
])
])
])
]);
}
};
// Enhanced Chat Interface with better security indicators
const EnhancedChatInterface = ({
messages,
messageInput,
setMessageInput,
onSendMessage,
onDisconnect,
keyFingerprint,
isVerified,
chatMessagesRef,
scrollToBottom
}) => {
const [showScrollButton, setShowScrollButton] = React.useState(false);
React.useEffect(() => {
if (chatMessagesRef.current) {
// Плавная прокрутка вниз
chatMessagesRef.current.scrollTo({
top: chatMessagesRef.current.scrollHeight,
behavior: 'smooth'
});
}
}, [messages]);
// Обработчик прокрутки для показа кнопки
const handleScroll = () => {
if (chatMessagesRef.current) {
const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;
const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;
setShowScrollButton(!isNearBottom);
}
};
// Обертка для scrollToBottom с обновлением состояния кнопки
const handleScrollToBottom = () => {
scrollToBottom();
setShowScrollButton(false);
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
onSendMessage();
}
};
return React.createElement('div', {
className: "chat-container"
}, [
// Chat Messages Area
React.createElement('div', {
key: 'chat-body',
className: "chat-messages-area max-w-4xl mx-auto w-full p-4 relative"
}, [
React.createElement('div', {
key: 'messages-container',
ref: chatMessagesRef,
onScroll: handleScroll,
className: "h-full overflow-y-auto space-y-3 custom-scrollbar pr-2 scroll-smooth"
},
messages.length === 0 ?
React.createElement('div', {
key: 'empty-state',
className: "flex items-center justify-center h-full"
}, [
React.createElement('div', {
className: "text-center max-w-md"
}, [
React.createElement('div', {
key: 'icon',
className: "w-16 h-16 bg-green-500/10 border border-green-500/20 rounded-xl flex items-center justify-center mx-auto mb-4"
}, [
React.createElement('i', {
className: 'fas fa-comments text-2xl accent-green'
})
]),
React.createElement('h3', {
key: 'title',
className: "text-lg font-medium text-primary mb-2"
}, "Защищенный канал готов!"),
React.createElement('p', {
key: 'description',
className: "text-secondary text-sm mb-4"
}, "Все сообщения защищены современными криптографическими алгоритмами"),
React.createElement('div', {
key: 'features',
className: "text-left space-y-2"
}, [
React.createElement('div', {
key: 'f1',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-check accent-green mr-3'
}),
'End-to-end шифрование'
]),
React.createElement('div', {
key: 'f2',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-check accent-green mr-3'
}),
'Защита от replay атак'
]),
React.createElement('div', {
key: 'f3',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-check accent-green mr-3'
}),
'Верификация целостности'
]),
React.createElement('div', {
key: 'f4',
className: "flex items-center text-sm text-muted"
}, [
React.createElement('i', {
className: 'fas fa-sync-alt accent-purple mr-3'
}),
'Perfect Forward Secrecy'
])
])
])
]) :
messages.map((msg) =>
React.createElement(EnhancedChatMessage, {
key: msg.id,
message: msg.message,
type: msg.type,
timestamp: msg.timestamp
})
)
)
]),
// Кнопка прокрутки вниз
showScrollButton && React.createElement('button', {
key: 'scroll-down-btn',
onClick: handleScrollToBottom,
className: "absolute bottom-4 right-4 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 shadow-lg",
style: { zIndex: 10 }
}, [
React.createElement('i', {
className: 'fas fa-chevron-down'
})
]),
// Enhanced Chat Input Area
React.createElement('div', {
key: 'chat-input',
className: "chat-input-area border-t border-gray-500/10"
}, [
React.createElement('div', {
key: 'input-container',
className: "max-w-4xl mx-auto p-4"
}, [
React.createElement('div', {
key: 'input-wrapper',
className: "flex items-end space-x-3"
}, [
React.createElement('div', {
key: 'text-area-wrapper',
className: "flex-1 relative"
}, [
React.createElement('textarea', {
key: 'input',
value: messageInput,
onChange: (e) => setMessageInput(e.target.value),
onKeyDown: handleKeyPress,
placeholder: "Введите сообщение для шифрования...",
rows: 2,
maxLength: 2000,
className: "w-full p-3 bg-gray-900/30 border border-gray-500/20 rounded-lg resize-none text-primary placeholder-gray-500 focus:border-green-500/40 focus:outline-none transition-all custom-scrollbar text-sm"
}),
React.createElement('div', {
key: 'input-info',
className: "absolute bottom-2 right-3 flex items-center space-x-2 text-xs text-muted"
}, [
React.createElement('span', {
key: 'counter'
}, `${messageInput.length}/2000`),
React.createElement('span', {
key: 'hint'
}, "• Enter для отправки")
])
]),
React.createElement('button', {
key: 'send',
onClick: onSendMessage,
disabled: !messageInput.trim(),
className: "security-shield text-white p-3 rounded-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
}, [
React.createElement('i', {
className: 'fas fa-paper-plane'
})
])
])
])
])
]);
};
// Main Enhanced Application Component
const EnhancedSecureP2PChat = () => {
const [messages, setMessages] = React.useState([]);
const [connectionStatus, setConnectionStatus] = React.useState('disconnected');
const [messageInput, setMessageInput] = React.useState('');
const [offerData, setOfferData] = React.useState('');
const [answerData, setAnswerData] = React.useState('');
const [offerInput, setOfferInput] = React.useState('');
const [answerInput, setAnswerInput] = React.useState('');
const [keyFingerprint, setKeyFingerprint] = React.useState('');
const [verificationCode, setVerificationCode] = React.useState('');
const [showOfferStep, setShowOfferStep] = React.useState(false);
const [showAnswerStep, setShowAnswerStep] = React.useState(false);
const [showVerification, setShowVerification] = React.useState(false);
const [isVerified, setIsVerified] = React.useState(false);
const [securityLevel, setSecurityLevel] = React.useState(null);
// Password modal state
const [showPasswordModal, setShowPasswordModal] = React.useState(false);
const [passwordInput, setPasswordInput] = React.useState('');
const [passwordAction, setPasswordAction] = React.useState(null); // 'offer' or 'answer'
const [passwordCallback, setPasswordCallback] = React.useState(null);
// Store generated passwords
const [offerPassword, setOfferPassword] = React.useState('');
const [answerPassword, setAnswerPassword] = React.useState('');
// Pay-per-session state
const [sessionManager] = React.useState(() => new PayPerSessionManager());
const [showPaymentModal, setShowPaymentModal] = React.useState(false);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
const [pendingSession, setPendingSession] = React.useState(null); // { type, preimage }
const webrtcManagerRef = React.useRef(null);
// Update security level based on real verification
const updateSecurityLevel = React.useCallback(async () => {
if (webrtcManagerRef.current) {
try {
const level = await webrtcManagerRef.current.calculateSecurityLevel();
setSecurityLevel(level);
} catch (error) {
console.error('Failed to update security level:', error);
setSecurityLevel({
level: 'ERROR',
score: 0,
color: 'red',
details: 'Verification failed'
});
}
}
}, []);
// Session time ticker
React.useEffect(() => {
const timer = setInterval(() => {
if (sessionManager.hasActiveSession()) {
setSessionTimeLeft(sessionManager.getTimeLeft());
} else {
setSessionTimeLeft(0);
}
}, 1000);
return () => clearInterval(timer);
}, [sessionManager]);
// Session expiration handler
React.useEffect(() => {
sessionManager.onSessionExpired = () => {
setMessages(prev => [...prev, {
message: '⏰ Время сессии истекло. Соединение будет разорвано.',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
setTimeout(() => handleDisconnect(), 3000);
};
}, [sessionManager]);
const chatMessagesRef = React.useRef(null);
// Функция прокрутки вниз
const scrollToBottom = () => {
if (chatMessagesRef.current) {
setTimeout(() => {
chatMessagesRef.current.scrollTo({
top: chatMessagesRef.current.scrollHeight,
behavior: 'smooth'
});
}, 100);
}
};
// Password modal functions
const showPasswordPrompt = (action, callback) => {
setPasswordAction(action);
setPasswordCallback(() => callback);
setShowPasswordModal(true);
setPasswordInput('');
};
const handlePasswordSubmit = (password) => {
setShowPasswordModal(false);
if (passwordCallback) {
passwordCallback(password);
}
};
const handlePasswordCancel = () => {
setShowPasswordModal(false);
setPasswordInput('');
setPasswordCallback(null);
};
React.useEffect(() => {
const handleMessage = (message, type) => {
setMessages(prev => [...prev, {
message,
type,
id: Date.now() + Math.random(),
timestamp: Date.now()
}]);
// Прокрутка при получении нового сообщения
setTimeout(scrollToBottom, 100);
};
const handleStatusChange = (status) => {
setConnectionStatus(status);
if (status === 'connected') {
setIsVerified(true);
setShowVerification(false);
updateSecurityLevel().catch(console.error);
} else if (status === 'verifying') {
setShowVerification(true);
updateSecurityLevel().catch(console.error);
} else if (status === 'connecting') {
updateSecurityLevel().catch(console.error);
} else if (status === 'disconnected') {
// Полная очистка UI при отключении
setKeyFingerprint('');
setVerificationCode('');
setSecurityLevel(null);
setIsVerified(false);
setShowVerification(false);
} else if (status === 'peer_disconnected') {
// Небольшая задержка перед очисткой для показа статуса
setTimeout(() => {
setKeyFingerprint('');
setVerificationCode('');
setSecurityLevel(null);
setIsVerified(false);
setShowVerification(false);
}, 2000);
}
};
const handleKeyExchange = (fingerprint) => {
if (fingerprint === '') {
setKeyFingerprint('');
} else {
setKeyFingerprint(fingerprint);
}
};
const handleVerificationRequired = (code) => {
if (code === '') {
setVerificationCode('');
} else {
setVerificationCode(code);
}
};
// Callback для обработки ошибок ответа
const handleAnswerError = (errorType, errorMessage) => {
console.log('Answer error callback:', errorType, errorMessage);
if (errorType === 'replay_attack') {
// Сбрасываем сессию при replay attack
if (sessionManager.hasActiveSession()) {
sessionManager.resetSession();
setSessionTimeLeft(0);
}
setPendingSession(null);
setMessages(prev => [...prev, {
message: '💡 Данные устарели. Создайте новое приглашение или используйте актуальный код ответа',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
} else if (errorType === 'security_violation') {
// Сбрасываем сессию при нарушении безопасности
if (sessionManager.hasActiveSession()) {
sessionManager.resetSession();
setSessionTimeLeft(0);
}
setPendingSession(null);
setMessages(prev => [...prev, {
message: `🔒 Нарушение безопасности: ${errorMessage}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
}
};
webrtcManagerRef.current = new EnhancedSecureWebRTCManager(
handleMessage,
handleStatusChange,
handleKeyExchange,
handleVerificationRequired,
handleAnswerError
);
handleMessage('🚀 LockBit.chat Enhanced Edition инициализирован. Готов к установке защищенного соединения с ECDH, шифрованным обменом и верификацией.', 'system');
// Cleanup on page unload
const handleBeforeUnload = () => {
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
if (webrtcManagerRef.current) {
webrtcManagerRef.current.disconnect();
}
};
}, []);
const ensureActiveSessionOrPurchase = async () => {
if (sessionManager.hasActiveSession()) return true;
if (pendingSession) return true; // уже куплено, активируем на коннекте
setShowPaymentModal(true);
return false;
};
const handleCreateOffer = async () => {
try {
const ok = await ensureActiveSessionOrPurchase();
if (!ok) return;
setOfferData('');
setShowOfferStep(false);
const offer = await webrtcManagerRef.current.createSecureOffer();
// Generate secure password for encryption
const password = EnhancedSecureCryptoUtils.generateSecurePassword();
// Encrypt the offer data
const encryptedOffer = await EnhancedSecureCryptoUtils.encryptData(offer, password);
setOfferData(encryptedOffer);
setOfferPassword(password);
setShowOfferStep(true);
setMessages(prev => [...prev, {
message: '🔐 Защищенное приглашение создано и зашифровано!',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
setMessages(prev => [...prev, {
message: '📤 Отправьте зашифрованный код и пароль собеседнику по защищенному каналу (голос, SMS, etc).',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
updateSecurityLevel().catch(console.error);
} catch (error) {
setMessages(prev => [...prev, {
message: `❌ Ошибка создания приглашения: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
}
};
const handleCreateAnswer = async () => {
try {
if (!offerInput.trim()) {
setMessages(prev => [...prev, {
message: '⚠️ Необходимо вставить зашифрованный код приглашения от собеседника',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
return;
}
// Show password modal for offer decryption
showPasswordPrompt('offer', async (password) => {
if (!password) {
setMessages(prev => [...prev, {
message: '❌ Пароль не введен',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
return;
}
try {
setMessages(prev => [...prev, {
message: '🔄 Расшифровываю и обрабатываю защищенное приглашение...',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
setAnswerData('');
setShowAnswerStep(false);
let offer;
try {
// Decrypt the offer data
offer = await EnhancedSecureCryptoUtils.decryptData(offerInput.trim(), password);
} catch (decryptError) {
throw new Error(`Ошибка расшифровки: ${decryptError.message}`);
}
if (!offer || typeof offer !== 'object') {
throw new Error('Приглашение должно быть объектом');
}
if (!offer.type || offer.type !== 'enhanced_secure_offer') {
throw new Error('Неверный тип приглашения. Ожидается enhanced_secure_offer');
}
console.log('Creating secure answer for offer:', offer);
const answer = await webrtcManagerRef.current.createSecureAnswer(offer);
console.log('Secure answer created:', answer);
// Generate new password for answer encryption
const answerPassword = EnhancedSecureCryptoUtils.generateSecurePassword();
// Encrypt the answer data
const encryptedAnswer = await EnhancedSecureCryptoUtils.encryptData(answer, answerPassword);
setAnswerData(encryptedAnswer);
setAnswerPassword(answerPassword); // Store the password
setShowAnswerStep(true);
setMessages(prev => [...prev, {
message: '✅ Защищенный ответ создан и зашифрован!',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
setMessages(prev => [...prev, {
message: '📤 Отправьте зашифрованный код ответа и пароль инициатору по защищенному каналу.',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Update security level after creating answer
updateSecurityLevel().catch(console.error);
} catch (error) {
console.error('Error in handleCreateAnswer:', error);
setMessages(prev => [...prev, {
message: `❌ Ошибка обработки приглашения: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
}
});
return; // Exit early, callback will handle the rest
} catch (error) {
console.error('Error in handleCreateAnswer:', error);
setMessages(prev => [...prev, {
message: `❌ Ошибка обработки приглашения: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
}
};
const handleConnect = async () => {
try {
if (!answerInput.trim()) {
setMessages(prev => [...prev, {
message: '⚠️ Необходимо вставить зашифрованный код ответа от собеседника',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
return;
}
// Show password modal for answer decryption
showPasswordPrompt('answer', async (password) => {
if (!password) {
setMessages(prev => [...prev, {
message: '❌ Пароль не введен',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
return;
}
try {
setMessages(prev => [...prev, {
message: '🔄 Расшифровываю и обрабатываю защищенный ответ...',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
let answer;
try {
// Decrypt the answer data
answer = await EnhancedSecureCryptoUtils.decryptData(answerInput.trim(), password);
} catch (decryptError) {
throw new Error(`Ошибка расшифровки: ${decryptError.message}`);
}
if (!answer || typeof answer !== 'object') {
throw new Error('Ответ должен быть объектом');
}
if (!answer.type || answer.type !== 'enhanced_secure_answer') {
throw new Error('Неверный тип ответа. Ожидается enhanced_secure_answer');
}
await webrtcManagerRef.current.handleSecureAnswer(answer);
// Активируем сессию ТОЛЬКО после успешной обработки ответа
if (sessionManager.canActivateSession() && pendingSession) {
const result = sessionManager.safeActivateSession(pendingSession.type, pendingSession.preimage, pendingSession.paymentHash);
if (result.success) {
setPendingSession(null);
setSessionTimeLeft(sessionManager.getTimeLeft()); // Обновляем таймер
setMessages(prev => [...prev, {
message: `💰 Сессия активирована на ${sessionManager.sessionPrices[pendingSession.type].hours}ч (${result.method})`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
} else {
setMessages(prev => [...prev, {
message: `❌ Ошибка активации сессии: ${result.reason}`,
type: 'error',
id: Date.now(),
timestamp: Date.now()
}]);
}
}
setMessages(prev => [...prev, {
message: '🔄 Финализация защищенного соединения...',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Update security level after handling answer
updateSecurityLevel().catch(console.error);
} catch (error) {
setMessages(prev => [...prev, {
message: `❌ Ошибка установки соединения: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Обработка ошибок теперь происходит в callback handleAnswerError
// Дополнительная обработка только для общих ошибок
if (!error.message.includes('слишком старые') && !error.message.includes('too old')) {
setPendingSession(null);
}
}
});
return; // Exit early, callback will handle the rest
} catch (error) {
setMessages(prev => [...prev, {
message: `❌ Ошибка установки соединения: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Обработка ошибок теперь происходит в callback handleAnswerError
// Дополнительная обработка только для общих ошибок
if (!error.message.includes('слишком старые') && !error.message.includes('too old')) {
setPendingSession(null);
}
}
};
const handleVerifyConnection = (isValid) => {
if (isValid) {
webrtcManagerRef.current.confirmVerification();
} else {
setMessages(prev => [...prev, {
message: '❌ Верификация отклонена. Соединение небезопасно! Сессия сброшена.',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Сбрасываем сессию при неудачной верификации
sessionManager.resetSession();
setSessionTimeLeft(0);
setPendingSession(null);
handleDisconnect();
}
};
const handleSendMessage = async () => {
if (!messageInput.trim() || !webrtcManagerRef.current.isConnected()) {
return;
}
try {
await webrtcManagerRef.current.sendSecureMessage(messageInput);
setMessageInput('');
// Прокрутка после отправки сообщения
setTimeout(scrollToBottom, 50);
} catch (error) {
setMessages(prev => [...prev, {
message: `❌ Ошибка отправки: ${error.message}`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Прокрутка после ошибки
setTimeout(scrollToBottom, 50);
}
};
const handleClearData = () => {
setOfferData('');
setAnswerData('');
setOfferInput('');
setAnswerInput('');
setShowOfferStep(false);
setShowAnswerStep(false);
setShowVerification(false);
setVerificationCode('');
setIsVerified(false);
setKeyFingerprint('');
setSecurityLevel(null);
setConnectionStatus('disconnected');
setMessages([]);
setMessageInput('');
setOfferPassword(''); // Clear offer password
setAnswerPassword(''); // Clear answer password
if (typeof console.clear === 'function') {
console.clear();
}
// Cleanup pay-per-session state
sessionManager.cleanup();
setSessionTimeLeft(0);
setShowPaymentModal(false);
};
const handleDisconnect = () => {
// Cleanup pay-per-session state
sessionManager.cleanup();
setSessionTimeLeft(0);
setShowPaymentModal(false);
webrtcManagerRef.current.disconnect();
// Полная очистка UI состояния
setKeyFingerprint('');
setVerificationCode('');
setSecurityLevel(null);
setIsVerified(false);
setShowVerification(false);
setMessages(prev => [...prev, {
message: '🔌 Защищенное соединение разорвано',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
handleClearData();
};
const handleSessionActivated = (session) => {
setMessages(prev => [...prev, {
message: `💰 Сессия активирована на ${sessionManager.sessionPrices[session.type].hours}ч. Можете создавать приглашения!`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
};
React.useEffect(() => {
if (connectionStatus === 'connected' && isVerified) {
setMessages(prev => [...prev, {
message: '🎉 Защищенное соединение успешно установлено и верифицировано! Теперь вы можете безопасно общаться с полной защитой от MITM атак и Perfect Forward Secrecy.',
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
// Прокрутка при подключении
setTimeout(scrollToBottom, 200);
}
}, [connectionStatus, isVerified]);
const isConnectedAndVerified = connectionStatus === 'connected' && isVerified;
// Активировать сессию при коннекте инициатора (когда мы создаем offer и получаем connected)
React.useEffect(() => {
if (isConnectedAndVerified && sessionManager.canActivateSession() && pendingSession && connectionStatus !== 'failed') {
const result = sessionManager.safeActivateSession(pendingSession.type, pendingSession.preimage, pendingSession.paymentHash);
if (result.success) {
setPendingSession(null);
setSessionTimeLeft(sessionManager.getTimeLeft()); // Обновляем таймер
setMessages(prev => [...prev, {
message: `💰 Сессия активирована на ${sessionManager.sessionPrices[pendingSession.type].hours}ч (${result.method})`,
type: 'system',
id: Date.now(),
timestamp: Date.now()
}]);
} else {
setMessages(prev => [...prev, {
message: `❌ Ошибка активации сессии: ${result.reason}`,
type: 'error',
id: Date.now(),
timestamp: Date.now()
}]);
}
}
}, [isConnectedAndVerified, sessionManager, pendingSession, connectionStatus]);
return React.createElement('div', {
className: "minimal-bg min-h-screen"
}, [
React.createElement(EnhancedMinimalHeader, {
key: 'header',
status: connectionStatus,
fingerprint: keyFingerprint,
verificationCode: verificationCode,
onDisconnect: handleDisconnect,
isConnected: isConnectedAndVerified,
securityLevel: securityLevel,
sessionManager: sessionManager,
sessionTimeLeft: sessionTimeLeft
}),
React.createElement('main', {
key: 'main'
},
isConnectedAndVerified
? React.createElement(EnhancedChatInterface, {
messages: messages,
messageInput: messageInput,
setMessageInput: setMessageInput,
onSendMessage: handleSendMessage,
onDisconnect: handleDisconnect,
keyFingerprint: keyFingerprint,
isVerified: isVerified,
chatMessagesRef: chatMessagesRef,
scrollToBottom: scrollToBottom
})
: React.createElement(EnhancedConnectionSetup, {
onCreateOffer: handleCreateOffer,
onCreateAnswer: handleCreateAnswer,
onConnect: handleConnect,
onClearData: handleClearData,
onVerifyConnection: handleVerifyConnection,
connectionStatus: connectionStatus,
offerData: offerData,
answerData: answerData,
offerInput: offerInput,
setOfferInput: setOfferInput,
answerInput: answerInput,
setAnswerInput: setAnswerInput,
showOfferStep: showOfferStep,
showAnswerStep: showAnswerStep,
verificationCode: verificationCode,
showVerification: showVerification,
messages: messages,
offerPassword: offerPassword,
answerPassword: answerPassword
})
),
// Password Modal
React.createElement(PasswordModal, {
key: 'password-modal',
isOpen: showPasswordModal,
onClose: handlePasswordCancel,
onSubmit: handlePasswordSubmit,
action: passwordAction,
password: passwordInput,
setPassword: setPasswordInput
}),
// Payment Modal
React.createElement(PaymentModal, {
key: 'payment-modal',
isOpen: showPaymentModal,
onClose: () => setShowPaymentModal(false),
sessionManager: sessionManager,
onSessionPurchased: (session) => { setPendingSession(session); handleSessionActivated({ type: session.type }); }
})
]);
};
function initializeApp() {
// Проверяем что оба модуля загружены
if (window.EnhancedSecureCryptoUtils && window.EnhancedSecureWebRTCManager) {
console.log('🚀 Инициализируем React приложение...');
ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));
} else {
console.error('❌ Модули не загружены:', {
hasCrypto: !!window.EnhancedSecureCryptoUtils,
hasWebRTC: !!window.EnhancedSecureWebRTCManager
});
}
}
// Render Enhanced Application
// ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));
</script>
<script type="module">
try {
const timestamp = Date.now();
const [cryptoModule, webrtcModule, paymentModule] = await Promise.all([
import(`./src/crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
import(`./src/network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
import(`./src/session/PayPerSessionManager.js?v=${timestamp}`)
]);
// Добавьте эти строки:
const { EnhancedSecureCryptoUtils } = cryptoModule;
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
const { EnhancedSecureWebRTCManager } = webrtcModule;
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
const { PayPerSessionManager } = paymentModule;
window.PayPerSessionManager = PayPerSessionManager;
// Функция загрузки компонентов React через fetch
async function loadReactComponent(path, componentName) {
try {
console.log(`🔄 Загружаем компонент ${componentName} из ${path}`);
const response = await fetch(`${path}?v=${timestamp}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const componentCode = await response.text();
// Выполняем код компонента в глобальном контексте
eval(componentCode);
// Проверяем, что компонент загружен
if (window[componentName]) {
console.log(`✅ Компонент ${componentName} успешно загружен`);
} else {
throw new Error(`Компонент ${componentName} не найден в window после загрузки`);
}
} catch (error) {
console.error(`❌ Ошибка загрузки компонента ${componentName}:`, error);
throw error;
}
}
// Загружаем UI компоненты поэтапно
console.log('🚀 Начинаем загрузку UI компонентов...');
await Promise.all([
loadReactComponent('./src/components/ui/SessionTimer.jsx', 'SessionTimer'),
loadReactComponent('./src/components/ui/Header.jsx', 'EnhancedMinimalHeader'),
loadReactComponent('./src/components/ui/PasswordModal.jsx', 'PasswordModal'),
loadReactComponent('./src/components/ui/SessionTypeSelector.jsx', 'SessionTypeSelector'),
loadReactComponent('./src/components/ui/LightningPayment.jsx', 'LightningPayment'),
loadReactComponent('./src/components/ui/PaymentModal.jsx', 'PaymentModal')
]);
console.log('🎉 UI компоненты успешно загружены!');
console.log('📋 Доступные компоненты:', {
EnhancedMinimalHeader: !!window.EnhancedMinimalHeader,
SessionTimer: !!window.SessionTimer
});
if (typeof initializeApp === 'function') {
initializeApp();
} else {
console.error('❌ Функция initializeApp не найдена');
}
} catch (error) {
console.error('❌ Ошибка загрузки модуля:', error);
console.error('📍 Стек:', error.stack);
// Показываем подробную ошибку на странице
document.getElementById('root').innerHTML = `
<div style="padding: 20px; color: #ef4444; font-family: monospace; background: #1a1a1a; min-height: 100vh;">
<h2>❌ Ошибка загрузки модуля</h2>
<p><strong>Ошибка:</strong> ${error.message}</p>
<div style="margin-top: 20px; padding: 15px; background: rgba(245, 158, 11, 0.1); border-radius: 8px;">
<h3 style="color: #f59e0b;">🔧 Возможные решения:</h3>
<ol style="color: #f59e0b;">
<li>Убедитесь, что файлы находятся в src/components/ui/</li>
<li>Проверьте SessionTimer.jsx и Header.jsx</li>
<li>Убедитесь, что файлы используют window.React</li>
<li>Перезагрузите страницу (Ctrl+F5)</li>
<li>Откройте Developer Tools и проверьте Network tab</li>
</ol>
</div>
<pre style="background: rgba(0,0,0,0.3); padding: 10px; border-radius: 5px; margin-top: 15px; overflow: auto;">${error.stack}</pre>
</div>
`;
}
</script>
</body>
</html>