| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- import { useTranslation } from 'react-i18next';
- import { Button, Form, Input, InputNumber, Radio, Select, Space, Switch } from 'antd';
- import { MinusOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
- import {
- ALPN_OPTION,
- TLS_CIPHER_OPTION,
- TLS_VERSION_OPTION,
- USAGE_OPTION,
- UTLS_FINGERPRINT,
- } from '@/schemas/primitives';
- const { TextArea } = Input;
- interface TlsFormProps {
- saving: boolean;
- setCertFromPanel: (certName: number) => void;
- clearCertFiles: (certName: number) => void;
- generateRandomPinHash: () => void;
- getNewEchCert: () => void;
- clearEchCert: () => void;
- }
- export default function TlsForm({
- saving,
- setCertFromPanel,
- clearCertFiles,
- generateRandomPinHash,
- getNewEchCert,
- clearEchCert,
- }: TlsFormProps) {
- const { t } = useTranslation();
- return (
- <>
- <Form.Item name={['streamSettings', 'tlsSettings', 'serverName']} label="SNI">
- <Input placeholder={t('pages.inbounds.form.serverNameIndication')} />
- </Form.Item>
- <Form.Item name={['streamSettings', 'tlsSettings', 'cipherSuites']} label={t('pages.inbounds.form.cipherSuites')}>
- <Select
- options={[
- { value: '', label: t('pages.inbounds.form.autoOption') },
- ...Object.entries(TLS_CIPHER_OPTION).map(([k, v]) => ({ value: v, label: k })),
- ]}
- />
- </Form.Item>
- <Form.Item label={t('pages.inbounds.form.minMaxVersion')}>
- <Space.Compact block>
- <Form.Item name={['streamSettings', 'tlsSettings', 'minVersion']} noStyle>
- <Select
- style={{ width: '50%' }}
- options={Object.values(TLS_VERSION_OPTION).map((v) => ({ value: v, label: v }))}
- />
- </Form.Item>
- <Form.Item name={['streamSettings', 'tlsSettings', 'maxVersion']} noStyle>
- <Select
- style={{ width: '50%' }}
- options={Object.values(TLS_VERSION_OPTION).map((v) => ({ value: v, label: v }))}
- />
- </Form.Item>
- </Space.Compact>
- </Form.Item>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'settings', 'fingerprint']}
- label="uTLS"
- >
- <Select
- options={[
- { value: '', label: 'None' },
- ...Object.values(UTLS_FINGERPRINT).map((fp) => ({ value: fp, label: fp })),
- ]}
- />
- </Form.Item>
- <Form.Item name={['streamSettings', 'tlsSettings', 'alpn']} label="ALPN">
- <Select
- mode="multiple"
- tokenSeparators={[',']}
- style={{ width: '100%' }}
- options={Object.values(ALPN_OPTION).map((a) => ({ value: a, label: a }))}
- />
- </Form.Item>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'rejectUnknownSni']}
- label={t('pages.inbounds.form.rejectUnknownSni')}
- valuePropName="checked"
- >
- <Switch />
- </Form.Item>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'disableSystemRoot']}
- label={t('pages.inbounds.form.disableSystemRoot')}
- valuePropName="checked"
- >
- <Switch />
- </Form.Item>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'enableSessionResumption']}
- label={t('pages.inbounds.form.sessionResumption')}
- valuePropName="checked"
- >
- <Switch />
- </Form.Item>
- <Form.List name={['streamSettings', 'tlsSettings', 'certificates']}>
- {(certFields, { add, remove }) => (
- <>
- <Form.Item label={t('certificate')}>
- <Button
- type="primary"
- size="small"
- onClick={() => add({
- useFile: true,
- certificateFile: '',
- keyFile: '',
- certificate: [],
- key: [],
- ocspStapling: 3600,
- oneTimeLoading: false,
- usage: 'encipherment',
- buildChain: false,
- })}
- >
- <PlusOutlined />
- </Button>
- </Form.Item>
- {certFields.map((certField, idx) => (
- <div key={certField.key}>
- <Form.Item
- name={[certField.name, 'useFile']}
- label={`${t('certificate')} ${idx + 1}`}
- >
- <Radio.Group buttonStyle="solid">
- <Radio.Button value={true}>
- {t('pages.inbounds.certificatePath')}
- </Radio.Button>
- <Radio.Button value={false}>
- {t('pages.inbounds.certificateContent')}
- </Radio.Button>
- </Radio.Group>
- </Form.Item>
- {certFields.length > 1 && (
- <Form.Item label=" ">
- <Button
- size="small"
- danger
- onClick={() => remove(certField.name)}
- >
- <MinusOutlined /> {t('remove')}
- </Button>
- </Form.Item>
- )}
- <Form.Item
- noStyle
- shouldUpdate={(prev, curr) =>
- prev.streamSettings?.tlsSettings?.certificates?.[certField.name]?.useFile
- !== curr.streamSettings?.tlsSettings?.certificates?.[certField.name]?.useFile
- }
- >
- {({ getFieldValue }) => {
- const useFile = getFieldValue([
- 'streamSettings', 'tlsSettings', 'certificates',
- certField.name, 'useFile',
- ]);
- return useFile ? (
- <>
- <Form.Item
- name={[certField.name, 'certificateFile']}
- label={t('pages.inbounds.publicKey')}
- >
- <Input />
- </Form.Item>
- <Form.Item
- name={[certField.name, 'keyFile']}
- label={t('pages.inbounds.privatekey')}
- >
- <Input />
- </Form.Item>
- <Form.Item label=" ">
- <Space>
- <Button
- type="primary"
- loading={saving}
- onClick={() => setCertFromPanel(certField.name)}
- >
- {t('pages.inbounds.setDefaultCert')}
- </Button>
- <Button danger onClick={() => clearCertFiles(certField.name)}>
- {t('clear')}
- </Button>
- </Space>
- </Form.Item>
- </>
- ) : (
- <>
- <Form.Item
- name={[certField.name, 'certificate']}
- label={t('pages.inbounds.publicKey')}
- normalize={(v) => typeof v === 'string'
- ? v.split('\n')
- : v}
- getValueProps={(v) => ({
- value: Array.isArray(v) ? v.join('\n') : v,
- })}
- >
- <TextArea autoSize={{ minRows: 3, maxRows: 8 }} />
- </Form.Item>
- <Form.Item
- name={[certField.name, 'key']}
- label={t('pages.inbounds.privatekey')}
- normalize={(v) => typeof v === 'string'
- ? v.split('\n')
- : v}
- getValueProps={(v) => ({
- value: Array.isArray(v) ? v.join('\n') : v,
- })}
- >
- <TextArea autoSize={{ minRows: 3, maxRows: 8 }} />
- </Form.Item>
- </>
- );
- }}
- </Form.Item>
- <Form.Item
- name={[certField.name, 'ocspStapling']}
- label="OCSP Stapling"
- >
- <InputNumber min={0} addonAfter="s" style={{ width: '50%' }} />
- </Form.Item>
- <Form.Item
- name={[certField.name, 'oneTimeLoading']}
- label={t('pages.inbounds.form.oneTimeLoading')}
- valuePropName="checked"
- >
- <Switch />
- </Form.Item>
- <Form.Item
- name={[certField.name, 'usage']}
- label={t('pages.inbounds.form.usageOption')}
- >
- <Select
- style={{ width: '50%' }}
- options={Object.values(USAGE_OPTION).map((u) => ({ value: u, label: u }))}
- />
- </Form.Item>
- <Form.Item
- noStyle
- shouldUpdate={(prev, curr) =>
- prev.streamSettings?.tlsSettings?.certificates?.[certField.name]?.usage
- !== curr.streamSettings?.tlsSettings?.certificates?.[certField.name]?.usage
- }
- >
- {({ getFieldValue }) => {
- const usage = getFieldValue([
- 'streamSettings', 'tlsSettings', 'certificates',
- certField.name, 'usage',
- ]);
- if (usage !== 'issue') return null;
- return (
- <Form.Item
- name={[certField.name, 'buildChain']}
- label={t('pages.inbounds.form.buildChain')}
- valuePropName="checked"
- >
- <Switch />
- </Form.Item>
- );
- }}
- </Form.Item>
- </div>
- ))}
- </>
- )}
- </Form.List>
- <Form.Item name={['streamSettings', 'tlsSettings', 'echServerKeys']} label={t('pages.inbounds.form.echKey')}>
- <Input />
- </Form.Item>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'settings', 'echConfigList']}
- label={t('pages.inbounds.form.echConfig')}
- >
- <Input />
- </Form.Item>
- <Form.Item
- label={t('pages.inbounds.form.pinnedPeerCertSha256')}
- tooltip={t('pages.inbounds.form.pinnedPeerCertSha256Tip')}
- >
- <Space.Compact block>
- <Form.Item
- name={['streamSettings', 'tlsSettings', 'settings', 'pinnedPeerCertSha256']}
- noStyle
- >
- <Select
- mode="tags"
- tokenSeparators={[',', ' ']}
- placeholder={t('pages.inbounds.form.pinnedPeerCertSha256Placeholder')}
- style={{ width: 'calc(100% - 32px)' }}
- />
- </Form.Item>
- <Button
- icon={<ReloadOutlined />}
- onClick={generateRandomPinHash}
- title={t('pages.inbounds.form.generateRandomPin')}
- />
- </Space.Compact>
- </Form.Item>
- <Form.Item label=" ">
- <Space>
- <Button type="primary" loading={saving} onClick={getNewEchCert}>
- {t('pages.inbounds.form.getNewEchCert')}
- </Button>
- <Button danger onClick={clearEchCert}>{t('clear')}</Button>
- </Space>
- </Form.Item>
- </>
- );
- }
|