// VPN connection guide — Apple-light style const { useState: useStateC, useEffect: useEffectC } = React; // SiteNav from nav.jsx // ───────── Hero ───────── function Hero(){ return (
ЧАСТНЫЙ СЕРВЕР

Подключение к VPN

Используйте приложение AmneziaVPN. Конфигурацию выдаёт администратор — импортируйте файл или ключ, который вам передали, и включите туннель. Ниже — пошаговые инструкции для WireGuard и Amnezia VPN.

); } const DEFAULT_SERVER_IP = "SERVER_IP"; // ───────── Server info card ───────── function ServerCard(){ const [copied,setCopied] = useStateC(false); const [copiedProxy,setCopiedProxy] = useStateC(false); const [config,setConfig] = useStateC({ server_ip: DEFAULT_SERVER_IP, telegram_proxy: "", telegram_proxy_label: "Прокси Telegram", }); useEffectC(() => { fetch("/data/site-config.json?t=" + Date.now()) .then(r => r.ok ? r.json() : null) .then(data => { if (data) setConfig(prev => ({ ...prev, ...data })); }) .catch(() => {}); }, []); const serverIp = (config.server_ip || DEFAULT_SERVER_IP).trim(); const proxyLabel = config.telegram_proxy_label || "Прокси Telegram"; const proxyUrl = (config.telegram_proxy || "").trim(); function copyIp(){ navigator.clipboard?.writeText(serverIp); setCopied(true); setTimeout(()=>setCopied(false), 1600); } function copyProxy(){ if (!proxyUrl) return; navigator.clipboard?.writeText(proxyUrl); setCopiedProxy(true); setTimeout(()=>setCopiedProxy(false), 1600); } return (
Сервер
Адрес
{serverIp}
{proxyLabel}
{proxyUrl ? (
{proxyUrl}
) : (
Не настроено

Администратор может добавить ссылку в{" "} /var/www/domain-web/data/site-config.json {" "}→ поле telegram_proxy. Подробнее — в data/README.txt на сервере.

)}
Сервисы на машине: XRay (TCP) AmneziaWG (UDP)

