🐛 fix: Remove unnecessary comments and improve variable handling in GoTemplateEditor; ensure zero value displays as empty in EnhancedInput

This commit is contained in:
web
2025-09-13 02:23:42 -07:00
parent 6376ec1a79
commit c4a47a47dd
27 changed files with 100 additions and 90 deletions
+28 -16
View File
@@ -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>
);
})}