mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-21 03:56:07 +00:00
f02018cfb7
freedomToWire called Object.entries(s.fragment), but getFieldsValue(true) returns freedom settings without a fragment object when the Fragment switch is off (its sub-fields never register). That threw 'Cannot convert undefined or null to object' and silently killed the save. Guard fragment with a fallback so an unset value is treated as empty. While verifying against xray-core's freedom config, also: - add the missing userLevel field (schema, form schema, adapter, UI) - fix noise applyTo enum to ip/ipv4/ipv6 (xray rejects the old host/all) Closes #4686
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
export const OutboundDomainStrategySchema = z.enum([
|
|
'AsIs',
|
|
'UseIP',
|
|
'UseIPv4',
|
|
'UseIPv6',
|
|
'UseIPv6v4',
|
|
'UseIPv4v6',
|
|
'ForceIP',
|
|
'ForceIPv6v4',
|
|
'ForceIPv6',
|
|
'ForceIPv4v6',
|
|
'ForceIPv4',
|
|
]);
|
|
export type OutboundDomainStrategy = z.infer<typeof OutboundDomainStrategySchema>;
|
|
|
|
// Fragment knobs are TCP-level splitting controls; all four fields are
|
|
// dash-range strings (e.g. '1-3', '10-20').
|
|
export const FreedomFragmentSchema = z.object({
|
|
packets: z.string().default('1-3'),
|
|
length: z.string().default(''),
|
|
interval: z.string().default(''),
|
|
maxSplit: z.string().default(''),
|
|
});
|
|
export type FreedomFragment = z.infer<typeof FreedomFragmentSchema>;
|
|
|
|
export const FreedomNoiseTypeSchema = z.enum(['rand', 'str', 'base64', 'hex']);
|
|
export const FreedomNoiseApplyToSchema = z.enum(['ip', 'ipv4', 'ipv6']);
|
|
|
|
export const FreedomNoiseSchema = z.object({
|
|
type: FreedomNoiseTypeSchema.default('rand'),
|
|
packet: z.string().default('10-20'),
|
|
delay: z.string().default('10-16'),
|
|
applyTo: FreedomNoiseApplyToSchema.default('ip'),
|
|
});
|
|
export type FreedomNoise = z.infer<typeof FreedomNoiseSchema>;
|
|
|
|
export const FreedomFinalRuleActionSchema = z.enum(['allow', 'block']);
|
|
|
|
// Final rules express the legacy ipsBlocked behavior plus generalized
|
|
// allow/block per network+port+ip combinations.
|
|
export const FreedomFinalRuleSchema = z.object({
|
|
action: FreedomFinalRuleActionSchema.default('block'),
|
|
network: z.string().optional(),
|
|
port: z.string().optional(),
|
|
ip: z.array(z.string()).default([]),
|
|
blockDelay: z.string().optional(),
|
|
});
|
|
export type FreedomFinalRule = z.infer<typeof FreedomFinalRuleSchema>;
|
|
|
|
export const FreedomOutboundSettingsSchema = z.object({
|
|
domainStrategy: OutboundDomainStrategySchema.optional(),
|
|
redirect: z.string().optional(),
|
|
userLevel: z.number().int().min(0).optional(),
|
|
proxyProtocol: z.number().optional(),
|
|
fragment: FreedomFragmentSchema.optional(),
|
|
noises: z.array(FreedomNoiseSchema).optional(),
|
|
finalRules: z.array(FreedomFinalRuleSchema).optional(),
|
|
});
|
|
export type FreedomOutboundSettings = z.infer<typeof FreedomOutboundSettingsSchema>;
|