mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-03 19:09:36 +00:00
feat(xray): merge basic routing into the routing rules section
Move the basic routing presets (block torrent/IPs/domains, direct IPs/domains, IPv4) out of the Basics page into a Basic tab in the Routing section, next to the advanced Rules table; both edit the same routing.rules so existing rules stay in sync. Drop the WARP and Nord routing preset rows - WARP/Nord outbounds are still added from the Outbounds page and any existing rules remain editable in the Rules tab. Hide the Source and Balancers columns in the rules table when no rule populates them.
This commit is contained in:
@@ -94,9 +94,6 @@ export default function XrayPage() {
|
||||
[setTemplateSettings],
|
||||
);
|
||||
|
||||
const warpExist = !!templateSettings?.outbounds?.find((o) => o?.tag === 'warp');
|
||||
const nordExist = !!templateSettings?.outbounds?.find((o) => o?.tag?.startsWith?.('nord-'));
|
||||
|
||||
async function onTestOutbound(idx: number, mode: string) {
|
||||
const outbound = templateSettings?.outbounds?.[idx];
|
||||
if (outbound) await testOutbound(idx, outbound, mode);
|
||||
@@ -287,10 +284,6 @@ export default function XrayPage() {
|
||||
setTemplateSettings={setTemplateSettings}
|
||||
outboundTestUrl={outboundTestUrl}
|
||||
onChangeOutboundTestUrl={setOutboundTestUrl}
|
||||
warpExist={warpExist}
|
||||
nordExist={nordExist}
|
||||
onShowWarp={() => setWarpOpen(true)}
|
||||
onShowNord={() => setNordOpen(true)}
|
||||
onResetDefault={resetToDefault}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,13 +2,10 @@ import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Alert, Button, Input, Modal, Select, Space, Switch, Tabs } from 'antd';
|
||||
import {
|
||||
ApiOutlined,
|
||||
BarChartOutlined,
|
||||
CloudOutlined,
|
||||
FileTextOutlined,
|
||||
ReloadOutlined,
|
||||
SettingOutlined,
|
||||
SwapOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { OutboundDomainStrategies } from '@/schemas/primitives';
|
||||
@@ -20,29 +17,17 @@ import './BasicsTab.css';
|
||||
|
||||
import {
|
||||
ACCESS_LOG,
|
||||
BITTORRENT_PROTOCOLS,
|
||||
BLOCK_DOMAINS_OPTIONS,
|
||||
DOMAINS_OPTIONS,
|
||||
ERROR_LOG,
|
||||
IPS_OPTIONS,
|
||||
LOG_LEVELS,
|
||||
MASK_ADDRESS,
|
||||
ROUTING_DOMAIN_STRATEGIES,
|
||||
SERVICES_OPTIONS,
|
||||
directSettings,
|
||||
ipv4Settings,
|
||||
} from './constants';
|
||||
import { ruleGetter, ruleSetter, syncOutbound } from './helpers';
|
||||
|
||||
interface BasicsTabProps {
|
||||
templateSettings: XraySettingsValue | null;
|
||||
setTemplateSettings: SetTemplate;
|
||||
outboundTestUrl: string;
|
||||
onChangeOutboundTestUrl: (v: string) => void;
|
||||
warpExist: boolean;
|
||||
nordExist: boolean;
|
||||
onShowWarp: () => void;
|
||||
onShowNord: () => void;
|
||||
onResetDefault: () => void;
|
||||
}
|
||||
|
||||
@@ -51,10 +36,6 @@ export default function BasicsTab({
|
||||
setTemplateSettings,
|
||||
outboundTestUrl,
|
||||
onChangeOutboundTestUrl,
|
||||
warpExist,
|
||||
nordExist,
|
||||
onShowWarp,
|
||||
onShowNord,
|
||||
onResetDefault,
|
||||
}: BasicsTabProps) {
|
||||
const { t } = useTranslation();
|
||||
@@ -92,19 +73,6 @@ export default function BasicsTab({
|
||||
const log = (templateSettings?.log || {}) as Record<string, unknown>;
|
||||
const policy = (templateSettings?.policy?.system || {}) as Record<string, boolean>;
|
||||
|
||||
const blockedIPs = ruleGetter(templateSettings, 'blocked', 'ip');
|
||||
const blockedDomains = ruleGetter(templateSettings, 'blocked', 'domain');
|
||||
const blockedProtocols = ruleGetter(templateSettings, 'blocked', 'protocol');
|
||||
const directIPs = ruleGetter(templateSettings, 'direct', 'ip');
|
||||
const directDomains = ruleGetter(templateSettings, 'direct', 'domain');
|
||||
const ipv4Domains = ruleGetter(templateSettings, 'IPv4', 'domain');
|
||||
const warpDomains = ruleGetter(templateSettings, 'warp', 'domain');
|
||||
const nordTag =
|
||||
templateSettings?.outbounds?.find((o) => o?.tag?.startsWith?.('nord-'))?.tag || 'nord';
|
||||
const nordDomains = ruleGetter(templateSettings, nordTag, 'domain');
|
||||
|
||||
const torrentActive = BITTORRENT_PROTOCOLS.every((p) => blockedProtocols.includes(p));
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: '1',
|
||||
@@ -277,165 +245,6 @@ export default function BasicsTab({
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
label: catTabLabel(<SwapOutlined />, t('pages.xray.basicRouting'), isMobile),
|
||||
children: (
|
||||
<>
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
className="mb-12 hint-alert"
|
||||
title={t('pages.xray.blockConnectionsConfigsDesc')}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.Torrent')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Switch
|
||||
checked={torrentActive}
|
||||
onChange={(checked) => mutate((tt) => {
|
||||
const next = checked
|
||||
? [...blockedProtocols, ...BITTORRENT_PROTOCOLS]
|
||||
: blockedProtocols.filter((d) => !BITTORRENT_PROTOCOLS.includes(d));
|
||||
ruleSetter(tt, 'blocked', 'protocol', next);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.blockips')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={blockedIPs}
|
||||
style={{ width: '100%' }}
|
||||
options={IPS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, 'blocked', 'ip', v))}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.blockdomains')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={blockedDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={BLOCK_DOMAINS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, 'blocked', 'domain', v))}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
className="mb-12 hint-alert"
|
||||
title={t('pages.xray.directConnectionsConfigsDesc')}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.directips')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={directIPs}
|
||||
style={{ width: '100%' }}
|
||||
options={IPS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'direct', 'ip', v);
|
||||
syncOutbound(tt, 'direct', directSettings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.directdomains')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={directDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={DOMAINS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'direct', 'domain', v);
|
||||
syncOutbound(tt, 'direct', directSettings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.ipv4Routing')}
|
||||
description={t('pages.xray.ipv4RoutingDesc')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={ipv4Domains}
|
||||
style={{ width: '100%' }}
|
||||
options={SERVICES_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'IPv4', 'domain', v);
|
||||
syncOutbound(tt, 'IPv4', ipv4Settings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.warpRouting')}
|
||||
description={t('pages.xray.warpRoutingDesc')}
|
||||
paddings="small"
|
||||
control={
|
||||
warpExist ? (
|
||||
<Select
|
||||
mode="tags"
|
||||
value={warpDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={SERVICES_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, 'warp', 'domain', v))}
|
||||
/>
|
||||
) : (
|
||||
<Button type="primary" onClick={onShowWarp} icon={<CloudOutlined />}>
|
||||
WARP
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.nordRouting')}
|
||||
description={t('pages.xray.nordRoutingDesc')}
|
||||
paddings="small"
|
||||
control={
|
||||
nordExist ? (
|
||||
<Select
|
||||
mode="tags"
|
||||
value={nordDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={SERVICES_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, nordTag, 'domain', v))}
|
||||
/>
|
||||
) : (
|
||||
<Button type="primary" onClick={onShowNord} icon={<ApiOutlined />}>
|
||||
NordVPN
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'reset',
|
||||
label: catTabLabel(<ReloadOutlined />, t('pages.settings.resetDefaultConfig'), isMobile),
|
||||
|
||||
160
frontend/src/pages/xray/routing/RoutingBasic.tsx
Normal file
160
frontend/src/pages/xray/routing/RoutingBasic.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Alert, Select, Switch } from 'antd';
|
||||
|
||||
import { SettingListItem } from '@/components/ui';
|
||||
import type { XraySettingsValue, SetTemplate } from '@/hooks/useXraySetting';
|
||||
import {
|
||||
BITTORRENT_PROTOCOLS,
|
||||
BLOCK_DOMAINS_OPTIONS,
|
||||
DOMAINS_OPTIONS,
|
||||
IPS_OPTIONS,
|
||||
SERVICES_OPTIONS,
|
||||
directSettings,
|
||||
ipv4Settings,
|
||||
} from '../basics/constants';
|
||||
import { ruleGetter, ruleSetter, syncOutbound } from '../basics/helpers';
|
||||
|
||||
interface RoutingBasicProps {
|
||||
templateSettings: XraySettingsValue | null;
|
||||
setTemplateSettings: SetTemplate;
|
||||
}
|
||||
|
||||
export default function RoutingBasic({ templateSettings, setTemplateSettings }: RoutingBasicProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const mutate = useCallback(
|
||||
(mutator: (next: XraySettingsValue) => void) => {
|
||||
setTemplateSettings((prev) => {
|
||||
if (!prev) return prev;
|
||||
const clone = JSON.parse(JSON.stringify(prev)) as XraySettingsValue;
|
||||
mutator(clone);
|
||||
return clone;
|
||||
});
|
||||
},
|
||||
[setTemplateSettings],
|
||||
);
|
||||
|
||||
const blockedIPs = ruleGetter(templateSettings, 'blocked', 'ip');
|
||||
const blockedDomains = ruleGetter(templateSettings, 'blocked', 'domain');
|
||||
const blockedProtocols = ruleGetter(templateSettings, 'blocked', 'protocol');
|
||||
const directIPs = ruleGetter(templateSettings, 'direct', 'ip');
|
||||
const directDomains = ruleGetter(templateSettings, 'direct', 'domain');
|
||||
const ipv4Domains = ruleGetter(templateSettings, 'IPv4', 'domain');
|
||||
|
||||
const torrentActive = BITTORRENT_PROTOCOLS.every((p) => blockedProtocols.includes(p));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
className="mb-12 hint-alert"
|
||||
title={t('pages.xray.blockConnectionsConfigsDesc')}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.Torrent')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Switch
|
||||
checked={torrentActive}
|
||||
onChange={(checked) => mutate((tt) => {
|
||||
const next = checked
|
||||
? [...blockedProtocols, ...BITTORRENT_PROTOCOLS]
|
||||
: blockedProtocols.filter((d) => !BITTORRENT_PROTOCOLS.includes(d));
|
||||
ruleSetter(tt, 'blocked', 'protocol', next);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.blockips')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={blockedIPs}
|
||||
style={{ width: '100%' }}
|
||||
options={IPS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, 'blocked', 'ip', v))}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.blockdomains')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={blockedDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={BLOCK_DOMAINS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => ruleSetter(tt, 'blocked', 'domain', v))}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
className="mb-12 hint-alert"
|
||||
title={t('pages.xray.directConnectionsConfigsDesc')}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.directips')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={directIPs}
|
||||
style={{ width: '100%' }}
|
||||
options={IPS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'direct', 'ip', v);
|
||||
syncOutbound(tt, 'direct', directSettings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.directdomains')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={directDomains}
|
||||
style={{ width: '100%' }}
|
||||
options={DOMAINS_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'direct', 'domain', v);
|
||||
syncOutbound(tt, 'direct', directSettings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingListItem
|
||||
title={t('pages.xray.ipv4Routing')}
|
||||
description={t('pages.xray.ipv4RoutingDesc')}
|
||||
paddings="small"
|
||||
control={
|
||||
<Select
|
||||
mode="tags"
|
||||
value={ipv4Domains}
|
||||
style={{ width: '100%' }}
|
||||
options={SERVICES_OPTIONS}
|
||||
onChange={(v) => mutate((tt) => {
|
||||
ruleSetter(tt, 'IPv4', 'domain', v);
|
||||
syncOutbound(tt, 'IPv4', ipv4Settings);
|
||||
})}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -231,3 +231,7 @@
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.hint-alert {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Modal, Space, Table } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Modal, Space, Table, Tabs } from 'antd';
|
||||
import { ControlOutlined, PlusOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||
|
||||
import { catTabLabel } from '@/pages/settings/catTabLabel';
|
||||
import RoutingBasic from './RoutingBasic';
|
||||
import RuleFormModal from './RuleFormModal';
|
||||
import type { RoutingRule } from './RuleFormModal';
|
||||
import RuleCardList from './RuleCardList';
|
||||
@@ -226,9 +228,14 @@ export default function RoutingTab({
|
||||
document.addEventListener('pointercancel', onUp);
|
||||
}
|
||||
|
||||
const hasSource = rows.some((r) => r.sourceIP || r.sourcePort || r.vlessRoute);
|
||||
const hasBalancer = rows.some((r) => r.balancerTag);
|
||||
|
||||
const desktopColumns = useRoutingColumns({
|
||||
isMobile,
|
||||
rowsLength: rows.length,
|
||||
showSource: hasSource,
|
||||
showBalancer: hasBalancer,
|
||||
onHandlePointerDown,
|
||||
openEdit,
|
||||
moveUp,
|
||||
@@ -236,56 +243,81 @@ export default function RoutingTab({
|
||||
confirmDelete,
|
||||
});
|
||||
|
||||
const tableScrollX = desktopColumns.reduce((sum, c) => {
|
||||
const col = c as { width?: number; hidden?: boolean };
|
||||
return col.hidden ? sum : sum + (typeof col.width === 'number' ? col.width : 0);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
{modalContextHolder}
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
{t('pages.xray.Routings')}
|
||||
</Button>
|
||||
<Tabs
|
||||
defaultActiveKey="basic"
|
||||
items={[
|
||||
{
|
||||
key: 'basic',
|
||||
label: catTabLabel(<ControlOutlined />, t('pages.xray.basicRouting'), isMobile),
|
||||
children: (
|
||||
<RoutingBasic
|
||||
templateSettings={templateSettings}
|
||||
setTemplateSettings={setTemplateSettings}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'rules',
|
||||
label: catTabLabel(<UnorderedListOutlined />, t('pages.xray.Routings'), isMobile),
|
||||
children: (
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
{t('pages.xray.Routings')}
|
||||
</Button>
|
||||
|
||||
{isMobile ? (
|
||||
<RuleCardList
|
||||
rows={rows}
|
||||
draggedIndex={draggedIndex}
|
||||
dropTargetIndex={dropTargetIndex}
|
||||
onHandlePointerDown={onHandlePointerDown}
|
||||
openEdit={openEdit}
|
||||
moveUp={moveUp}
|
||||
moveDown={moveDown}
|
||||
confirmDelete={confirmDelete}
|
||||
/>
|
||||
) : (
|
||||
<Table
|
||||
columns={desktopColumns}
|
||||
dataSource={rows}
|
||||
rowKey={(r) => r.key}
|
||||
pagination={false}
|
||||
scroll={{ x: 1150 }}
|
||||
size="small"
|
||||
className="routing-table"
|
||||
onRow={(_record, index) => {
|
||||
const classes: string[] = [];
|
||||
const i = index ?? -1;
|
||||
if (draggedIndex === i) classes.push('row-dragging');
|
||||
if (dropTargetIndex === i && draggedIndex !== i && draggedIndex != null) {
|
||||
classes.push(i > draggedIndex ? 'drop-after' : 'drop-before');
|
||||
}
|
||||
return { className: classes.join(' '), 'data-row-key': i } as React.HTMLAttributes<HTMLElement>;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<RuleFormModal
|
||||
open={ruleModalOpen}
|
||||
rule={editingRule}
|
||||
inboundTags={inboundTagOptions}
|
||||
outboundTags={outboundTagOptions}
|
||||
balancerTags={balancerTagOptions}
|
||||
onClose={() => setRuleModalOpen(false)}
|
||||
onConfirm={onRuleConfirm}
|
||||
/>
|
||||
</Space>
|
||||
{isMobile ? (
|
||||
<RuleCardList
|
||||
rows={rows}
|
||||
draggedIndex={draggedIndex}
|
||||
dropTargetIndex={dropTargetIndex}
|
||||
onHandlePointerDown={onHandlePointerDown}
|
||||
openEdit={openEdit}
|
||||
moveUp={moveUp}
|
||||
moveDown={moveDown}
|
||||
confirmDelete={confirmDelete}
|
||||
/>
|
||||
) : (
|
||||
<Table
|
||||
columns={desktopColumns}
|
||||
dataSource={rows}
|
||||
rowKey={(r) => r.key}
|
||||
pagination={false}
|
||||
scroll={{ x: tableScrollX }}
|
||||
size="small"
|
||||
className="routing-table"
|
||||
onRow={(_record, index) => {
|
||||
const classes: string[] = [];
|
||||
const i = index ?? -1;
|
||||
if (draggedIndex === i) classes.push('row-dragging');
|
||||
if (dropTargetIndex === i && draggedIndex !== i && draggedIndex != null) {
|
||||
classes.push(i > draggedIndex ? 'drop-after' : 'drop-before');
|
||||
}
|
||||
return { className: classes.join(' '), 'data-row-key': i } as React.HTMLAttributes<HTMLElement>;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<RuleFormModal
|
||||
open={ruleModalOpen}
|
||||
rule={editingRule}
|
||||
inboundTags={inboundTagOptions}
|
||||
outboundTags={outboundTagOptions}
|
||||
balancerTags={balancerTagOptions}
|
||||
onClose={() => setRuleModalOpen(false)}
|
||||
onConfirm={onRuleConfirm}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import type { RuleRow } from './types';
|
||||
interface RoutingColumnsParams {
|
||||
isMobile: boolean;
|
||||
rowsLength: number;
|
||||
showSource: boolean;
|
||||
showBalancer: boolean;
|
||||
onHandlePointerDown: (idx: number, ev: React.PointerEvent) => void;
|
||||
openEdit: (idx: number) => void;
|
||||
moveUp: (idx: number) => void;
|
||||
@@ -29,6 +31,8 @@ interface RoutingColumnsParams {
|
||||
export function useRoutingColumns({
|
||||
isMobile,
|
||||
rowsLength,
|
||||
showSource,
|
||||
showBalancer,
|
||||
onHandlePointerDown,
|
||||
openEdit,
|
||||
moveUp,
|
||||
@@ -84,6 +88,7 @@ export function useRoutingColumns({
|
||||
align: 'left',
|
||||
width: 180,
|
||||
key: 'source',
|
||||
hidden: !showSource,
|
||||
render: (_v, record) => (
|
||||
<div className="criterion-flow">
|
||||
{record.sourceIP && <CriterionRow label="IP" value={record.sourceIP} title={`Source IP: ${record.sourceIP}`} />}
|
||||
@@ -110,6 +115,7 @@ export function useRoutingColumns({
|
||||
{
|
||||
title: t('pages.xray.rules.dest'),
|
||||
align: 'left',
|
||||
width: 200,
|
||||
key: 'destination',
|
||||
render: (_v, record) => (
|
||||
<div className="criterion-flow">
|
||||
@@ -153,6 +159,7 @@ export function useRoutingColumns({
|
||||
align: 'left',
|
||||
width: 150,
|
||||
key: 'balancer',
|
||||
hidden: !showBalancer,
|
||||
render: (_v, record) =>
|
||||
record.balancerTag ? (
|
||||
<div className="target-row">
|
||||
@@ -165,6 +172,6 @@ export function useRoutingColumns({
|
||||
},
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[t, isMobile, rowsLength],
|
||||
[t, isMobile, rowsLength, showSource, showBalancer],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user