精简版第四轮补裁(P4): 页面层摘除 + Xray更新端点下线 + 本机核心监管下线
CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
页面层(用户真机截图发现前两轮漏裁): - 设置: 摘除 Telegram机器人/邮件菜单与页面, 及页内残留入口(通知页/外部流量/LDAP/2FA设置页) - Xray配置: 摘除负载均衡/DNS/高级配置菜单与页面; 基础配置(出站+路由)完好 - 总览: 摘除 Xray更新弹窗(VersionModal+GeodataSection)全部入口, 版本号改为静态展示 - 13个locale同步摘除461个已删功能专属翻译键(逐键核查前后端引用为零; i18n-dead-keys测试全绿) Xray更新后端(经调查确认仅服务本机bin/xray, 与agent下发/订阅无关): - 删除 getXrayVersion/installXray/updateGeofile 路由与handler(访问返回404) - 删除 ServerService 对应实现与校验和函数; 同步api-docs/monitor白名单/openapi 本机核心监管下线(设计: 主控永不运行本机xray, 入站全在agent): - XrayService.RestartXray 降级为no-op(一次性Warning说明日志); StopXray无进程时幂等返回 - web.go 摘除启动拉起本机核心/CheckXrayRunning看门狗(1s)/ApplyPendingRestart cron(30s)/XrayTrafficJob/CheckClientIpJob 注册 - ApplyPendingRestart 降级为消费即清, 其测试更新为新语义 - XrayMetricsService.Sample 短路: 不再每2s探本机/debug/vars, 状态优雅置为不可用 - 配置生成(纯JSON)/agent链路/订阅/入站CRUD未动; 状态读数优雅归零(stop/Unknown) 注: TestGetXrayConfig_EnabledClientsStillEmitted 在基线945b3a5同样失败(既有问题, 非本轮引入)
This commit is contained in:
@@ -1,14 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Input, InputNumber, Select, Switch, Tabs } from 'antd';
|
||||
import {
|
||||
ApartmentOutlined,
|
||||
BellOutlined,
|
||||
ClockCircleOutlined,
|
||||
GlobalOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SettingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { ClockCircleOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import type { AllSetting } from '@/models/setting';
|
||||
import { HttpUtil, LanguageManager } from '@/utils';
|
||||
import { onNumber } from '@/utils/onNumber';
|
||||
@@ -16,7 +9,6 @@ import { DefaultSettingTag, SettingListItem } from '@/components/ui';
|
||||
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
||||
import { catTabLabel } from './catTabLabel';
|
||||
import { sanitizePath } from './uriPath';
|
||||
import SecretInput from './SecretInput';
|
||||
|
||||
interface ApiMsg<T = unknown> {
|
||||
success?: boolean;
|
||||
@@ -33,39 +25,9 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
|
||||
const { isMobile } = useMediaQuery();
|
||||
|
||||
const [lang, setLang] = useState<string>(() => LanguageManager.getLanguage());
|
||||
const [inboundOptions, setInboundOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [outboundTagList, setOutboundTagList] = useState<string[]>([]);
|
||||
const [balancerTagList, setBalancerTagList] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
// /options is the slim picker-shaped endpoint — it skips the heavy
|
||||
// per-client settings and clientStats payloads that /list ships.
|
||||
const msg = (await HttpUtil.get('/panel/api/inbounds/options')) as ApiMsg<
|
||||
{
|
||||
tag: string;
|
||||
protocol: string;
|
||||
port: number;
|
||||
}[]
|
||||
>;
|
||||
if (cancelled) return;
|
||||
if (msg?.success && Array.isArray(msg.obj)) {
|
||||
setInboundOptions(
|
||||
msg.obj.map((ib) => ({
|
||||
label: `${ib.tag} (${ib.protocol}@${ib.port})`,
|
||||
value: ib.tag,
|
||||
})),
|
||||
);
|
||||
} else {
|
||||
setInboundOptions([]);
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
@@ -136,20 +98,6 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
|
||||
];
|
||||
}, [outboundTagList, balancerTagList, t]);
|
||||
|
||||
const ldapInboundTagList = useMemo(() => {
|
||||
const csv = allSetting.ldapInboundTags || '';
|
||||
return csv.length
|
||||
? csv
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
}, [allSetting.ldapInboundTags]);
|
||||
|
||||
function setLdapInboundTagList(list: string[]) {
|
||||
updateSetting({ ldapInboundTags: Array.isArray(list) ? list.join(',') : '' });
|
||||
}
|
||||
|
||||
function onLangChange(value: string) {
|
||||
setLang(value);
|
||||
LanguageManager.setLanguage(value);
|
||||
@@ -327,43 +275,6 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: catTabLabel(<BellOutlined />, t('pages.settings.notifications'), isMobile),
|
||||
children: (
|
||||
<>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.expireTimeDiff')}
|
||||
badge={<DefaultSettingTag settingKey="expireDiff" value={allSetting.expireDiff} />}
|
||||
description={t('pages.settings.expireTimeDiffDesc')}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.expireDiff}
|
||||
min={0}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ expireDiff: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.trafficDiff')}
|
||||
badge={
|
||||
<DefaultSettingTag settingKey="trafficDiff" value={allSetting.trafficDiff} />
|
||||
}
|
||||
description={t('pages.settings.trafficDiffDesc')}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.trafficDiff}
|
||||
min={0}
|
||||
max={100}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ trafficDiff: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: catTabLabel(<SafetyCertificateOutlined />, t('pages.settings.certs'), isMobile),
|
||||
@@ -392,35 +303,6 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
label: catTabLabel(<GlobalOutlined />, t('pages.settings.externalTraffic'), isMobile),
|
||||
children: (
|
||||
<>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.externalTrafficInformEnable')}
|
||||
description={t('pages.settings.externalTrafficInformEnableDesc')}
|
||||
>
|
||||
<Switch
|
||||
checked={allSetting.externalTrafficInformEnable}
|
||||
onChange={(v) => updateSetting({ externalTrafficInformEnable: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.externalTrafficInformURI')}
|
||||
description={t('pages.settings.externalTrafficInformURIDesc')}
|
||||
>
|
||||
<Input
|
||||
value={allSetting.externalTrafficInformURI}
|
||||
placeholder="(http|https)://domain[:port]/path/"
|
||||
onChange={(e) => updateSetting({ externalTrafficInformURI: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
label: catTabLabel(<ClockCircleOutlined />, t('pages.settings.dateAndTime'), isMobile),
|
||||
@@ -454,225 +336,6 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
label: catTabLabel(<ApartmentOutlined />, 'LDAP', isMobile),
|
||||
children: (
|
||||
<>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.enable')}>
|
||||
<Switch
|
||||
checked={allSetting.ldapEnable}
|
||||
onChange={(v) => updateSetting({ ldapEnable: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.host')}>
|
||||
<Input
|
||||
value={allSetting.ldapHost}
|
||||
onChange={(e) => updateSetting({ ldapHost: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.port')}
|
||||
badge={<DefaultSettingTag settingKey="ldapPort" value={allSetting.ldapPort} />}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.ldapPort}
|
||||
min={1}
|
||||
max={65535}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ ldapPort: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.useTls')}>
|
||||
<Switch
|
||||
checked={allSetting.ldapUseTLS}
|
||||
onChange={(v) => updateSetting({ ldapUseTLS: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.skipTlsVerify')}
|
||||
description={t('pages.settings.ldap.skipTlsVerifyDesc')}
|
||||
>
|
||||
<Switch
|
||||
checked={allSetting.ldapInsecureSkipVerify}
|
||||
disabled={!allSetting.ldapUseTLS}
|
||||
onChange={(v) => updateSetting({ ldapInsecureSkipVerify: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.bindDn')}>
|
||||
<Input
|
||||
value={allSetting.ldapBindDN}
|
||||
onChange={(e) => updateSetting({ ldapBindDN: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('password')}
|
||||
description={
|
||||
allSetting.hasLdapPassword && !allSetting.clearLdapPassword
|
||||
? t('pages.settings.ldap.passwordConfigured')
|
||||
: t('pages.settings.ldap.passwordUnconfigured')
|
||||
}
|
||||
>
|
||||
<SecretInput
|
||||
value={allSetting.ldapPassword}
|
||||
configured={allSetting.hasLdapPassword}
|
||||
clearArmed={allSetting.clearLdapPassword}
|
||||
placeholder={t('pages.settings.ldap.passwordPlaceholder')}
|
||||
onChange={(v) => updateSetting({ ldapPassword: v })}
|
||||
onClearArmedChange={(armed) => updateSetting({ clearLdapPassword: armed })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.baseDn')}>
|
||||
<Input
|
||||
value={allSetting.ldapBaseDN}
|
||||
onChange={(e) => updateSetting({ ldapBaseDN: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.userFilter')}>
|
||||
<Input
|
||||
value={allSetting.ldapUserFilter}
|
||||
onChange={(e) => updateSetting({ ldapUserFilter: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.userAttr')}>
|
||||
<Input
|
||||
value={allSetting.ldapUserAttr}
|
||||
onChange={(e) => updateSetting({ ldapUserAttr: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.vlessField')}>
|
||||
<Input
|
||||
value={allSetting.ldapVlessField}
|
||||
onChange={(e) => updateSetting({ ldapVlessField: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.flagField')}
|
||||
description={t('pages.settings.ldap.flagFieldDesc')}
|
||||
>
|
||||
<Input
|
||||
value={allSetting.ldapFlagField}
|
||||
onChange={(e) => updateSetting({ ldapFlagField: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.truthyValues')}
|
||||
description={t('pages.settings.ldap.truthyValuesDesc')}
|
||||
>
|
||||
<Input
|
||||
value={allSetting.ldapTruthyValues}
|
||||
onChange={(e) => updateSetting({ ldapTruthyValues: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.invertFlag')}
|
||||
description={t('pages.settings.ldap.invertFlagDesc')}
|
||||
>
|
||||
<Switch
|
||||
checked={allSetting.ldapInvertFlag}
|
||||
onChange={(v) => updateSetting({ ldapInvertFlag: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.syncSchedule')}
|
||||
description={t('pages.settings.ldap.syncScheduleDesc')}
|
||||
>
|
||||
<Input
|
||||
value={allSetting.ldapSyncCron}
|
||||
onChange={(e) => updateSetting({ ldapSyncCron: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.inboundTags')}
|
||||
description={t('pages.settings.ldap.inboundTagsDesc')}
|
||||
>
|
||||
<>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={ldapInboundTagList}
|
||||
onChange={setLdapInboundTagList}
|
||||
style={{ width: '100%' }}
|
||||
options={inboundOptions}
|
||||
/>
|
||||
{inboundOptions.length === 0 && (
|
||||
<div className="ldap-no-inbounds">{t('pages.settings.ldap.noInbounds')}</div>
|
||||
)}
|
||||
</>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.autoCreate')}>
|
||||
<Switch
|
||||
checked={allSetting.ldapAutoCreate}
|
||||
onChange={(v) => updateSetting({ ldapAutoCreate: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.ldap.autoDelete')}>
|
||||
<Switch
|
||||
checked={allSetting.ldapAutoDelete}
|
||||
onChange={(v) => updateSetting({ ldapAutoDelete: v })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.defaultTotalGb')}
|
||||
badge={
|
||||
<DefaultSettingTag
|
||||
settingKey="ldapDefaultTotalGB"
|
||||
value={allSetting.ldapDefaultTotalGB}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.ldapDefaultTotalGB}
|
||||
min={0}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ ldapDefaultTotalGB: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.defaultExpiryDays')}
|
||||
badge={
|
||||
<DefaultSettingTag
|
||||
settingKey="ldapDefaultExpiryDays"
|
||||
value={allSetting.ldapDefaultExpiryDays}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.ldapDefaultExpiryDays}
|
||||
min={0}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ ldapDefaultExpiryDays: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<SettingListItem
|
||||
paddings="small"
|
||||
title={t('pages.settings.ldap.defaultIpLimit')}
|
||||
badge={
|
||||
<DefaultSettingTag
|
||||
settingKey="ldapDefaultLimitIP"
|
||||
value={allSetting.ldapDefaultLimitIP}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InputNumber
|
||||
value={allSetting.ldapDefaultLimitIP}
|
||||
min={0}
|
||||
style={{ width: '100%' }}
|
||||
onChange={onNumber((v) => updateSetting({ ldapDefaultLimitIP: v }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user