feat: link mac client to shared gateway proxy
This commit is contained in:
@@ -26,6 +26,7 @@ function CopyField({ label, value }) {
|
||||
|
||||
function ClientHero({ state, status, activeServer }) {
|
||||
const homeBypass = Boolean(state?.clientSettings?.homeBypassEnabled);
|
||||
const sharedProxy = Boolean(state?.clientSettings?.sharedProxyEnabled);
|
||||
const cfg = {
|
||||
running: {
|
||||
title: 'Локальный proxy работает',
|
||||
@@ -58,7 +59,15 @@ function ClientHero({ state, status, activeServer }) {
|
||||
kind: 'neutral',
|
||||
},
|
||||
}[status] || {};
|
||||
const view = homeBypass
|
||||
const view = sharedProxy
|
||||
? {
|
||||
...cfg,
|
||||
title: 'Общий gateway proxy',
|
||||
hint: 'Локальный proxy отправляет трафик на серверный gateway',
|
||||
badge: 'Gateway',
|
||||
kind: 'success',
|
||||
}
|
||||
: homeBypass
|
||||
? {
|
||||
...cfg,
|
||||
title: 'Домашний режим: VPN выключен',
|
||||
@@ -83,7 +92,13 @@ function ClientHero({ state, status, activeServer }) {
|
||||
<div className="client-hero-meta">
|
||||
<div>
|
||||
<small className="muted">Активный сервер</small>
|
||||
<strong>{homeBypass ? 'Не используется дома' : activeServer ? `${flagFor(activeServer)} ${activeServer.tag}` : state?.selectedTag || 'Не выбран'}</strong>
|
||||
<strong>
|
||||
{sharedProxy
|
||||
? `${state?.clientSettings?.sharedProxy?.host}:${state?.clientSettings?.sharedProxy?.port}`
|
||||
: homeBypass
|
||||
? 'Не используется дома'
|
||||
: activeServer ? `${flagFor(activeServer)} ${activeServer.tag}` : state?.selectedTag || 'Не выбран'}
|
||||
</strong>
|
||||
</div>
|
||||
<div>
|
||||
<small className="muted">Трафик</small>
|
||||
@@ -112,6 +127,7 @@ function ClientSetup({
|
||||
const selected = pendingTag || state?.selectedTag || '';
|
||||
const canApply = selected && selected !== state?.selectedTag;
|
||||
const homeBypass = Boolean(state?.clientSettings?.homeBypassEnabled);
|
||||
const sharedProxy = Boolean(state?.clientSettings?.sharedProxyEnabled);
|
||||
|
||||
return (
|
||||
<div className="card client-setup">
|
||||
@@ -157,7 +173,9 @@ function ClientSetup({
|
||||
</button>
|
||||
</div>
|
||||
<small className="field-hint">
|
||||
{homeBypass
|
||||
{sharedProxy
|
||||
? 'Включён общий gateway: локальный VPN-сервер не используется.'
|
||||
: homeBypass
|
||||
? 'Домашний режим включён: proxy-трафик сейчас идёт напрямую без VPN.'
|
||||
: 'В Mac-клиенте весь трафик, который приложение отправит в proxy, идёт через выбранный VPN.'}
|
||||
</small>
|
||||
@@ -166,6 +184,60 @@ function ClientSetup({
|
||||
);
|
||||
}
|
||||
|
||||
function SharedProxyCard({ settings, busy, onCheck, onSave }) {
|
||||
const enabled = Boolean(settings?.sharedProxyEnabled);
|
||||
const sharedProxy = settings?.sharedProxy;
|
||||
const [draftUrl, setDraftUrl] = useState(settings?.sharedProxyControlUrl || '');
|
||||
|
||||
useEffect(() => {
|
||||
setDraftUrl(settings?.sharedProxyControlUrl || '');
|
||||
}, [settings?.sharedProxyControlUrl]);
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h2>Общий gateway</h2>
|
||||
<span className={`badge ${enabled ? 'success' : 'neutral'}`}>
|
||||
{enabled ? 'Подключён' : 'Не выбран'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="muted">
|
||||
Укажите адрес серверного UI. Mac-клиент проверит <code>/api/shared-proxy</code> и переключит локальный proxy на общий gateway.
|
||||
</p>
|
||||
<div className="field">
|
||||
<label className="field-label">Адрес gateway</label>
|
||||
<div className="subscription-input">
|
||||
<input
|
||||
className="input"
|
||||
placeholder="http://192.168.50.111:3456"
|
||||
value={draftUrl}
|
||||
onChange={(e) => setDraftUrl(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && draftUrl && onCheck(draftUrl)}
|
||||
/>
|
||||
<button className="btn btn-primary" disabled={busy || !draftUrl} onClick={() => onCheck(draftUrl)}>
|
||||
Проверить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{sharedProxy && (
|
||||
<div className="copy-stack" style={{ marginTop: 12 }}>
|
||||
<CopyField label="Gateway SOCKS5" value={`socks5://${sharedProxy.host}:${sharedProxy.port}`} />
|
||||
</div>
|
||||
)}
|
||||
{enabled && (
|
||||
<button
|
||||
className="btn btn-ghost sm"
|
||||
style={{ marginTop: 12 }}
|
||||
disabled={busy}
|
||||
onClick={() => onSave({ ...settings, sharedProxyEnabled: false })}
|
||||
>
|
||||
Вернуться к локальному VPN
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ProxyCard({ state, settings, busy, onSave }) {
|
||||
const range = state?.clientProxyPortRange || { start: 8080, end: 8090 };
|
||||
const port = settings?.proxyPort || state?.proxyPort || 8080;
|
||||
@@ -225,6 +297,7 @@ function ProxyCard({ state, settings, busy, onSave }) {
|
||||
|
||||
function HomeBypassCard({ state, settings, busy, onSave }) {
|
||||
const enabled = Boolean(settings?.homeBypassEnabled);
|
||||
const sharedProxy = Boolean(settings?.sharedProxyEnabled);
|
||||
const port = settings?.proxyPort || state?.proxyPort || 8080;
|
||||
|
||||
return (
|
||||
@@ -247,7 +320,11 @@ function HomeBypassCard({ state, settings, busy, onSave }) {
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
disabled={busy}
|
||||
onChange={(e) => onSave({ ...settings, homeBypassEnabled: e.target.checked })}
|
||||
onChange={(e) => onSave({
|
||||
...settings,
|
||||
homeBypassEnabled: e.target.checked,
|
||||
sharedProxyEnabled: e.target.checked ? false : sharedProxy,
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -256,11 +333,16 @@ function HomeBypassCard({ state, settings, busy, onSave }) {
|
||||
|
||||
function ClientFlow({ state, activeServer }) {
|
||||
const homeBypass = Boolean(state?.clientSettings?.homeBypassEnabled);
|
||||
const sharedProxy = Boolean(state?.clientSettings?.sharedProxyEnabled);
|
||||
const shared = state?.clientSettings?.sharedProxy;
|
||||
const steps = [
|
||||
{ label: 'Mac', value: 'приложения' },
|
||||
{ label: 'Локальный proxy', value: `127.0.0.1:${state?.proxyPort || 8080}` },
|
||||
{ label: homeBypass ? 'Домашняя сеть' : 'VPN-сервер', value: homeBypass ? 'напрямую' : activeServer?.tag || state?.selectedTag || 'не выбран' },
|
||||
{ label: 'Интернет', value: state?.singboxRunning ? homeBypass ? 'без VPN' : 'через VPN' : 'ожидает' },
|
||||
{
|
||||
label: sharedProxy ? 'Общий gateway' : homeBypass ? 'Домашняя сеть' : 'VPN-сервер',
|
||||
value: sharedProxy ? `${shared?.host}:${shared?.port}` : homeBypass ? 'напрямую' : activeServer?.tag || state?.selectedTag || 'не выбран',
|
||||
},
|
||||
{ label: 'Интернет', value: state?.singboxRunning ? sharedProxy ? 'через gateway' : homeBypass ? 'без VPN' : 'через VPN' : 'ожидает' },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -305,6 +387,7 @@ export function ClientOverviewPage({
|
||||
setPendingTag,
|
||||
clientSettings,
|
||||
onSaveClientSettings,
|
||||
onCheckSharedProxy,
|
||||
onFetchSubscription,
|
||||
onApply,
|
||||
onRestart,
|
||||
@@ -324,6 +407,12 @@ export function ClientOverviewPage({
|
||||
onFetchSubscription={onFetchSubscription}
|
||||
onApply={onApply}
|
||||
/>
|
||||
<SharedProxyCard
|
||||
settings={clientSettings}
|
||||
busy={busy}
|
||||
onCheck={onCheckSharedProxy}
|
||||
onSave={onSaveClientSettings}
|
||||
/>
|
||||
<div className="grid-2">
|
||||
<ProxyCard
|
||||
state={state}
|
||||
|
||||
Reference in New Issue
Block a user