Update VPN client and gateway behavior
Build and Deploy Gateway / build-and-push (push) Successful in 19s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-11 08:50:44 +03:00
parent 396c5d1917
commit 79ffff194f
35 changed files with 1036 additions and 590 deletions
+27 -33
View File
@@ -4,13 +4,14 @@ import {
useState,
type FormEvent,
type ReactNode,
type RefObject,
} from 'react';
import { flushSync } from 'react-dom';
import { canAppendRouteRule } from '../../../shared/routingRules.js';
import type { RouteRule } from '../../../shared/contracts/state.js';
import { operationBlocked } from '../../state/operations.js';
import { ConfirmationDialog } from '../../ui/ConfirmationDialog.js';
import { Drawer } from '../../ui/Drawer.js';
import { RailAction } from '../../ui/RailAction.js';
const ROUTE_RULE_OPTIONS: Array<[RouteRule['type'], string]> = [
['domain', 'Точный домен'],
@@ -393,18 +394,22 @@ export function RoutingToggle({
}) {
const disabled = gatewayDirect || (isGateway && !hasSubscription);
return (
<button
ref={feature.toggleRef}
className={`client-local-rules-toggle${feature.isOpen ? ' is-open' : ''}${feature.pendingRestart ? ' has-pending' : ''}`}
type="button"
<RailAction
buttonRef={feature.toggleRef}
className={`client-local-rules-toggle${feature.pendingRestart ? ' has-pending' : ''}`}
open={feature.isOpen}
controls="client-local-rules"
disabled={disabled}
aria-expanded={feature.isOpen}
aria-controls="client-local-rules"
aria-label={disabled
ariaLabel={disabled
? hasSubscription
? 'Локальные правила недоступны: сейчас работают правила Harbor Gateway'
: 'Локальные правила недоступны: сначала добавьте подписку'
: feature.isOpen ? 'Закрыть локальные правила' : 'Настроить локальные правила'}
label={disabled
? hasSubscription
? 'Локальные правила недоступны: сейчас работают правила Gateway'
: 'Сначала добавьте подписку'
: feature.pendingRestart ? 'Правила ждут перезапуска' : 'Локальные правила'}
onClick={() => feature.isOpen ? feature.requestClose() : onOpen()}
>
<svg viewBox="0 0 24 24" aria-hidden="true">
@@ -412,12 +417,7 @@ export function RoutingToggle({
<circle className="client-rail-rule-knob is-top" cx="15" cy="7" r="2" />
<circle className="client-rail-rule-knob is-bottom" cx="9" cy="17" r="2" />
</svg>
<span>{disabled
? hasSubscription
? 'Локальные правила недоступны: сейчас работают правила Gateway'
: 'Сначала добавьте подписку'
: feature.pendingRestart ? 'Правила ждут перезапуска' : 'Локальные правила'}</span>
</button>
</RailAction>
);
}
@@ -446,22 +446,17 @@ export function RoutingPanel({ feature, statusSlot }: { feature: RoutingFeature;
const incomplete = draftRules.some((rule) => !String(rule.value || '').trim());
return (
<aside
ref={feature.panelRef as RefObject<HTMLElement>}
<Drawer
panelRef={feature.panelRef}
closeRef={feature.closeRef}
id="client-local-rules"
className={`client-drawer client-local-rules${feature.isOpen ? ' is-open' : ''}`}
aria-labelledby="local-rules-title"
aria-hidden={!feature.isOpen}
inert={!feature.isOpen ? true : undefined}
className="client-local-rules"
sheetClassName="client-local-rules-sheet"
open={feature.isOpen}
labelledBy="local-rules-title"
closeLabel="Закрыть локальные правила"
onClose={feature.requestClose}
>
<div className="client-drawer-sheet client-local-rules-sheet">
<button
ref={feature.closeRef}
className="client-drawer-close"
type="button"
aria-label="Закрыть локальные правила"
onClick={feature.requestClose}
>×</button>
<header className="client-local-rules-header">
<span>Маршрутизация</span>
<button
@@ -532,7 +527,7 @@ export function RoutingPanel({ feature, statusSlot }: { feature: RoutingFeature;
/>
<span className="client-local-rule-status" role="status">{statusLabel}</span>
<button
className="client-local-rule-delete"
className="client-row-delete"
type="button"
aria-label={`Удалить правило ${index + 1}`}
onClick={() => feature.remove(rule._key)}
@@ -549,8 +544,8 @@ export function RoutingPanel({ feature, statusSlot }: { feature: RoutingFeature;
})}
{!feature.rules.length && <p className="client-local-rules-empty">Правил пока нет. Весь трафик идёт через VPN.</p>}
</div>
<div className="client-local-rule-add-slot">
<button className="client-local-rule-add" type="button" disabled={!canAdd} onClick={feature.add}>
<div className="client-row-add-slot">
<button className="client-row-add" type="button" disabled={!canAdd} onClick={feature.add}>
+ Добавить правило
</button>
<span className={incomplete ? 'is-visible' : ''}>Сначала заполните текущее правило</span>
@@ -565,8 +560,7 @@ export function RoutingPanel({ feature, statusSlot }: { feature: RoutingFeature;
<button type="button" onClick={feature.requestClose}>Отмена</button>
</div>
</form>
</div>
</aside>
</Drawer>
);
}