Enable connectivity diagnostics in Connect mode
Build and Deploy Gateway / build-and-push (push) Successful in 13s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-07 21:43:09 +03:00
parent 70cc221f34
commit d32bc14108
12 changed files with 60 additions and 31 deletions
+1 -2
View File
@@ -135,7 +135,7 @@ const deviceInventory = settings.appMode === 'gateway'
vendor: createVendorLookup(),
})
: null;
const localConnectivityDiagnostics = settings.appMode === 'gateway' && !remoteDataplane
const localConnectivityDiagnostics = !remoteDataplane
? createConnectivityDiagnosticsService({ proxyPort: settings.diagnosticsProxyPort })
: null;
let subscriptionRefreshPromise = null;
@@ -699,7 +699,6 @@ async function handleApi(req, res) {
}
if (req.method === 'POST' && req.url === '/api/diagnostics/connectivity') {
if (settings.appMode !== 'gateway') throw new HarborError('ENDPOINT_NOT_FOUND');
const { services = [] } = await readBody(req);
const state = stateStore.read();
const appliedServerId = state.appliedServerId || state.selectedServerId;
+12 -10
View File
@@ -25,12 +25,10 @@ export function buildGatewayConfig(subscriptionConfig, selectedTag, {
} = {}) {
const clientMode = settings.appMode === 'client';
const directClient = clientMode && clientDirect;
const vpnOutbound = directClient
? null
: structuredClone(findOutbound(subscriptionConfig, selectedTag));
if (!directClient && !vpnOutbound) throw new HarborError('SERVER_NOT_FOUND');
if (vpnOutbound && !vpnOutbound.tag) vpnOutbound.tag = 'vpn-out';
if (vpnOutbound?.type === 'vless' && !vpnOutbound.packet_encoding) {
const vpnOutbound = structuredClone(findOutbound(subscriptionConfig, selectedTag));
if (!vpnOutbound) throw new HarborError('SERVER_NOT_FOUND');
if (!vpnOutbound.tag) vpnOutbound.tag = 'vpn-out';
if (vpnOutbound.type === 'vless' && !vpnOutbound.packet_encoding) {
vpnOutbound.packet_encoding = 'xudp';
}
const outboundTag = directClient ? 'direct' : vpnOutbound.tag;
@@ -52,20 +50,24 @@ export function buildGatewayConfig(subscriptionConfig, selectedTag, {
sniff: true,
set_system_proxy: false,
},
...(!clientMode ? [{
{
type: 'mixed',
tag: DIAGNOSTICS_INBOUND,
listen: '127.0.0.1',
listen_port: settings.diagnosticsProxyPort,
sniff: true,
set_system_proxy: false,
}] : []),
},
];
const directRules = normalizeRouteRules(routeRules)
.filter((rule) => rule.enabled)
.map((rule) => ({ [rule.type]: [rule.value], outbound: 'direct' }));
const rules = clientMode
? [...directRules, { inbound: [MIXED_INBOUND], outbound: outboundTag }]
? [
{ inbound: [DIAGNOSTICS_INBOUND], outbound: vpnOutbound.tag },
...directRules,
{ inbound: [MIXED_INBOUND], outbound: outboundTag },
]
: [
{ inbound: [DIAGNOSTICS_INBOUND], outbound: outboundTag },
...directRules,
@@ -81,7 +83,7 @@ export function buildGatewayConfig(subscriptionConfig, selectedTag, {
dns: { independent_cache: true },
inbounds,
outbounds: [
...(vpnOutbound ? [vpnOutbound] : []),
vpnOutbound,
{ type: 'direct', tag: 'direct' },
{ type: 'block', tag: 'block' },
],
+3 -3
View File
@@ -1,7 +1,7 @@
export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.16.2',
gatewayClient: '0.17.2',
gatewayBackend: '0.17.1',
macClient: '0.17.1',
gatewayClient: '0.18.1',
gatewayBackend: '0.18.0',
});
export function parseVersion(value) {
+5 -4
View File
@@ -1168,13 +1168,13 @@ export function ClientOverviewPage({
</svg>
<span>Устройства</span>
</button>}
{isGateway && <button
<button
ref={diagnosticsToggleRef}
className={`client-instructions-toggle client-diagnostics-toggle${diagnosticsOpen ? ' is-open' : ''}`}
type="button"
aria-expanded={diagnosticsOpen}
aria-controls="client-diagnostics"
aria-label={diagnosticsOpen ? 'Закрыть диагностику' : 'Проверить маршруты Gateway'}
aria-label={diagnosticsOpen ? 'Закрыть диагностику' : 'Проверить маршруты'}
onClick={() => {
if (localRulesOpen && !requestCloseLocalRules()) return;
setInstructionsOpen(false);
@@ -1186,7 +1186,7 @@ export function ClientOverviewPage({
<path d="M3 12h4l2.2-5 4.2 10 2.1-5H21" />
</svg>
<span>Диагностика</span>
</button>}
</button>
<button
ref={localRulesToggleRef}
className={`client-local-rules-toggle${localRulesOpen ? ' is-open' : ''}${localRulesPendingRestart ? ' has-pending' : ''}`}
@@ -1536,7 +1536,8 @@ export function ClientOverviewPage({
onClose={() => setDevicesOpen(false)}
/>}
{hasSubscription && subscriptionContentReady && isGateway && <ConnectivityDiagnosticsPanel
{hasSubscription && subscriptionContentReady && <ConnectivityDiagnosticsPanel
isGateway={isGateway}
open={diagnosticsOpen}
panelRef={diagnosticsPanelRef}
closeRef={diagnosticsCloseRef}
@@ -63,7 +63,7 @@ function IpCell({ path, source, pending, route }) {
return <code aria-label={`${route}: ${value.address}`}>{value.address}</code>;
}
export function ConnectivityDiagnosticsPanel({ open, panelRef, closeRef, onClose }) {
export function ConnectivityDiagnosticsPanel({ isGateway, open, panelRef, closeRef, onClose }) {
const [result, setResult] = useState(null);
const [status, setStatus] = useState('idle');
const [error, setError] = useState(null);
@@ -138,7 +138,7 @@ export function ConnectivityDiagnosticsPanel({ open, panelRef, closeRef, onClose
onClick={onClose}
>×</button>
<header className="client-instructions-header client-diagnostics-header">
<span>Gateway · Direct VPN</span>
<span>{isGateway ? 'Gateway' : 'Connect'} · Direct VPN</span>
<div className="client-diagnostics-title-row">
<h2 id="client-diagnostics-title">Маршруты</h2>
<span className="client-diagnostics-refresh-wrap client-tooltip-anchor">
+2 -2
View File
@@ -589,7 +589,7 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
onClick={() => copyDeviceIp(device)}
>{device.ip}</button> : !hasName && <span>Неизвестное устройство</span>}
</h3>
<span className="client-device-edit-wrap client-tooltip-anchor">
{!hasName && <span className="client-device-edit-wrap client-tooltip-anchor">
<button
className={`client-device-edit${pencilAnimationId === device.id ? ' is-writing' : ''}`}
type="button"
@@ -607,7 +607,7 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
</svg>
</button>
<Tooltip>Изменить название</Tooltip>
</span>
</span>}
</>
)}
<span className={`client-device-last-seen${online ? ' is-online' : ''}`} tabIndex="0">
+9 -2
View File
@@ -946,7 +946,11 @@ p {
display: flex;
align-items: flex-end;
gap: 2px;
padding: 11px 27px 0 0;
padding: 11px 0 0;
}
.client-device-main:has(.client-device-edit-wrap) {
padding-right: 27px;
}
.client-device-main > h3 {
@@ -1396,7 +1400,10 @@ p {
.client-device-traffic-plot {
grid-column: 1 / -1;
grid-row: 1;
height: 100%;
min-height: 0;
min-width: 0;
overflow: hidden;
cursor: crosshair;
}
@@ -1408,7 +1415,7 @@ p {
width: 100%;
height: 100%;
display: block;
overflow: visible;
overflow: hidden;
}
.client-device-traffic-grid line {