external-proxy.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { useTranslation } from 'react-i18next';
  2. import { Button, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
  3. import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
  4. import { InputAddon } from '@/components/ui';
  5. import { ALPN_OPTION, UTLS_FINGERPRINT } from '@/schemas/primitives';
  6. export default function ExternalProxyForm({
  7. toggleExternalProxy,
  8. }: {
  9. toggleExternalProxy: (on: boolean) => void;
  10. }) {
  11. const { t } = useTranslation();
  12. return (
  13. <Form.Item
  14. noStyle
  15. shouldUpdate={(prev, curr) => {
  16. const a = (prev.streamSettings as { externalProxy?: unknown[] } | undefined)?.externalProxy;
  17. const b = (curr.streamSettings as { externalProxy?: unknown[] } | undefined)?.externalProxy;
  18. return (Array.isArray(a) ? a.length : 0) !== (Array.isArray(b) ? b.length : 0);
  19. }}
  20. >
  21. {({ getFieldValue }) => {
  22. const arr = getFieldValue(['streamSettings', 'externalProxy']);
  23. const on = Array.isArray(arr) && arr.length > 0;
  24. return (
  25. <>
  26. <Form.Item label={t('pages.inbounds.form.externalProxy')}>
  27. <Switch checked={on} onChange={toggleExternalProxy} />
  28. </Form.Item>
  29. {on && (
  30. <Form.List name={['streamSettings', 'externalProxy']}>
  31. {(fields, { add, remove }) => (
  32. <>
  33. <Form.Item label=" " colon={false}>
  34. <Button
  35. size="small"
  36. type="primary"
  37. onClick={() => add({
  38. forceTls: 'same',
  39. dest: '',
  40. port: 443,
  41. remark: '',
  42. sni: '',
  43. fingerprint: '',
  44. alpn: [],
  45. })}
  46. >
  47. <PlusOutlined />
  48. </Button>
  49. </Form.Item>
  50. <Form.Item wrapperCol={{ span: 24 }}>
  51. {fields.map((field) => (
  52. <div key={field.key} style={{ margin: '8px 0' }}>
  53. <Space.Compact block>
  54. <Form.Item name={[field.name, 'forceTls']} noStyle>
  55. <Select
  56. style={{ width: '20%' }}
  57. options={[
  58. { value: 'same', label: t('pages.inbounds.same') },
  59. { value: 'none', label: t('none') },
  60. { value: 'tls', label: 'TLS' },
  61. ]}
  62. />
  63. </Form.Item>
  64. <Form.Item name={[field.name, 'dest']} noStyle>
  65. <Input style={{ width: '30%' }} placeholder={t('host')} />
  66. </Form.Item>
  67. <Form.Item name={[field.name, 'port']} noStyle>
  68. <InputNumber style={{ width: '15%' }} min={1} max={65535} />
  69. </Form.Item>
  70. <Form.Item name={[field.name, 'remark']} noStyle>
  71. <Input style={{ width: '25%' }} placeholder={t('pages.inbounds.remark')} />
  72. </Form.Item>
  73. <InputAddon onClick={() => remove(field.name)}>
  74. <MinusOutlined />
  75. </InputAddon>
  76. </Space.Compact>
  77. <Form.Item
  78. noStyle
  79. shouldUpdate={(prev, curr) =>
  80. prev.streamSettings?.externalProxy?.[field.name]?.forceTls
  81. !== curr.streamSettings?.externalProxy?.[field.name]?.forceTls
  82. }
  83. >
  84. {({ getFieldValue }) => {
  85. const ft = getFieldValue([
  86. 'streamSettings', 'externalProxy', field.name, 'forceTls',
  87. ]);
  88. if (ft !== 'tls') return null;
  89. return (
  90. <Space.Compact style={{ marginTop: 6 }} block>
  91. <Form.Item name={[field.name, 'sni']} noStyle>
  92. <Input style={{ width: '30%' }} placeholder={t('pages.inbounds.form.sniPlaceholder')} />
  93. </Form.Item>
  94. <Form.Item name={[field.name, 'fingerprint']} noStyle>
  95. <Select
  96. style={{ width: '30%' }}
  97. placeholder={t('pages.inbounds.form.fingerprint')}
  98. options={[
  99. { value: '', label: t('pages.inbounds.form.defaultOption') },
  100. ...Object.values(UTLS_FINGERPRINT).map((fp) => ({
  101. value: fp,
  102. label: fp,
  103. })),
  104. ]}
  105. />
  106. </Form.Item>
  107. <Form.Item name={[field.name, 'alpn']} noStyle>
  108. <Select
  109. mode="multiple"
  110. style={{ width: '40%' }}
  111. placeholder="ALPN"
  112. options={Object.values(ALPN_OPTION).map((a) => ({
  113. value: a,
  114. label: a,
  115. }))}
  116. />
  117. </Form.Item>
  118. </Space.Compact>
  119. );
  120. }}
  121. </Form.Item>
  122. </div>
  123. ))}
  124. </Form.Item>
  125. </>
  126. )}
  127. </Form.List>
  128. )}
  129. </>
  130. );
  131. }}
  132. </Form.Item>
  133. );
  134. }