1
0
Fork 0

working on 4th case

This commit is contained in:
koplenov 2026-04-05 05:55:51 +03:00
parent 41085f3c01
commit bbd8620e94
2 changed files with 396 additions and 6 deletions

View file

@ -1,7 +1,11 @@
import React, { useState } from 'react';
import './AmneziaVPN.css';
const AmneziaVPN: React.FC = () => {
interface AmneziaVPNProps {
onConnectedChange?: (connected: boolean) => void;
}
const AmneziaVPN: React.FC<AmneziaVPNProps> = ({ onConnectedChange }) => {
const [connected, setConnected] = useState(() => localStorage.getItem('amnezia_connected') === 'true');
const [connecting, setConnecting] = useState(false);
@ -9,12 +13,14 @@ const AmneziaVPN: React.FC = () => {
if (connected) {
setConnected(false);
localStorage.setItem('amnezia_connected', 'false');
onConnectedChange?.(false);
} else {
setConnecting(true);
setTimeout(() => {
setConnecting(false);
setConnected(true);
localStorage.setItem('amnezia_connected', 'true');
onConnectedChange?.(true);
}, 1500);
}
};

View file

@ -1,18 +1,402 @@
import React, { useState } from 'react';
import type { WallpaperType } from '../App';
import MainApp from '../MainApp';
import AmneziaVPN from '../apps/amneziavpn/AmneziaVPN';
interface Case3DesktopProps {
interface Case4DesktopProps {
onComplete: (type: WallpaperType) => void;
}
const Case4Desktop: React.FC<Case3DesktopProps> = ({ onComplete }) => {
type OpenWindow = 'vpn' | 'browser' | null;
const Case4Desktop: React.FC<Case4DesktopProps> = ({ onComplete }) => {
const [vpnConnected, setVpnConnected] = useState(
() => localStorage.getItem('amnezia_connected') === 'true'
);
const [openWindow, setOpenWindow] = useState<OpenWindow>(null);
const [showVpnWarning, setShowVpnWarning] = useState(false);
const [browserPage, setBrowserPage] = useState<'new-tab' | 'bank'>('new-tab');
const [completed, setCompleted] = useState(false);
const openBrowser = () => {
if (!vpnConnected) {
setShowVpnWarning(true);
} else {
setOpenWindow('browser');
}
};
const handleWarningContinue = () => {
setShowVpnWarning(false);
setOpenWindow('browser');
};
return (
<div>
<MainApp showGosuslugi={false} showAmnezia={true}/>
<div style={s.root}>
{/* Фон — кафе */}
<div style={s.cafeBar}>
<span style={s.wifiIcon}>📶</span>
<span style={s.wifiText}>Подключено: <b>CafeNet_Free</b></span>
<span style={s.wifiUnsafe}> Публичная сеть</span>
</div>
{/* Легенда */}
<div style={s.story}>
<p>Вы пришли в кафе и подключились к бесплатному Wi-Fi. Ваш трафик может перехватываться.<br/>
Перед тем как открывать браузер рекомендуется включить VPN.</p>
</div>
{/* Иконки рабочего стола */}
<div style={s.desktop}>
<button style={s.icon} onClick={() => setOpenWindow('vpn')}>
<span style={s.iconImg}>🔒</span>
<span style={s.iconLabel}>AmneziaVPN</span>
</button>
<button style={s.icon} onClick={openBrowser}>
<span style={s.iconImg}>🌐</span>
<span style={s.iconLabel}>Браузер</span>
</button>
</div>
{/* Статус VPN */}
<div style={{ ...s.vpnStatus, ...(vpnConnected ? s.vpnOn : s.vpnOff) }}>
{vpnConnected ? '🛡 VPN включён — соединение защищено' : '⚡ VPN выключен — трафик не защищён'}
</div>
{/* Кнопка завершения */}
{vpnConnected && !completed && (
<button style={s.completeBtn} onClick={() => { setCompleted(true); onComplete('win10'); }}>
Завершить кейс
</button>
)}
{/* Окно VPN */}
{openWindow === 'vpn' && (
<div style={s.windowOverlay}>
<div style={s.window}>
<div style={s.windowBar}>
<span>AmneziaVPN</span>
<button style={s.closeBtn} onClick={() => setOpenWindow(null)}></button>
</div>
<AmneziaVPN onConnectedChange={setVpnConnected} />
</div>
</div>
)}
{/* Окно браузера */}
{openWindow === 'browser' && (
<div style={s.windowOverlay}>
<div style={{ ...s.window, width: 700, maxWidth: '95vw' }}>
<div style={s.windowBar}>
<span>Браузер</span>
<button style={s.closeBtn} onClick={() => { setOpenWindow(null); setBrowserPage('new-tab'); }}></button>
</div>
<div style={s.browserChrome}>
<div style={s.urlBar}>
{vpnConnected && <span style={s.httpsIcon}>🔒</span>}
{!vpnConnected && <span style={s.httpIcon}></span>}
<span style={s.urlText}>
{browserPage === 'bank' ? 'http://bank-online.ru' : 'about:newtab'}
</span>
</div>
</div>
<div style={s.browserContent}>
{browserPage === 'new-tab' ? (
<div style={s.newTab}>
<div style={s.newTabTitle}>Новая вкладка</div>
{!vpnConnected && (
<div style={s.browserWarn}>
Вы используете незащищённое соединение. Ваш трафик виден другим пользователям сети.
</div>
)}
<div style={s.bookmarks}>
<button style={s.bookmark} onClick={() => setBrowserPage('bank')}>
🏦 Онлайн-банк
</button>
</div>
</div>
) : (
<div style={s.bankPage}>
{!vpnConnected && (
<div style={s.interceptAlert}>
<div style={s.interceptTitle}>🚨 ВНИМАНИЕ: Перехват трафика!</div>
<div style={s.interceptBody}>
Злоумышленник в сети перехватил ваш запрос к банку.<br/>
Логин, пароль и данные карты переданы в открытом виде через HTTP.<br/><br/>
<b>Если бы был включён VPN</b> весь трафик был бы зашифрован и недоступен для перехвата.
</div>
<button style={s.warnBtn} onClick={() => setBrowserPage('new-tab')}> Назад</button>
</div>
)}
{vpnConnected && (
<div style={s.safeBank}>
<div style={s.safeBankTitle}>🏦 Онлайн-банк</div>
<div style={s.safeBankBody}>
🛡 Соединение защищено VPN-шифрованием.<br/>
Ваши данные надёжно зашифрованы и недоступны другим пользователям сети.
</div>
<button style={s.warnBtn} onClick={() => setBrowserPage('new-tab')}> Назад</button>
</div>
)}
</div>
)}
</div>
</div>
</div>
)}
{/* Предупреждение при открытии браузера без VPN */}
{showVpnWarning && (
<div style={s.modalOverlay}>
<div style={s.modal}>
<div style={s.modalIcon}></div>
<div style={s.modalTitle}>VPN не подключён</div>
<div style={s.modalText}>
Вы подключены к публичной сети <b>CafeNet_Free</b>.<br/>
Без VPN ваш трафик может быть перехвачен злоумышленниками в этой же сети.<br/><br/>
Рекомендуется сначала включить AmneziaVPN.
</div>
<div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
<button style={s.modalBtnSecondary} onClick={() => { setShowVpnWarning(false); setOpenWindow('vpn'); }}>
Включить VPN
</button>
<button style={s.modalBtnDanger} onClick={handleWarningContinue}>
Всё равно открыть
</button>
</div>
</div>
</div>
)}
</div>
);
};
const s: Record<string, React.CSSProperties> = {
root: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #1a0a00 0%, #0d1117 50%, #001a1a 100%)',
color: '#ccc',
fontFamily: "'Share Tech Mono', monospace",
position: 'relative',
overflow: 'hidden',
},
cafeBar: {
background: 'rgba(0,0,0,0.6)',
borderBottom: '1px solid rgba(0,255,255,0.15)',
padding: '10px 24px',
display: 'flex',
alignItems: 'center',
gap: 12,
fontSize: 14,
},
wifiIcon: { fontSize: 18 },
wifiText: { color: '#ccc' },
wifiUnsafe: { color: '#f5a623', marginLeft: 8 },
story: {
maxWidth: 700,
margin: '24px auto 0',
padding: '16px 24px',
background: 'rgba(0,255,255,0.04)',
border: '1px solid rgba(0,255,255,0.15)',
borderRadius: 8,
fontSize: 14,
lineHeight: 1.7,
color: '#aaa',
textAlign: 'center',
},
desktop: {
display: 'flex',
gap: 40,
justifyContent: 'center',
marginTop: 48,
},
icon: {
background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 12,
padding: '20px 28px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 10,
transition: 'all 0.2s',
color: '#ccc',
},
iconImg: { fontSize: 40 },
iconLabel: { fontSize: 13, fontFamily: "'Share Tech Mono', monospace" },
vpnStatus: {
textAlign: 'center',
marginTop: 32,
fontSize: 13,
padding: '8px 20px',
borderRadius: 6,
display: 'inline-block',
position: 'relative',
left: '50%',
transform: 'translateX(-50%)',
},
vpnOn: { background: 'rgba(0,255,65,0.1)', border: '1px solid #00FF41', color: '#00FF41' },
vpnOff: { background: 'rgba(245,166,35,0.1)', border: '1px solid #f5a623', color: '#f5a623' },
completeBtn: {
display: 'block',
margin: '24px auto 0',
padding: '12px 36px',
background: '#00FF41',
border: 'none',
borderRadius: 6,
color: '#000',
fontFamily: "'Orbitron', monospace",
fontSize: 14,
fontWeight: 700,
cursor: 'pointer',
},
windowOverlay: {
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 100,
},
window: {
background: '#0d0d22',
border: '1px solid rgba(0,255,255,0.25)',
borderRadius: 10,
overflow: 'hidden',
width: 360,
boxShadow: '0 0 40px rgba(0,255,255,0.1)',
},
windowBar: {
background: '#080818',
borderBottom: '1px solid rgba(0,255,255,0.15)',
padding: '10px 16px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 13,
color: '#00FFFF',
fontFamily: "'Orbitron', monospace",
},
closeBtn: {
background: 'transparent',
border: '1px solid rgba(255,0,64,0.4)',
color: '#ff6b6b',
cursor: 'pointer',
borderRadius: 4,
padding: '2px 8px',
fontSize: 12,
},
browserChrome: {
background: '#080818',
borderBottom: '1px solid rgba(0,255,255,0.1)',
padding: '8px 12px',
},
urlBar: {
background: '#0a0a1a',
border: '1px solid rgba(0,255,255,0.2)',
borderRadius: 4,
padding: '6px 12px',
fontSize: 13,
display: 'flex',
alignItems: 'center',
gap: 8,
},
httpsIcon: { color: '#00FF41', fontSize: 14 },
httpIcon: { color: '#f5a623', fontSize: 14 },
urlText: { color: '#888', flex: 1 },
browserContent: { minHeight: 300, background: '#0a0a0a' },
newTab: { padding: 24 },
newTabTitle: { color: '#555', fontSize: 13, marginBottom: 16, fontFamily: "'Orbitron', monospace" },
browserWarn: {
background: 'rgba(245,166,35,0.1)',
border: '1px solid #f5a623',
borderRadius: 6,
padding: '10px 14px',
fontSize: 13,
color: '#f5a623',
marginBottom: 20,
},
bookmarks: { display: 'flex', gap: 12, flexWrap: 'wrap' },
bookmark: {
background: 'rgba(0,255,255,0.05)',
border: '1px solid rgba(0,255,255,0.2)',
borderRadius: 8,
padding: '12px 18px',
color: '#aaa',
cursor: 'pointer',
fontSize: 13,
fontFamily: "'Share Tech Mono', monospace",
},
bankPage: { padding: 24 },
interceptAlert: {
background: 'rgba(255,0,64,0.08)',
border: '1px solid #FF0040',
borderRadius: 8,
padding: 20,
},
interceptTitle: { color: '#FF0040', fontFamily: "'Orbitron', monospace", fontSize: 15, marginBottom: 12, fontWeight: 700 },
interceptBody: { color: '#ccc', fontSize: 13, lineHeight: 1.7, marginBottom: 16 },
safeBank: {
background: 'rgba(0,255,65,0.06)',
border: '1px solid #00FF41',
borderRadius: 8,
padding: 20,
},
safeBankTitle: { color: '#00FF41', fontFamily: "'Orbitron', monospace", fontSize: 15, marginBottom: 12, fontWeight: 700 },
safeBankBody: { color: '#ccc', fontSize: 13, lineHeight: 1.7, marginBottom: 16 },
warnBtn: {
background: 'transparent',
border: '1px solid rgba(0,255,255,0.3)',
color: '#888',
borderRadius: 6,
padding: '8px 16px',
cursor: 'pointer',
fontSize: 13,
fontFamily: "'Share Tech Mono', monospace",
},
modalOverlay: {
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.7)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 200,
},
modal: {
background: '#0d0d22',
border: '1px solid #f5a623',
borderRadius: 10,
padding: '32px 28px',
maxWidth: 420,
textAlign: 'center',
boxShadow: '0 0 30px rgba(245,166,35,0.2)',
},
modalIcon: { fontSize: 40, marginBottom: 12 },
modalTitle: { color: '#f5a623', fontFamily: "'Orbitron', monospace", fontSize: 16, marginBottom: 12, fontWeight: 700 },
modalText: { color: '#aaa', fontSize: 13, lineHeight: 1.7, marginBottom: 24 },
modalBtnSecondary: {
background: '#00FF41',
border: 'none',
borderRadius: 6,
padding: '10px 20px',
color: '#000',
cursor: 'pointer',
fontFamily: "'Orbitron', monospace",
fontSize: 12,
fontWeight: 700,
},
modalBtnDanger: {
background: 'transparent',
border: '1px solid rgba(255,0,64,0.5)',
borderRadius: 6,
padding: '10px 20px',
color: '#FF0040',
cursor: 'pointer',
fontFamily: "'Share Tech Mono', monospace",
fontSize: 12,
},
};
export default Case4Desktop;