mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-20 18:11:00 +00:00
Add remote routing URL support (#6168)
* Add remote routing URL support * Harden remote routing refresh * fix(sub): harden remote routing fetch and accept Mihomo src rule flag Remote routing bytes reach the YAML/JSON parsers from goroutines that run outside Gin's recovery, so a parser panic on crafted input would take down the whole panel. Contain it in fetch() (a panic now degrades to a failed refresh that keeps the last-good value and releases the in-flight slot) and start the refresh, cache-load and startup-warm goroutines through common.GoRecover like the other background workers. The route-graph validator only skipped a trailing no-resolve flag, so a valid Mihomo rule like IP-CIDR,x,DIRECT,no-resolve,src was rejected as an unknown target; skip both option flags. Also deduplicate the HTTPS-source classification into common.ParseRemoteRoutingURL so the save-time validator and the resolver can never drift (internal/sub imports internal/web/service, so the copy existed only to avoid the import cycle), move the test-only mergeRemoteClashRulesYAML helper into the test file, and trim oversized comment blocks. --------- Co-authored-by: Duxxie <yelloduxx@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Alert, Button, Input, InputNumber, Switch, Tabs } from 'antd';
|
||||
import { Alert, Button, Input, InputNumber, Switch, Tabs, Tag } from 'antd';
|
||||
import { BranchesOutlined, CompassOutlined, IdcardOutlined, InfoCircleOutlined, NodeIndexOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router';
|
||||
@@ -15,6 +15,12 @@ interface SubscriptionGeneralTabProps {
|
||||
updateSetting: (patch: Partial<AllSetting>) => void;
|
||||
}
|
||||
|
||||
const isRemoteRoutingSource = (value: string) => /^https:\/\/\S+$/i.test(value.trim());
|
||||
|
||||
const remoteSourceBadge = (value: string) => (
|
||||
isRemoteRoutingSource(value) ? <Tag color="blue">HTTPS URL</Tag> : undefined
|
||||
);
|
||||
|
||||
export default function SubscriptionGeneralTab({ allSetting, updateSetting }: SubscriptionGeneralTabProps) {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -193,8 +199,8 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subEnableRouting')} description={t('pages.settings.subEnableRoutingDesc')}>
|
||||
<Switch checked={allSetting.subEnableRouting} onChange={(v) => updateSetting({ subEnableRouting: v })} />
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subRoutingRules')} description={t('pages.settings.subRoutingRulesDesc')}>
|
||||
<Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/add/..."
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subRoutingRules')} badge={remoteSourceBadge(allSetting.subRoutingRules)} description={t('pages.settings.subRoutingRulesDesc')}>
|
||||
<Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/onadd/... or https://.../DEFAULT.DEEPLINK"
|
||||
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })} />
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subHideSettings')} description={t('pages.settings.subHideSettingsDesc')}>
|
||||
@@ -211,11 +217,11 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subClashEnableRouting')} description={t('pages.settings.subClashEnableRoutingDesc')}>
|
||||
<Switch checked={allSetting.subClashEnableRouting} onChange={(v) => updateSetting({ subClashEnableRouting: v })} />
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subClashRoutingRules')} description={t('pages.settings.subClashRoutingRulesDesc')}>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subClashRoutingRules')} badge={remoteSourceBadge(allSetting.subClashRules)} description={t('pages.settings.subClashRoutingRulesDesc')}>
|
||||
<Input.TextArea
|
||||
value={allSetting.subClashRules}
|
||||
rows={8}
|
||||
placeholder={'GEOSITE,category-ir,DIRECT\nGEOIP,private,DIRECT'}
|
||||
placeholder={'https://.../routing.yaml\n\nor inline rules:\nGEOSITE,category-ir,DIRECT'}
|
||||
onChange={(e) => updateSetting({ subClashRules: e.target.value })}
|
||||
/>
|
||||
</SettingListItem>
|
||||
@@ -230,8 +236,8 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subIncyEnableRouting')} description={t('pages.settings.subIncyEnableRoutingDesc')}>
|
||||
<Switch checked={allSetting.subIncyEnableRouting} onChange={(v) => updateSetting({ subIncyEnableRouting: v })} />
|
||||
</SettingListItem>
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subIncyRoutingRules')} description={t('pages.settings.subIncyRoutingRulesDesc')}>
|
||||
<Input.TextArea value={allSetting.subIncyRoutingRules} placeholder="incy://routing/onadd/..."
|
||||
<SettingListItem paddings="small" title={t('pages.settings.subIncyRoutingRules')} badge={remoteSourceBadge(allSetting.subIncyRoutingRules)} description={t('pages.settings.subIncyRoutingRulesDesc')}>
|
||||
<Input.TextArea value={allSetting.subIncyRoutingRules} placeholder="incy://routing/onadd/... or https://.../DEFAULT.JSON"
|
||||
onChange={(e) => updateSetting({ subIncyRoutingRules: e.target.value })} />
|
||||
</SettingListItem>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user