mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-10 23:25:02 +03:00
fix(routing): insert new rules after api instead of appending at the end
A new routing rule created through the Routing tab was always appended to the end of the list. Xray matches rules top-to-bottom, first hit wins, so any pre-existing broader rule (e.g. a block rule with no inboundTag restriction) silently shadows a newly created, more specific rule forever -- it looks saved and enabled but never actually fires. Root-caused this from a report that an AmneziaWG inbound routed through a real outbound lost all connectivity while routing it "direct" worked fine: the AmneziaWG bridge's own TPROXY/interface code turned out to be entirely fine (tunnel stayed up, client stayed online) -- the new rule was just sitting below existing RU-IP/bittorrent/domain block rules with no inbound restriction, so those matched first. Confirmed directly: manually dragging the rule to sit right after the pinned api rule fixed it. This isn't AmneziaWG-specific -- any protocol's newly added rule can be shadowed the same way. New rules now insert right after the pinned api rule (or at the very top if it's absent) instead of at the end, so a newly created rule takes effect by default; drag it lower afterward if a lower priority is actually wanted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ import RuleFormModal from './RuleFormModal';
|
||||
import type { RoutingRule } from './RuleFormModal';
|
||||
import RuleCardList from './RuleCardList';
|
||||
import { useRoutingColumns } from './useRoutingColumns';
|
||||
import { arrJoin, originalRuleIndex } from './helpers';
|
||||
import { arrJoin, originalRuleIndex, isApiRule } from './helpers';
|
||||
import type { RuleRow } from './types';
|
||||
import type { XraySettingsValue, SetTemplate } from '@/hooks/useXraySetting';
|
||||
import type { RuleObject } from '@/schemas/routing';
|
||||
@@ -209,8 +209,20 @@ export default function RoutingTab({
|
||||
if (!tt.routing) tt.routing = { rules: [] };
|
||||
if (!Array.isArray(tt.routing.rules)) tt.routing.rules = [];
|
||||
const typed = rule as unknown as RuleObject;
|
||||
if (editingIndex == null) tt.routing.rules.push(typed);
|
||||
else tt.routing.rules[editingIndex] = typed;
|
||||
if (editingIndex == null) {
|
||||
// Rules match top-to-bottom, first hit wins, so a brand-new rule
|
||||
// appended at the end is silently shadowed by any earlier
|
||||
// broader/catch-all rule that also happens to match its traffic —
|
||||
// a real trap: the rule looks saved and enabled, but never actually
|
||||
// fires. Insert it as early as possible instead (right after the
|
||||
// pinned api rule, if present) so a newly created rule takes effect
|
||||
// by default; the admin can still drag it lower with moveDown if
|
||||
// that's genuinely what they want.
|
||||
const insertAt = isApiRule(tt.routing.rules[0] as RuleObject) ? 1 : 0;
|
||||
tt.routing.rules.splice(insertAt, 0, typed);
|
||||
} else {
|
||||
tt.routing.rules[editingIndex] = typed;
|
||||
}
|
||||
});
|
||||
setRuleModalOpen(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user