diff --git a/frontend/eslint.deprecated.config.js b/frontend/eslint.deprecated.config.js new file mode 100644 index 00000000..5ac95dc5 --- /dev/null +++ b/frontend/eslint.deprecated.config.js @@ -0,0 +1,43 @@ +import tseslint from 'typescript-eslint'; + +export default [ + { ignores: ['node_modules/**', '../web/dist/**', 'src/generated/**'] }, + ...tseslint.configs.recommendedTypeChecked.map((config) => ({ + ...config, + files: ['**/*.{ts,tsx}'], + languageOptions: { + ...config.languageOptions, + parserOptions: { + ...config.languageOptions?.parserOptions, + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + })), + { + files: ['**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-deprecated': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-misused-promises': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-base-to-string': 'off', + '@typescript-eslint/no-redundant-type-constituents': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/await-thenable': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'off', + '@typescript-eslint/only-throw-error': 'off', + '@typescript-eslint/no-unnecessary-type-assertion': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, +]; diff --git a/frontend/src/components/FinalMaskForm.tsx b/frontend/src/components/FinalMaskForm.tsx index 23f2f02b..682eee75 100644 --- a/frontend/src/components/FinalMaskForm.tsx +++ b/frontend/src/components/FinalMaskForm.tsx @@ -1,4 +1,4 @@ -import { Button, Divider, Form, Input, InputNumber, Select, Switch } from 'antd'; +import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd'; import { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons'; import type { FormInstance } from 'antd/es/form'; import type { NamePath } from 'antd/es/form/interface'; @@ -638,7 +638,7 @@ function ItemEditor({ if (type === 'base64') { return ( - + @@ -646,7 +646,7 @@ function ItemEditor({ icon={} onClick={() => form.setFieldValue([...absoluteItemPath, 'packet'], RandomUtil.randomBase64())} /> - + ); } diff --git a/frontend/src/lib/xray/inbound-form-adapter.ts b/frontend/src/lib/xray/inbound-form-adapter.ts index 596266a3..322744f1 100644 --- a/frontend/src/lib/xray/inbound-form-adapter.ts +++ b/frontend/src/lib/xray/inbound-form-adapter.ts @@ -179,7 +179,7 @@ export function pruneEmpty(value: unknown): unknown { // those inside a vless inbound's settings.clients is confusing and rides // dead weight in the wire payload. Parsing through the protocol's schema // gives us the canonical projection. -function clientSchemaForProtocol(protocol: string): z.ZodTypeAny | null { +function clientSchemaForProtocol(protocol: string): z.ZodType | null { switch (protocol) { case 'vless': return VlessClientSchema; case 'vmess': return VmessClientSchema; diff --git a/frontend/src/pages/clients/ClientBulkAddModal.tsx b/frontend/src/pages/clients/ClientBulkAddModal.tsx index ba870274..3039d3bc 100644 --- a/frontend/src/pages/clients/ClientBulkAddModal.tsx +++ b/frontend/src/pages/clients/ClientBulkAddModal.tsx @@ -62,10 +62,10 @@ export default function ClientBulkAddModal({ useEffect(() => { if (!open) return; - + setForm(emptyForm()); setDelayedStart(false); - + }, [open]); function update(key: K, value: FormState[K]) { @@ -87,7 +87,7 @@ export default function ClientBulkAddModal({ useEffect(() => { if (!showFlow && form.flow) { - + update('flow', ''); } }, [showFlow, form.flow]); @@ -186,130 +186,131 @@ export default function ClientBulkAddModal({ open={open} title={t('pages.clients.bulk')} okText={t('create')} - cancelText={t('close')} - confirmLoading={saving} - mask={{ closable: false }} - width={640} - onOk={submit} - onCancel={() => onOpenChange(false)} - > -
- - update('emailMethod', v)} - options={[ - { value: 0, label: 'Random' }, - { value: 1, label: 'Random + Prefix' }, - { value: 2, label: 'Random + Prefix + Num' }, - { value: 3, label: 'Random + Prefix + Num + Postfix' }, - { value: 4, label: 'Prefix + Num + Postfix' }, - ]} - /> - - - {form.emailMethod > 1 && ( - <> - - update('firstNum', Number(v) || 1)} /> - - - update('lastNum', Number(v) || 1)} /> - - - )} - {form.emailMethod > 0 && ( - - update('emailPrefix', e.target.value)} /> - - )} - {form.emailMethod > 2 && ( - - update('emailPostfix', e.target.value)} /> - - )} - {form.emailMethod < 2 && ( - - update('quantity', Number(v) || 1)} /> - - )} - - - {t('subscription.title')} - update('subId', RandomUtil.randomLowerAndNum(16))} - /> - - }> - update('subId', e.target.value)} /> - - - - update('comment', e.target.value)} /> - - - {showFlow && ( - + cancelText={t('close')} + confirmLoading={saving} + mask={{ closable: false }} + width={640} + onOk={submit} + onCancel={() => onOpenChange(false)} + > + + update('emailMethod', v)} options={[ - { value: '', label: t('none') }, - ...FLOW_OPTIONS.map((k) => ({ value: k, label: k })), + { value: 0, label: 'Random' }, + { value: 1, label: 'Random + Prefix' }, + { value: 2, label: 'Random + Prefix + Num' }, + { value: 3, label: 'Random + Prefix + Num + Postfix' }, + { value: 4, label: 'Prefix + Num + Postfix' }, ]} /> - )} - {ipLimitEnable && ( - - update('limitIp', Number(v) || 0)} /> + {form.emailMethod > 1 && ( + <> + + update('firstNum', Number(v) || 1)} /> + + + update('lastNum', Number(v) || 1)} /> + + + )} + {form.emailMethod > 0 && ( + + update('emailPrefix', e.target.value)} /> + + )} + {form.emailMethod > 2 && ( + + update('emailPostfix', e.target.value)} /> + + )} + {form.emailMethod < 2 && ( + + update('quantity', Number(v) || 1)} /> + + )} + + + {t('subscription.title')} + update('subId', RandomUtil.randomLowerAndNum(16))} + /> + + }> + update('subId', e.target.value)} /> - )} - - update('totalGB', Number(v) || 0)} /> - + + update('comment', e.target.value)} /> + - - { setDelayedStart(!delayedStart); update('expiryTime', 0); }} - /> - + {showFlow && ( + + update('email', e.target.value)} - /> - - - - - - - - update('subId', e.target.value)} /> - - - - - - - - - - - update('auth', e.target.value)} /> - - - - - - - - update('password', e.target.value)} /> - - - - - - - - - - - update('uuid', e.target.value)} /> - - - - - - - update('totalGB', Number(v) || 0)} /> - - - {ipLimitEnable && ( - - - update('limitIp', Number(v) || 0)} /> + okText={isEdit ? t('save') : t('create')} + cancelText={t('cancel')} + okButtonProps={{ loading: submitting }} + width={720} + onOk={onSubmit} + onCancel={close} + > + + + + + + update('email', e.target.value)} + /> + + - )} - - - - - {form.delayedStart ? ( - - update('delayedDays', Number(v) || 0)} /> + + + + update('subId', e.target.value)} /> + + - ) : ( - - update('expiryDate', d || null)} - /> - - )} - - - - { - update('delayedStart', v); - if (v) update('expiryDate', null); - else update('delayedDays', 0); - }} - /> - - - + + - {(showFlow || showReverseTag) && ( - {showFlow && ( - - - update('reverseTag', e.target.value)} /> + + + + update('auth', e.target.value)} /> + + + + + + + + update('password', e.target.value)} /> + + + + + + + + + + + update('uuid', e.target.value)} /> + + + + + + + update('totalGB', Number(v) || 0)} /> + + + {ipLimitEnable && ( + + + update('limitIp', Number(v) || 0)} /> )} - )} - - {tgBotEnable && ( + - - update('tgId', Number(v) || 0)} /> + {form.delayedStart ? ( + + update('delayedDays', Number(v) || 0)} /> + + ) : ( + + update('expiryDate', d || null)} + /> + + )} + + + + { + update('delayedStart', v); + if (v) update('expiryDate', null); + else update('delayedDays', 0); + }} + /> + + + {(showFlow || showReverseTag) && ( + + {showFlow && ( + + + update('reverseTag', e.target.value)} /> + + + )} + )} - - - update('comment', e.target.value)} /> - - - - - update('comment', e.target.value)} /> + + + + + + + options={[ + { value: null, label: t('pages.inbounds.localPanel') }, + ...selectableNodes.map((n) => ({ + value: n.id, + label: `${n.name}${n.status === 'offline' ? ' (offline)' : ''}`, + disabled: n.status === 'offline', + })), + ]} + /> )} @@ -924,13 +921,12 @@ export default function InboundFormModal({ - + - TCP, UDP - TCP - UDP - + - noauth - password - + + options={SSMethodSchema.options.map((m) => ({ value: m, label: m }))} + /> {isSSWith2022 && ( )} - + + options={[ + { value: 'tcp', label: 'TCP (RAW)' }, + { value: 'kcp', label: 'mKCP' }, + { value: 'ws', label: 'WebSocket' }, + { value: 'grpc', label: 'gRPC' }, + { value: 'httpupgrade', label: 'HTTPUpgrade' }, + { value: 'xhttp', label: 'XHTTP' }, + ]} + /> )} @@ -1792,11 +1793,13 @@ export default function InboundFormModal({ - + - Default (POST) - POST - PUT - - GET (packet-up only) - - + - Default (queryInHeader) - queryInHeader - header - cookie - query - + - Default (repeat-x) - repeat-x - tokenish - + - Default (path) - path - header - cookie - query - + - Default (path) - path - header - cookie - query - + - Default (body) - body - header - cookie - query - + - {t('pages.inbounds.same')} - {t('none')} - TLS - + @@ -2104,19 +2124,28 @@ export default function InboundFormModal({ - + - {Object.values(ALPN_OPTION).map((a) => ( - {a} - ))} - + - {Object.values(DOMAIN_STRATEGY_OPTION).map((d) => ( - {d} - ))} - + - {Object.values(TCP_CONGESTION_OPTION).map((c) => ( - {c} - ))} - + - Off - Redirect - TProxy - + @@ -2257,22 +2287,26 @@ export default function InboundFormModal({ name={['streamSettings', 'sockopt', 'trustedXForwardedFor']} label="Trusted X-Forwarded-For" > - + - {Object.values(Address_Port_Strategy).map((v) => ( - {v} - ))} - + - + - {Object.values(TLS_VERSION_OPTION).map((v) => ( - {v} - ))} - + - {Object.values(TLS_VERSION_OPTION).map((v) => ( - {v} - ))} - + - None - {Object.values(UTLS_FINGERPRINT).map((fp) => ( - {fp} - ))} - + - {Object.values(ALPN_OPTION).map((a) => ( - {a} - ))} - + - {Object.values(USAGE_OPTION).map((u) => ( - {u} - ))} - + - {Object.values(UTLS_FINGERPRINT).map((fp) => ( - {fp} - ))} - + - 10 - 20 - 50 - 100 - 500 - - + diff --git a/frontend/src/pages/index/XrayLogModal.tsx b/frontend/src/pages/index/XrayLogModal.tsx index b2ca34f4..a9284587 100644 --- a/frontend/src/pages/index/XrayLogModal.tsx +++ b/frontend/src/pages/index/XrayLogModal.tsx @@ -124,13 +124,19 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) { > - + setAlertVisible(false) }} className="conf-alert" - onClose={() => setAlertVisible(false)} title={t('pages.settings.securityWarnings')} description={( <> diff --git a/frontend/src/pages/xray/NordModal.tsx b/frontend/src/pages/xray/NordModal.tsx index 5ef1a934..13660876 100644 --- a/frontend/src/pages/xray/NordModal.tsx +++ b/frontend/src/pages/xray/NordModal.tsx @@ -318,8 +318,7 @@ export default function NordModal({ ({ value: c.id, label: c.name }))]} /> @@ -344,8 +342,7 @@ export default function NordModal({