mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-10 05:10:58 +00:00
feat(sub): per-inbound sort order for subscription links
Add a subSortIndex field to inbounds that controls the order of links in subscription output only: the raw sub body, the HTML sub page, and the JSON/Clash formats (all served from the same query). Lower values come first; ties keep id order. The panel inbound list is unaffected. The value is editable in the inbound form next to the share-address fields, propagates to nodes via wireInbound, and follows the usual node-sync rules (copied on import, mirrored while not dirty, never a structural change). Rescoped from #5214 by @Ponywka.
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
formValuesToWirePayload,
|
||||
type RawInboundRow,
|
||||
} from '@/lib/xray/inbound-form-adapter';
|
||||
import { InboundFormSchema } from '@/schemas/forms/inbound-form';
|
||||
import { InboundDbFieldsSchema, InboundFormSchema } from '@/schemas/forms/inbound-form';
|
||||
import { SockoptStreamSettingsSchema } from '@/schemas/protocols/stream/sockopt';
|
||||
|
||||
// Round-trip: raw DB row → InboundFormValues → wire payload, asserting
|
||||
@@ -262,3 +262,35 @@ describe('formValuesToWirePayload', () => {
|
||||
expect(replay.streamSettings).toEqual(original.streamSettings);
|
||||
});
|
||||
});
|
||||
|
||||
describe('subSortIndex', () => {
|
||||
it('rawInboundToFormValues defaults to 1 when field is absent', () => {
|
||||
const values = rawInboundToFormValues({ ...vlessRow, subSortIndex: undefined });
|
||||
expect(values.subSortIndex).toBe(1);
|
||||
});
|
||||
|
||||
it('rawInboundToFormValues preserves valid values and clamps below-minimum ones to 1', () => {
|
||||
expect(rawInboundToFormValues({ ...vlessRow, subSortIndex: 5 }).subSortIndex).toBe(5);
|
||||
expect(rawInboundToFormValues({ ...vlessRow, subSortIndex: 0 }).subSortIndex).toBe(1);
|
||||
expect(rawInboundToFormValues({ ...vlessRow, subSortIndex: -10 }).subSortIndex).toBe(1);
|
||||
});
|
||||
|
||||
it('formValuesToWirePayload includes subSortIndex in the payload', () => {
|
||||
const values = rawInboundToFormValues({ ...vlessRow, subSortIndex: 3 });
|
||||
const payload = formValuesToWirePayload(values);
|
||||
expect(payload.subSortIndex).toBe(3);
|
||||
});
|
||||
|
||||
it('subSortIndex round-trips through raw → values → payload', () => {
|
||||
const values = rawInboundToFormValues({ ...vlessRow, subSortIndex: 42 });
|
||||
const payload = formValuesToWirePayload(values);
|
||||
const replay = rawInboundToFormValues({ ...vlessRow, subSortIndex: payload.subSortIndex });
|
||||
expect(replay.subSortIndex).toBe(42);
|
||||
});
|
||||
|
||||
it('InboundDbFieldsSchema enforces an integer minimum of 1 and defaults to 1', () => {
|
||||
expect(InboundDbFieldsSchema.partial().safeParse({ subSortIndex: 1.5 }).success).toBe(false);
|
||||
expect(InboundDbFieldsSchema.partial().safeParse({ subSortIndex: 0 }).success).toBe(false);
|
||||
expect(InboundDbFieldsSchema.parse({}).subSortIndex).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user