Номера портов и тип протокола уже зашиты в конфиг Amnezia — вручную их обычно не вводят.

); } // ───────── Big segmented control ───────── function BigSegment({ value, options, onChange }){ return (
{options.map(o=>{ const on = value===o.id; return ( ); })}
); } // ───────── OS tabs (smaller, pill) ───────── function OSTabs({ value, onChange }){ const os = [ {id:"ios", label:"iOS"}, {id:"android", label:"Android"}, {id:"windows", label:"Windows"}, {id:"macos", label:"macOS"} ]; return (
{os.map(o=>{ const on = value===o.id; return ( ); })}
); } // ───────── Numbered step ───────── function Step({ n, children }){ return (
{n}
{children}
); } function SuccessStep({ children }){ return (
{children}
); } // ───────── Steps card ───────── function StepsCard({ protocol, os }){ // Headlines per protocol / OS const titles = { wg:{ ios:{name:"WireGuard на iOS", sub:"iPhone и iPad · iOS 15 и новее", color:"linear-gradient(160deg,#5ac8fa,#0a84ff)", icon:"lock"}, android:{name:"WireGuard на Android", sub:"Android 8 и новее", color:"linear-gradient(160deg,#7ce0a2,#2cb67d)", icon:"lock"}, windows:{name:"WireGuard на Windows", sub:"Windows 10 / 11", color:"linear-gradient(160deg,#7ec2ff,#0a84ff)", icon:"lock"}, macos:{name:"WireGuard на macOS", sub:"macOS 12 и новее", color:"linear-gradient(160deg,#bdbdc0,#6e6e73)", icon:"lock"}, }, awg:{ ios:{name:"Amnezia VPN на iOS", sub:"AmneziaWG · обход DPI", color:"linear-gradient(160deg,#a385ff,#6a44f5)", icon:"shield"}, android:{name:"Amnezia VPN на Android", sub:"AmneziaWG · обход DPI", color:"linear-gradient(160deg,#9b82ff,#6a44f5)", icon:"shield"}, windows:{name:"Amnezia VPN на Windows", sub:"AmneziaWG · обход DPI", color:"linear-gradient(160deg,#a385ff,#6a44f5)", icon:"shield"}, macos:{name:"Amnezia VPN на macOS", sub:"AmneziaWG · обход DPI", color:"linear-gradient(160deg,#9b82ff,#6a44f5)", icon:"shield"}, }, }; const t = titles[protocol][os]; // Steps — protocol/os specific let steps; if (protocol==="wg"){ if (os==="ios") steps = [ <>Откройте App Store, найдите WireGuard и нажмите «Установить», <>Откройте приложение, нажмите + в правом верхнем углу, <> Выберите «Сканировать QR-код» и наведите камеру на QR — конфиг добавится автоматически Или «Создать из файла» → выберите файл .conf , <>Придумайте имя туннелю и нажмите «Сохранить», <>Нажмите тумблер → появится запрос, нажмите «Разрешить», ]; else if (os==="android") steps = [ <>Установите WireGuard из Google Play или скачайте .apk с wireguard.com, <>Откройте приложение, тапните по «+» внизу справа, <> Выберите «Сканировать из QR-кода» и наведите камеру на QR Или «Импорт из файла» → найдите .conf в загрузках , <>Дайте имя туннелю и подтвердите создание, <>Переключите тумблер вправо — разрешите создание VPN-подключения, ]; else if (os==="windows") steps = [ <>Скачайте WireGuard for Windows с wireguard.com/install, <>Установите и откройте приложение, <>Нажмите «Импорт туннеля из файла» и выберите .conf, <>Подтвердите создание туннеля, <>Нажмите «Подключить» — индикатор станет зелёным, ]; else steps = [ <>Установите WireGuard из Mac App Store, <>Откройте приложение в строке меню, <>Выберите «Импорт туннеля из файла».conf, <>Подтвердите добавление профиля в Системные настройки, <>Включите тумблер и подтвердите системный запрос, ]; } else { // Amnezia VPN (same flow across OS — slight wording variations) steps = [ <>Скачайте приложение AmneziaVPN с amnezia.org или из стора своей платформы, <>Запустите приложение и согласитесь с условиями использования, <> Нажмите «Добавить конфигурацию»«Сканировать QR-код» Или импорт ключа: вставьте строку вида vpn://… , <>При запросе системы — разрешите создание VPN-профиля, <>Нажмите большую кнопку «Подключиться» — индикатор станет зелёным, ]; } return (

{t.name}

{t.sub}
{steps.map((s,i)=>{s})} Тумблер зелёный — вы подключены. Значок VPN в строке статуса.
); } function FineNote({ children }){ return (
{children}
); } // ───────── Region change steps ───────── function RegionCard({ os }){ const cfg = { ios:{name:"Смена региона App Store на iPhone", sub:"iOS · Apple ID", color:"linear-gradient(160deg,#ffd166,#ff9500)"}, android:{name:"Смена региона Google Play", sub:"Android · Google аккаунт", color:"linear-gradient(160deg,#7ce0a2,#2cb67d)"}, windows:{name:"Смена региона Microsoft Store", sub:"Windows 10 / 11", color:"linear-gradient(160deg,#7ec2ff,#0a84ff)"}, macos:{name:"Смена региона Mac App Store", sub:"macOS", color:"linear-gradient(160deg,#bdbdc0,#6e6e73)"}, }; const t = cfg[os] || cfg.ios; let steps; if (os==="ios" || os==="macos") steps = [ <>Подключите VPN к серверу нужной страны, <>Откройте «Настройки» → ваше имя → «Медиаматериалы и покупки», <>Нажмите «Просмотреть учётную запись» → подтвердите вход, <>Тапните «Страна или регион»«Изменить страну или регион», <>Выберите страну, примите условия, заполните адрес и способ оплаты «Нет», ]; else if (os==="android") steps = [ <>Подключите VPN к серверу нужной страны, <>Очистите кеш и данные Google Play Store в настройках устройства, <>Откройте Play Store → меню → «Настройки» → «Общие» → «Настройки аккаунта», <>Выберите «Страна и профили», подтвердите смену, <>Дождитесь до 24 часов и проверьте — регион обновится, ]; else steps = [ <>Подключите VPN к серверу нужной страны, <>Откройте «Параметры» → «Время и язык» → «Язык и регион», <>В разделе «Страна или регион» выберите нужную страну, <>Перезапустите Microsoft Store, <>Войдите заново под нужным аккаунтом, ]; return (

