Add direct gateway forwarding when VPN is off
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { api } from '../api.js';
|
||||
import {
|
||||
connectionAction,
|
||||
copyText,
|
||||
formatConnectionDuration,
|
||||
localProxyUrls,
|
||||
subscriptionDomain,
|
||||
@@ -35,13 +36,15 @@ export function ClientOverviewPage({
|
||||
const [now, setNow] = useState(Date.now());
|
||||
const [editingSubscription, setEditingSubscription] = useState(!state?.hasSubscription);
|
||||
const [pings, setPings] = useState({});
|
||||
const [copiedProxy, setCopiedProxy] = useState('');
|
||||
const [accessTab, setAccessTab] = useState('gateway');
|
||||
const [copyFeedback, setCopyFeedback] = useState(null);
|
||||
const [refreshingInfo, setRefreshingInfo] = useState(false);
|
||||
const [usageUpdated, setUsageUpdated] = useState(false);
|
||||
const [serverRevealVersion, setServerRevealVersion] = useState(0);
|
||||
const [serversLeaving, setServersLeaving] = useState(false);
|
||||
const subscriptionInputRef = useRef(null);
|
||||
const subscriptionRef = useRef(null);
|
||||
const copyTimerRef = useRef(null);
|
||||
const serverKey = servers.map((server) => `${server.tag}:${server.server}:${server.server_port}`).join('|');
|
||||
const gatewayAddress = isGateway ? window.location.hostname : '127.0.0.1';
|
||||
const proxyUrls = localProxyUrls(state?.proxyPort, gatewayAddress);
|
||||
@@ -133,6 +136,8 @@ export function ClientOverviewPage({
|
||||
return () => cancelAnimationFrame(frame);
|
||||
}, [usage.used]);
|
||||
|
||||
useEffect(() => () => clearTimeout(copyTimerRef.current), []);
|
||||
|
||||
async function toggleConnection() {
|
||||
const action = connectionAction({ connected, selectedTag, configExists: state?.configExists });
|
||||
if (action?.type === 'stop') return onStop();
|
||||
@@ -154,13 +159,14 @@ export function ClientOverviewPage({
|
||||
}
|
||||
|
||||
async function copyProxy(kind) {
|
||||
const value = kind === 'gateway' ? gatewayAddress : proxyUrls[kind];
|
||||
clearTimeout(copyTimerRef.current);
|
||||
setCopyFeedback({ kind, failed: false });
|
||||
copyTimerRef.current = setTimeout(() => setCopyFeedback(null), 800);
|
||||
try {
|
||||
await navigator.clipboard.writeText(kind === 'gateway' ? gatewayAddress : proxyUrls[kind]);
|
||||
setCopiedProxy(kind);
|
||||
setTimeout(() => setCopiedProxy(''), 800);
|
||||
await copyText(value);
|
||||
} catch {
|
||||
setCopiedProxy('error');
|
||||
setTimeout(() => setCopiedProxy(''), 800);
|
||||
setCopyFeedback({ kind, failed: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +200,7 @@ export function ClientOverviewPage({
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={connected}
|
||||
aria-label={connected ? `Выключить ${isGateway ? 'Gateway' : 'VPN'}` : `Включить ${isGateway ? 'Gateway' : 'VPN'}`}
|
||||
aria-label={connected ? 'Выключить VPN' : 'Включить VPN'}
|
||||
disabled={busy || (!connected && !canStart)}
|
||||
onClick={toggleConnection}
|
||||
>
|
||||
@@ -204,9 +210,7 @@ export function ClientOverviewPage({
|
||||
</button>
|
||||
<div className="client-state-copy" aria-live="polite">
|
||||
<h2 key={connected ? 'connected' : 'disconnected'} id="connection-title">
|
||||
{isGateway
|
||||
? connected ? 'Gateway включён' : 'Gateway выключен'
|
||||
: connected ? 'VPN включён' : 'VPN выключен'}
|
||||
{connected ? 'VPN включён' : 'VPN выключен'}
|
||||
</h2>
|
||||
<div className="client-state-detail">
|
||||
{connected ? (
|
||||
@@ -221,24 +225,42 @@ export function ClientOverviewPage({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="client-proxies" aria-label={isGateway ? 'Адреса Gateway и Proxy' : 'Локальный прокси'}>
|
||||
<section className={`client-proxies${isGateway ? ' has-tabs' : ''}`} aria-label={isGateway ? 'Gateway и Local Proxy' : 'Локальный прокси'}>
|
||||
{isGateway && (
|
||||
<div className="client-access-point">
|
||||
<span className="client-proxy-label">Gateway</span>
|
||||
<div className="client-access-tabs" role="tablist" aria-label="Способ подключения">
|
||||
{[
|
||||
['gateway', 'Gateway'],
|
||||
['proxy', 'Local Proxy'],
|
||||
].map(([tab, label]) => (
|
||||
<button
|
||||
className={`client-access-tab${accessTab === tab ? ' is-active' : ''}`}
|
||||
type="button"
|
||||
role="tab"
|
||||
key={tab}
|
||||
aria-selected={accessTab === tab}
|
||||
onClick={() => setAccessTab(tab)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{(isGateway ? accessTab === 'gateway' : false) ? (
|
||||
<div className="client-access-point" role="tabpanel" key="gateway">
|
||||
<strong className="client-proxy-address">{gatewayAddress}</strong>
|
||||
<button
|
||||
className={copiedProxy === 'gateway' ? 'is-copied' : ''}
|
||||
className={`client-copy-button${copyFeedback?.kind === 'gateway' ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
|
||||
type="button"
|
||||
aria-label={`Скопировать Gateway: ${gatewayAddress}`}
|
||||
onClick={() => copyProxy('gateway')}
|
||||
>
|
||||
<span className="client-copy-label">КОПИРОВАТЬ</span>
|
||||
{copiedProxy === 'gateway' && <span className="client-copy-feedback">Copied</span>}
|
||||
{copyFeedback?.kind === 'gateway' && <span className="client-copy-feedback">{copyFeedback.failed ? 'Error' : 'Copied'}</span>}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="client-access-point">
|
||||
<span className="client-proxy-label">{isGateway ? 'Proxy' : 'Адрес'}</span>
|
||||
) : (
|
||||
<div className="client-access-point" role={isGateway ? 'tabpanel' : undefined} key="proxy">
|
||||
{!isGateway && <span className="client-proxy-label">Адрес</span>}
|
||||
<strong className="client-proxy-address">
|
||||
{proxyUrls.http.replace(/^https?:\/\//, '')}
|
||||
</strong>
|
||||
@@ -248,18 +270,19 @@ export function ClientOverviewPage({
|
||||
['http', 'HTTP'],
|
||||
].map(([kind, label]) => (
|
||||
<button
|
||||
className={copiedProxy === kind ? 'is-copied' : ''}
|
||||
className={`client-copy-button${copyFeedback?.kind === kind ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
|
||||
type="button"
|
||||
key={kind}
|
||||
aria-label={`Скопировать ${label}: ${proxyUrls[kind]}`}
|
||||
onClick={() => copyProxy(kind)}
|
||||
>
|
||||
<span className="client-copy-label">{label}</span>
|
||||
{copiedProxy === kind && <span className="client-copy-feedback">Copied</span>}
|
||||
{copyFeedback?.kind === kind && <span className="client-copy-feedback">{copyFeedback.failed ? 'Error' : 'Copied'}</span>}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user