mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-15 17:16:07 +00:00
fix(frontend): preserve routing-rule fields the form does not surface
The rule form rebuilt the rule from a fixed literal of only the fields it edits, and RoutingTab replaces the rule wholesale on confirm. Fields the form never exposes — localPort, localIP, process, ruleTag, webhook — are in the rule schema and can arrive via the advanced JSON editor or Import Rules; opening such a rule in the form and saving silently dropped them. Carry over every key of the original rule the form does not manage before applying the form-derived fields, so an edit only touches what it surfaces.
This commit is contained in:
@@ -126,7 +126,13 @@ export default function RuleFormModal({
|
||||
outboundTag: v.outboundTag === '' ? undefined : v.outboundTag,
|
||||
balancerTag: v.balancerTag === '' ? undefined : v.balancerTag,
|
||||
};
|
||||
const managedKeys = new Set(Object.keys(built));
|
||||
const out: Record<string, unknown> = {};
|
||||
if (rule) {
|
||||
for (const [key, value] of Object.entries(rule)) {
|
||||
if (!managedKeys.has(key) && value !== undefined) out[key] = value;
|
||||
}
|
||||
}
|
||||
for (const [k, v] of Object.entries(built)) {
|
||||
if (v == null) continue;
|
||||
if (Array.isArray(v) && v.length === 0) continue;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
import RuleFormModal from '@/pages/xray/routing/RuleFormModal';
|
||||
|
||||
import { renderWithProviders } from './test-utils';
|
||||
|
||||
describe('RuleFormModal edit preserves unsurfaced fields', () => {
|
||||
it('keeps a field the form does not surface (ruleTag) when saving an edit', () => {
|
||||
const onConfirm = vi.fn();
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
renderWithProviders(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RuleFormModal
|
||||
open
|
||||
rule={{ type: 'field', outboundTag: 'block', ruleTag: 'my-tag', enabled: true }}
|
||||
inboundTags={[]}
|
||||
outboundTags={['block']}
|
||||
balancerTags={[]}
|
||||
onClose={vi.fn()}
|
||||
onConfirm={onConfirm}
|
||||
/>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));
|
||||
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
expect(onConfirm.mock.calls[0][0]).toMatchObject({ ruleTag: 'my-tag' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user