mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-08 14:36:13 +00:00
* ws/inbounds: realtime fixes + perf for 10k+ client inbounds - hub: dedup, throttle, panic-restart, deadlock fix, race tests - client: backoff cap + slow-retry instead of giving up - broadcast: delta-only payload, count-based invalidate fallback - filter: fix empty online list (Inbound has no .id, use dbInbound.toInbound) - perf: O(N²)→O(N) traffic merge, bulk delete, /setEnable endpoint - traffic: monotonic all_time + UI clamp + propagate in delta handler - session: persist on update/logout (fixes logout-after-password-change) - ui: protocol tags flex, traffic bar normalize * Remove hub_test.go file * fix: ws hub, inbound service, and frontend correctness - propagate DelInbound error on disable path in SetInboundEnable - skip empty emails in updateClientTraffics to avoid constraint violations - use consistent IN ? clause, drop redundant ErrRecordNotFound guards - Hub.Unregister: direct removeClient fallback when channel is full - applyClientStatsDelta: O(1) email lookup via per-inbound Map cache - WS payload size check: Blob.size instead of .length for real byte count * fix: chunk large IN ? queries and fix IPv6 same-origin check * fix: chunk large IN ? queries and fix IPv6 same-origin check * fix: unify clientStats cache, throttle clarity, hub constants * fix(ui): align traffic/expiry cell columns across all rows * style(ui): redesign outbounds table for visual consistency * style(ui): redesign routing table for visual consistency * fix: * fix: * fix: * fix: * fix: * fix: font * refactor: simplify outbound tone functions for consistency and maintainability --------- Co-authored-by: lolka1333 <test123@gmail.com>
194 lines
10 KiB
HTML
194 lines
10 KiB
HTML
{{define "settings/xray/routing"}}
|
|
<a-space direction="vertical" size="middle" class="routing-modern">
|
|
<a-button type="primary" icon="plus" @click="addRule">{{ i18n
|
|
"pages.xray.rules.add" }}</a-button>
|
|
<a-table-sortable :columns="isMobile ? rulesMobileColumns : rulesColumns"
|
|
:row-key="r => r.key"
|
|
:data-source="routingRuleData"
|
|
:scroll="{}"
|
|
:pagination="false"
|
|
:indent-size="0"
|
|
class="routing-table"
|
|
v-on:onSort="replaceRule">
|
|
<template slot="action" slot-scope="text, rule, index">
|
|
<div class="routing-action-cell">
|
|
<a-table-sort-trigger :item-index="index"></a-table-sort-trigger>
|
|
<span class="routing-index">[[ index+1 ]]</span>
|
|
<a-dropdown :trigger="['click']">
|
|
<a-button shape="circle" size="small" class="routing-action-btn"
|
|
@click="e => e.preventDefault()">
|
|
<a-icon type="more"></a-icon>
|
|
</a-button>
|
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
|
<a-menu-item @click="editRule(index)">
|
|
<a-icon type="edit"></a-icon>
|
|
<span>{{ i18n "edit" }}</span>
|
|
</a-menu-item>
|
|
<a-menu-item @click="deleteRule(index)">
|
|
<span :style="{ color: '#FF4D4F' }">
|
|
<a-icon type="delete"></a-icon>
|
|
<span>{{ i18n "delete" }}</span>
|
|
</span>
|
|
</a-menu-item>
|
|
</a-menu>
|
|
</a-dropdown>
|
|
</div>
|
|
</template>
|
|
|
|
<template slot="source" slot-scope="text, rule">
|
|
<div class="criterion-flow">
|
|
<a-tooltip v-if="rule.sourceIP"
|
|
:title="'Source IP: ' + joinCsv(rule.sourceIP)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">IP</span>
|
|
<span class="criterion-value">[[ csv(rule.sourceIP)[0] ]]</span>
|
|
<span v-if="csv(rule.sourceIP).length > 1" class="criterion-more">+[[ csv(rule.sourceIP).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.sourcePort"
|
|
:title="'Source Port: ' + joinCsv(rule.sourcePort)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Port</span>
|
|
<span class="criterion-value">[[ csv(rule.sourcePort)[0] ]]</span>
|
|
<span v-if="csv(rule.sourcePort).length > 1" class="criterion-more">+[[ csv(rule.sourcePort).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.vlessRoute"
|
|
:title="'VLESS Route: ' + joinCsv(rule.vlessRoute)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">VLESS</span>
|
|
<span class="criterion-value">[[ csv(rule.vlessRoute)[0] ]]</span>
|
|
<span v-if="csv(rule.vlessRoute).length > 1" class="criterion-more">+[[ csv(rule.vlessRoute).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<span class="routing-criteria-empty"
|
|
v-if="!rule.sourceIP && !rule.sourcePort && !rule.vlessRoute">—</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template slot="network" slot-scope="text, rule">
|
|
<div class="criterion-flow">
|
|
<a-tooltip v-if="rule.network"
|
|
:title="'L4: ' + joinCsv(rule.network)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">L4</span>
|
|
<span class="criterion-value">[[ csv(rule.network)[0] ]]</span>
|
|
<span v-if="csv(rule.network).length > 1" class="criterion-more">+[[ csv(rule.network).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.protocol"
|
|
:title="'Protocol: ' + joinCsv(rule.protocol)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Protocol</span>
|
|
<span class="criterion-value">[[ csv(rule.protocol)[0] ]]</span>
|
|
<span v-if="csv(rule.protocol).length > 1" class="criterion-more">+[[ csv(rule.protocol).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.attrs"
|
|
:title="'Attrs: ' + joinCsv(rule.attrs)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Attrs</span>
|
|
<span class="criterion-value">[[ csv(rule.attrs)[0] ]]</span>
|
|
<span v-if="csv(rule.attrs).length > 1" class="criterion-more">+[[ csv(rule.attrs).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<span class="routing-criteria-empty"
|
|
v-if="!rule.network && !rule.protocol && !rule.attrs">—</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template slot="destination" slot-scope="text, rule">
|
|
<div class="criterion-flow">
|
|
<a-tooltip v-if="rule.ip"
|
|
:title="'Destination IP: ' + joinCsv(rule.ip)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">IP</span>
|
|
<span class="criterion-value">[[ csv(rule.ip)[0] ]]</span>
|
|
<span v-if="csv(rule.ip).length > 1" class="criterion-more">+[[ csv(rule.ip).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.domain"
|
|
:title="'Domain: ' + joinCsv(rule.domain)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Domain</span>
|
|
<span class="criterion-value">[[ csv(rule.domain)[0] ]]</span>
|
|
<span v-if="csv(rule.domain).length > 1" class="criterion-more">+[[ csv(rule.domain).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.port"
|
|
:title="'Destination Port: ' + joinCsv(rule.port)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Port</span>
|
|
<span class="criterion-value">[[ csv(rule.port)[0] ]]</span>
|
|
<span v-if="csv(rule.port).length > 1" class="criterion-more">+[[ csv(rule.port).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<span class="routing-criteria-empty"
|
|
v-if="!rule.ip && !rule.domain && !rule.port">—</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template slot="inbound" slot-scope="text, rule">
|
|
<div class="criterion-flow">
|
|
<a-tooltip v-if="rule.inboundTag"
|
|
:title="'Inbound Tag: ' + joinCsv(rule.inboundTag)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">Tag</span>
|
|
<span class="criterion-value">[[ csv(rule.inboundTag)[0] ]]</span>
|
|
<span v-if="csv(rule.inboundTag).length > 1" class="criterion-more">+[[ csv(rule.inboundTag).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<a-tooltip v-if="rule.user"
|
|
:title="'Client: ' + joinCsv(rule.user)"
|
|
:overlay-class-name="themeSwitcher.currentTheme"
|
|
:trigger="['hover', 'click']">
|
|
<span class="criterion-row">
|
|
<span class="criterion-label">User</span>
|
|
<span class="criterion-value">[[ csv(rule.user)[0] ]]</span>
|
|
<span v-if="csv(rule.user).length > 1" class="criterion-more">+[[ csv(rule.user).length - 1 ]]</span>
|
|
</span>
|
|
</a-tooltip>
|
|
<span class="routing-criteria-empty"
|
|
v-if="!rule.inboundTag && !rule.user">—</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template slot="target" slot-scope="text, rule">
|
|
<div class="routing-target-cell">
|
|
<div class="routing-target-row" v-if="rule.outboundTag">
|
|
<a-icon type="export" class="routing-target-icon"></a-icon>
|
|
<span class="outbound-pill tone-emerald">[[ rule.outboundTag ]]</span>
|
|
</div>
|
|
<div class="routing-target-row" v-if="rule.balancerTag">
|
|
<a-icon type="cluster" class="routing-target-icon"></a-icon>
|
|
<span class="outbound-pill tone-violet">[[ rule.balancerTag ]]</span>
|
|
</div>
|
|
<span class="routing-criteria-empty"
|
|
v-if="!rule.outboundTag && !rule.balancerTag">—</span>
|
|
</div>
|
|
</template>
|
|
|
|
</a-table-sortable>
|
|
</a-space>
|
|
{{end}}
|