{t.name}

{t.sub}
{steps.map((s,i)=>{s})} Регион изменён — теперь доступны приложения этой страны.
); } // ───────── Verify section ───────── function VerifySection(){ return (

Как проверить, что VPN работает?

{[ {n:1, text:<>Откройте 2ip.ru или whatismyip.com}, {n:2, text:<>Страна должна измениться на страну вашего сервера}, {n:3, text:<>Попробуйте открыть нужный сайт — загрузится}, ].map(c=>(
{c.n}
{c.text}
))}
); } function cstyle(){ return { background:"var(--bg-tint)", border:"1px solid var(--line)", padding:"2px 8px", borderRadius:8, fontSize:12.5, color:"var(--ink-2)" }; } // ───────── Troubleshooting ───────── function Troubleshooting(){ const items = [ { title:"VPN не подключается", icon:"refresh", color:"linear-gradient(160deg,#ff8a8a,#ff3b30)", points:[ "Перезапустите приложение", "Выключите/включите Wi-Fi или мобильный интернет", "Удалите туннель и добавьте конфиг заново", "Попробуйте другую сеть", ]}, { title:"VPN включён, нет интернета", icon:"globe", color:"linear-gradient(160deg,#ffd166,#ff9500)", points:[ "Отключитесь и подключитесь снова", "Убедитесь, что конфиг актуальный", "Перезагрузите устройство", "Напишите в поддержку", ]}, { title:"Сайты не открываются", icon:"search", color:"linear-gradient(160deg,#7ec2ff,#0a84ff)", points:[ "Очистите кеш браузера", "Смените DNS на 1.1.1.1", "Откройте в режиме инкогнито", "Попробуйте другой браузер", ]}, { title:"Медленная скорость", icon:"gauge", color:"linear-gradient(160deg,#a385ff,#6a44f5)", points:[ "Проверьте скорость без VPN", "Переподключитесь", "Закройте лишние приложения", "Переключитесь Wi-Fi ↔ 4G", ]}, ]; return (

Что делать, если что-то пошло не так

{items.map(it=>(
{it.title}
    {it.points.map((p,i)=>(
  • {p}
  • ))}
))}
); } // ───────── Footer ───────── function Footer(){ return ( ); } // ───────── Main App ───────── function ConnectApp(){ const initialMode = (typeof URLSearchParams !== "undefined" && new URLSearchParams(location.search).get("mode") === "region") ? "region" : "connect"; const [mode, setMode] = useStateC(initialMode); // connect | region const [protocol, setProtocol] = useStateC("wg"); // wg | awg const [os, setOs] = useStateC("ios"); useEffectC(() => { const params = new URLSearchParams(location.search); if (params.get("mode") === "region") setMode("region"); }, []); return (
{/* Mode big segment */}
{/* Protocol (only for connect mode) */} {mode==="connect" && (
)} {/* OS */}
{/* Steps */} {mode==="connect" ? : }
); } ReactDOM.createRoot(document.getElementById("root")).render();