🐛 fix: Remove unnecessary comments and improve variable handling in GoTemplateEditor; ensure zero value displays as empty in EnhancedInput
This commit is contained in:
@@ -40,10 +40,6 @@ export const LABELS = {
|
||||
'mkcp': 'mKCP',
|
||||
'httpupgrade': 'HTTP Upgrade',
|
||||
'xhttp': 'XHTTP',
|
||||
// vless flow
|
||||
'xtls-rprx-direct': 'XTLS-RPRX-Direct',
|
||||
'xtls-rprx-splice': 'XTLS-RPRX-Splice',
|
||||
'xtls-rprx-vision': 'XTLS-RPRX-Vision',
|
||||
// security
|
||||
'none': 'NONE',
|
||||
'tls': 'TLS',
|
||||
@@ -92,6 +88,8 @@ export const SECURITY = {
|
||||
vless: ['none', 'tls', 'reality'] as const,
|
||||
trojan: ['tls'] as const,
|
||||
hysteria2: ['tls'] as const,
|
||||
tuic: ['tls'] as const,
|
||||
anytls: ['tls'] as const,
|
||||
naive: ['none', 'tls'] as const,
|
||||
http: ['none', 'tls'] as const,
|
||||
} as const;
|
||||
@@ -116,7 +114,8 @@ export const FINGERPRINTS = [
|
||||
export const multiplexLevels = ['off', 'low', 'middle', 'high'] as const;
|
||||
|
||||
export function getLabel(value: string): string {
|
||||
return (LABELS as Record<string, string>)[value] ?? value;
|
||||
const label = (LABELS as Record<string, string>)[value];
|
||||
return label ?? value.toUpperCase();
|
||||
}
|
||||
|
||||
const nullableString = z.string().nullish();
|
||||
@@ -183,7 +182,6 @@ const hysteria2 = z.object({
|
||||
hop_ports: nullableString,
|
||||
hop_interval: z.number().nullish(),
|
||||
obfs_password: nullableString,
|
||||
host: nullableString,
|
||||
port: nullablePort,
|
||||
security: z.enum(SECURITY.hysteria2 as any).nullish(),
|
||||
sni: nullableString,
|
||||
@@ -199,11 +197,22 @@ const tuic = z.object({
|
||||
reduce_rtt: z.boolean().nullish(),
|
||||
udp_relay_mode: z.enum(TUIC_UDP_RELAY_MODES as any).nullish(),
|
||||
congestion_controller: z.enum(TUIC_CONGESTION as any).nullish(),
|
||||
security: z.enum(SECURITY.tuic as any).nullish(),
|
||||
sni: nullableString,
|
||||
allow_insecure: nullableBool,
|
||||
fingerprint: nullableString,
|
||||
});
|
||||
|
||||
const anytls = z.object({
|
||||
type: z.literal('anytls'),
|
||||
port: nullablePort,
|
||||
security: z.enum(SECURITY.anytls as any).nullish(),
|
||||
sni: nullableString,
|
||||
allow_insecure: nullableBool,
|
||||
fingerprint: nullableString,
|
||||
padding_scheme: nullableString,
|
||||
});
|
||||
|
||||
const socks = z.object({
|
||||
type: z.literal('socks'),
|
||||
port: nullablePort,
|
||||
@@ -234,15 +243,6 @@ const meru = z.object({
|
||||
transport: z.enum(TRANSPORTS.meru as any).nullish(),
|
||||
});
|
||||
|
||||
const anytls = z.object({
|
||||
type: z.literal('anytls'),
|
||||
port: nullablePort,
|
||||
sni: nullableString,
|
||||
allow_insecure: nullableBool,
|
||||
fingerprint: nullableString,
|
||||
padding_scheme: nullableString,
|
||||
});
|
||||
|
||||
export const protocolApiScheme = z.discriminatedUnion('type', [
|
||||
ss,
|
||||
vmess,
|
||||
@@ -304,6 +304,10 @@ export function getProtocolDefaultConfig(proto: ProtocolType) {
|
||||
reduce_rtt: false,
|
||||
udp_relay_mode: 'native',
|
||||
congestion_controller: 'bbr',
|
||||
security: 'tls',
|
||||
sni: null,
|
||||
allow_insecure: false,
|
||||
fingerprint: 'chrome',
|
||||
} as any;
|
||||
case 'socks':
|
||||
return {
|
||||
@@ -330,7 +334,15 @@ export function getProtocolDefaultConfig(proto: ProtocolType) {
|
||||
transport: 'tcp',
|
||||
} as any;
|
||||
case 'anytls':
|
||||
return { type: 'anytls', port: null, padding_scheme: null } as any;
|
||||
return {
|
||||
type: 'anytls',
|
||||
port: null,
|
||||
security: 'tls',
|
||||
padding_scheme: null,
|
||||
sni: null,
|
||||
allow_insecure: false,
|
||||
fingerprint: 'chrome',
|
||||
} as any;
|
||||
default:
|
||||
return {} as any;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@workspace/ui/components/accordion';
|
||||
import { Badge } from '@workspace/ui/components/badge';
|
||||
import { Button } from '@workspace/ui/components/button';
|
||||
import {
|
||||
Form,
|
||||
@@ -49,10 +50,6 @@ import {
|
||||
ServerFormValues,
|
||||
} from './form-schema';
|
||||
|
||||
function titleCase(s: string) {
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
|
||||
function DynamicField({
|
||||
field,
|
||||
control,
|
||||
@@ -196,6 +193,7 @@ function DynamicField({
|
||||
<FormControl>
|
||||
<textarea
|
||||
{...fieldProps}
|
||||
value={fieldProps.value ?? ''}
|
||||
className='border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'
|
||||
placeholder={
|
||||
field.placeholder
|
||||
@@ -287,7 +285,7 @@ export default function ServerForm(props: {
|
||||
const { trigger, title, loading, initialValues, onSubmit } = props;
|
||||
const t = useTranslations('servers');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [protocolsEnabled, setProtocolsEnabled] = useState<string[]>([]);
|
||||
const [accordionValue, setAccordionValue] = useState<string>();
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
@@ -307,11 +305,6 @@ export default function ServerForm(props: {
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValues) {
|
||||
const enabledProtocols = PROTOCOLS.filter((type) => {
|
||||
const protocol = initialValues.protocols?.find((p) => p.type === type);
|
||||
return protocol && protocol.port && Number(protocol.port) > 0;
|
||||
});
|
||||
setProtocolsEnabled(enabledProtocols);
|
||||
form.reset({
|
||||
name: '',
|
||||
address: '',
|
||||
@@ -329,19 +322,11 @@ export default function ServerForm(props: {
|
||||
}, [initialValues]);
|
||||
|
||||
async function handleSubmit(values: Record<string, any>) {
|
||||
const filtered = (values?.protocols || [])
|
||||
.filter((p: any, index: number) => {
|
||||
const port = Number(p?.port);
|
||||
const protocolType = PROTOCOLS[index];
|
||||
return (
|
||||
protocolType &&
|
||||
protocolsEnabled.includes(protocolType) &&
|
||||
Number.isFinite(port) &&
|
||||
port > 0 &&
|
||||
port <= 65535
|
||||
);
|
||||
})
|
||||
.map((p: any) => ({ ...p, port: Number(p.port) }));
|
||||
const filtered = (values?.protocols || []).filter((p: any, index: number) => {
|
||||
const port = Number(p?.port);
|
||||
const protocolType = PROTOCOLS[index];
|
||||
return protocolType && p && Number.isFinite(port) && port > 0 && port <= 65535;
|
||||
});
|
||||
|
||||
if (filtered.length === 0) {
|
||||
toast.error(t('validation_failed'));
|
||||
@@ -379,7 +364,6 @@ export default function ServerForm(props: {
|
||||
ratio: 1,
|
||||
protocols: full,
|
||||
});
|
||||
setProtocolsEnabled([]);
|
||||
}
|
||||
setOpen(true);
|
||||
}}
|
||||
@@ -481,72 +465,66 @@ export default function ServerForm(props: {
|
||||
{t('protocol_configurations_desc')}
|
||||
</p>
|
||||
</div>
|
||||
<Accordion type='single' className='w-full space-y-3'>
|
||||
<Accordion
|
||||
type='single'
|
||||
collapsible
|
||||
className='w-full space-y-3'
|
||||
value={accordionValue}
|
||||
onValueChange={setAccordionValue}
|
||||
>
|
||||
{PROTOCOLS.map((type) => {
|
||||
const i = Math.max(
|
||||
0,
|
||||
PROTOCOLS.findIndex((t) => t === type),
|
||||
);
|
||||
const current = Array.isArray(protocolsValues) ? protocolsValues[i] || {} : {};
|
||||
const isEnabled = protocolsEnabled.includes(type);
|
||||
const current = (protocolsValues[i] || {}) as Record<string, any>;
|
||||
const isEnabled = current.port && Number(current.port) > 0;
|
||||
const fields = PROTOCOL_FIELDS[type] || [];
|
||||
|
||||
return (
|
||||
<AccordionItem key={type} value={type} className='mb-2 rounded-lg border'>
|
||||
<AccordionTrigger className='px-4 py-3 hover:no-underline'>
|
||||
<div className='flex w-full items-center justify-between'>
|
||||
<div className='flex flex-col items-start'>
|
||||
<span className='font-medium'>{titleCase(type)}</span>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='font-medium capitalize'>{type}</span>
|
||||
</div>
|
||||
<span className='text-muted-foreground text-xs'>
|
||||
{isEnabled ? t('enabled') : t('disabled')}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-3'>
|
||||
{isEnabled && (
|
||||
<div className='flex h-2 w-2 rounded-full bg-green-500'></div>
|
||||
<div className='mr-2 flex items-center gap-1'>
|
||||
{current.transport && (
|
||||
<Badge variant='secondary' className='text-xs'>
|
||||
{current.transport.toUpperCase()}
|
||||
</Badge>
|
||||
)}
|
||||
<Switch
|
||||
className='mr-2'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
checked={isEnabled}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setProtocolsEnabled([...protocolsEnabled, type]);
|
||||
} else {
|
||||
setProtocolsEnabled(protocolsEnabled.filter((p) => p !== type));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{current.security && current.security !== 'none' && (
|
||||
<Badge variant='outline' className='text-xs'>
|
||||
{current.security.toUpperCase()}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{current.port && <Badge className='text-xs'>{current.port}</Badge>}
|
||||
</div>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
{isEnabled && (
|
||||
<AccordionContent className='px-4 pb-4 pt-0'>
|
||||
<div className='-mx-4 space-y-4 rounded-b-lg border-t px-4 pt-4'>
|
||||
{renderGroupCard('basic', fields, 'basic', control, i, current, t)}
|
||||
{renderGroupCard('plugin', fields, 'plugin', control, i, current, t)}
|
||||
{renderGroupCard(
|
||||
'transport',
|
||||
fields,
|
||||
'transport',
|
||||
control,
|
||||
i,
|
||||
current,
|
||||
t,
|
||||
)}
|
||||
{renderGroupCard(
|
||||
'security',
|
||||
fields,
|
||||
'security',
|
||||
control,
|
||||
i,
|
||||
current,
|
||||
t,
|
||||
)}
|
||||
{renderGroupCard('reality', fields, 'reality', control, i, current, t)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
)}
|
||||
<AccordionContent className='px-4 pb-4 pt-0'>
|
||||
<div className='-mx-4 space-y-4 rounded-b-lg border-t px-4 pt-4'>
|
||||
{renderGroupCard('basic', fields, 'basic', control, i, current, t)}
|
||||
{renderGroupCard('plugin', fields, 'plugin', control, i, current, t)}
|
||||
{renderGroupCard(
|
||||
'transport',
|
||||
fields,
|
||||
'transport',
|
||||
control,
|
||||
i,
|
||||
current,
|
||||
t,
|
||||
)}
|
||||
{renderGroupCard('security', fields, 'security', control, i, current, t)}
|
||||
{renderGroupCard('reality', fields, 'reality', control, i, current, t)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user