mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-27 00:04:59 +03:00
b9eda09da9
npm install was failing with ERESOLVE: the lockfile pinned storybook 10.5.7 and vitest 4.1.10 as peers while package.json asked for ^10.5.9 and ^4.1.11, and npm would not move either. Neither npm update, a targeted install, nor --package-lock-only broke the cycle, so node_modules and package-lock.json were regenerated from scratch (601 packages, 0 vulnerabilities). oxlint 1.79.0 then promoted five React Compiler rules into the correctness category, flagging 101 pre-existing sites. 1.78.0 exits 0 on the same tree, so nothing in our code changed - the rule set grew. They are fixed rather than suppressed: - refs (31): latest-value ref writes moved out of render into an effect. onlineClientsRef turned out to be write-only and is gone; expireDiffRef and trafficDiffRef were replaced by reading the values directly. - set-state-in-effect (55): reset-on-open modals now adjust state during render; where an effect mixed a synchronous reset with an async fetch, the reset moved to render and the effect kept only the request. useMediaQuery became useSyncExternalStore. - preserve-manual-memoization (11): optional-chained deps the compiler cannot match, hoisted to locals or dropped where the memo wrapped a string concat. - purity (3): Date.now() in render replaced by a state-backed clock, which also refreshes the expiry tag every 60s instead of freezing it until the next unrelated re-render. - immutability (1): applyClientStatsEvent merged websocket traffic into DBInbound rows in place; it now rebuilds only the rows it touches. Two things fell out of that. clientCount is derived with useMemo instead of an imperative rebuildClientCount() called from five sites, which also fixes a staleness bug where changing the expiry or traffic threshold left the counts alone until some later rebuild. statsVersion existed only to force a re-render after an in-place mutation, is meaningless now that rows are replaced, and nothing read it, so it is removed. Also adds a lint:fix script - oxlint --fix was previously only reachable through the lint-staged hook.
99 lines
3.2 KiB
TypeScript
99 lines
3.2 KiB
TypeScript
import { useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Button, Form, Input, InputNumber, Select, Space, Typography } from 'antd';
|
|
import { Controller, useFormContext } from 'react-hook-form';
|
|
|
|
import { FormField } from '@/components/form/rhf';
|
|
import { VLESS_AUTH_LABEL_KEYS, type VlessAuthKind } from '@/lib/xray/vless-encryption';
|
|
|
|
interface VlessFieldsProps {
|
|
saving: boolean;
|
|
selectedVlessAuth: string;
|
|
vlessAuthKind: VlessAuthKind | null;
|
|
network: string;
|
|
security: string;
|
|
getNewVlessEnc: (kind: VlessAuthKind) => void;
|
|
clearVlessEnc: () => void;
|
|
}
|
|
|
|
export default function VlessFields({
|
|
saving,
|
|
selectedVlessAuth,
|
|
vlessAuthKind,
|
|
network,
|
|
security,
|
|
getNewVlessEnc,
|
|
clearVlessEnc,
|
|
}: VlessFieldsProps) {
|
|
const { t } = useTranslation();
|
|
const { control } = useFormContext();
|
|
const [authKind, setAuthKind] = useState<VlessAuthKind>(vlessAuthKind ?? 'x25519');
|
|
|
|
const [syncedAuthKind, setSyncedAuthKind] = useState(vlessAuthKind);
|
|
if (vlessAuthKind !== syncedAuthKind) {
|
|
setSyncedAuthKind(vlessAuthKind);
|
|
setAuthKind(vlessAuthKind ?? 'x25519');
|
|
}
|
|
|
|
const authOptions = (Object.entries(VLESS_AUTH_LABEL_KEYS) as [VlessAuthKind, string][]).map(
|
|
([value, labelKey]) => ({ value, label: t(labelKey) }),
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<FormField name={['settings', 'decryption']} label={t('pages.inbounds.decryption')}>
|
|
<Input />
|
|
</FormField>
|
|
<FormField name={['settings', 'encryption']} label={t('pages.inbounds.encryption')}>
|
|
<Input />
|
|
</FormField>
|
|
<Form.Item label={t('pages.inbounds.vlessAuthGenerate')}>
|
|
<Space size={8} wrap>
|
|
<Select
|
|
value={authKind}
|
|
onChange={(v) => setAuthKind(v)}
|
|
options={authOptions}
|
|
style={{ width: 240 }}
|
|
/>
|
|
<Button type="primary" loading={saving} onClick={() => getNewVlessEnc(authKind)}>
|
|
{t('pages.inbounds.vlessAuthGenerateButton')}
|
|
</Button>
|
|
<Button danger onClick={clearVlessEnc}>
|
|
{t('clear')}
|
|
</Button>
|
|
</Space>
|
|
<Typography.Text type="secondary" className="vless-auth-state">
|
|
{t('pages.inbounds.vlessAuthSelected', { auth: selectedVlessAuth })}
|
|
</Typography.Text>
|
|
</Form.Item>
|
|
{network === 'tcp' && (security === 'tls' || security === 'reality') && (
|
|
<Form.Item
|
|
label={t('pages.inbounds.form.visionTestseed')}
|
|
extra="Applies only to clients using the xtls-rprx-vision flow; ignored otherwise."
|
|
>
|
|
<Space.Compact block>
|
|
{[900, 500, 900, 256].map((def, i) => (
|
|
<Controller
|
|
key={i}
|
|
control={control}
|
|
name={`settings.testseed.${i}`}
|
|
defaultValue={def}
|
|
render={({ field }) => (
|
|
<InputNumber
|
|
min={1}
|
|
style={{ width: '25%' }}
|
|
value={field.value as number}
|
|
onChange={field.onChange}
|
|
onBlur={field.onBlur}
|
|
ref={field.ref}
|
|
/>
|
|
)}
|
|
/>
|
|
))}
|
|
</Space.Compact>
|
|
</Form.Item>
|
|
)}
|
|
</>
|
|
);
|
|
}
|