Compare commits

...

3 Commits

Author SHA1 Message Date
Memory
b76757bc19
fix: conn detail & log can't select 2025-11-25 23:46:51 +08:00
Memory
0753e5d138
chore: prepare full geoip.dat 2025-11-25 22:30:04 +08:00
Memory
583ece0a64
feat: support disabling autoupdate 2025-11-25 22:21:14 +08:00
8 changed files with 81 additions and 60 deletions

View File

@ -305,7 +305,7 @@ const resolveGeosite = () =>
const resolveGeoIP = () => const resolveGeoIP = () =>
resolveResource({ resolveResource({
file: 'geoip.dat', file: 'geoip.dat',
downloadURL: `https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat` downloadURL: `https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat`
}) })
const resolveASN = () => const resolveASN = () =>
resolveResource({ resolveResource({

View File

@ -141,6 +141,7 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
override: item.override || [], override: item.override || [],
useProxy: item.useProxy || false, useProxy: item.useProxy || false,
allowFixedInterval: item.allowFixedInterval || false, allowFixedInterval: item.allowFixedInterval || false,
autoUpdate: item.autoUpdate ?? false,
updated: new Date().getTime() updated: new Date().getTime()
} as IProfileItem } as IProfileItem
switch (newItem.type) { switch (newItem.type) {

View File

@ -8,7 +8,7 @@ export async function initProfileUpdater(): Promise<void> {
const currentItem = await getCurrentProfileItem() const currentItem = await getCurrentProfileItem()
for (const item of items.filter((i) => i.id !== current)) { for (const item of items.filter((i) => i.id !== current)) {
if (item.type === 'remote' && item.interval) { if (item.type === 'remote' && item.autoUpdate && item.interval) {
if (typeof item.interval === 'number') { if (typeof item.interval === 'number') {
// 数字间隔使用setInterval // 数字间隔使用setInterval
intervalPool[item.id] = setInterval( intervalPool[item.id] = setInterval(
@ -40,7 +40,7 @@ export async function initProfileUpdater(): Promise<void> {
} }
} }
if (currentItem?.type === 'remote' && currentItem.interval) { if (currentItem?.type === 'remote' && currentItem.autoUpdate && currentItem.interval) {
if (typeof currentItem.interval === 'number') { if (typeof currentItem.interval === 'number') {
intervalPool[currentItem.id] = setInterval( intervalPool[currentItem.id] = setInterval(
async () => { async () => {
@ -82,7 +82,7 @@ export async function initProfileUpdater(): Promise<void> {
} }
export async function addProfileUpdater(item: IProfileItem): Promise<void> { export async function addProfileUpdater(item: IProfileItem): Promise<void> {
if (item.type === 'remote' && item.interval) { if (item.type === 'remote' && item.autoUpdate && item.interval) {
if (intervalPool[item.id]) { if (intervalPool[item.id]) {
if (intervalPool[item.id] instanceof Cron) { if (intervalPool[item.id] instanceof Cron) {
(intervalPool[item.id] as Cron).stop() (intervalPool[item.id] as Cron).stop()

View File

@ -79,6 +79,10 @@
user-select: none; user-select: none;
} }
.select-text {
user-select: text !important;
}
*:focus { *:focus {
outline: none; outline: none;
outline-color: transparent; outline-color: transparent;

View File

@ -100,68 +100,81 @@ const EditInfoModal: React.FC<Props> = (props) => {
}} }}
/> />
</SettingItem> </SettingItem>
<SettingItem title={t('profiles.editInfo.interval')}> <SettingItem title={t('profiles.editInfo.autoUpdate')}>
<div className="flex flex-col gap-2">
<Input
size="sm"
type="text"
className={cn(
inputWidth,
// 不合法
typeof values.interval === 'string' &&
!/^\d+$/.test(values.interval) &&
!isValidCron(values.interval, { seconds: false }) &&
'border-red-500'
)}
value={values.interval?.toString() ?? ''}
onValueChange={(v) => {
// 输入限制
if (/^[\d\s*\-,\/]*$/.test(v)) {
// 纯数字
if (/^\d+$/.test(v)) {
setValues({ ...values, interval: parseInt(v, 10) || 0 });
return;
}
// 非纯数字
try {
setValues({ ...values, interval: v });
} catch (e) {
// ignore
}
}
}}
placeholder={t('profiles.editInfo.intervalPlaceholder')}
/>
{/* 动态提示信息 */}
<div className="text-xs" style={{
color: typeof values.interval === 'string' &&
!/^\d+$/.test(values.interval) &&
!isValidCron(values.interval, { seconds: false })
? '#ef4444'
: '#6b7280'
}}>
{typeof values.interval === 'number' ? (
t('profiles.editInfo.intervalMinutes')
) : /^\d+$/.test(values.interval?.toString() || '') ? (
t('profiles.editInfo.intervalMinutes')
) : isValidCron(values.interval?.toString() || '', { seconds: false }) ? (
t('profiles.editInfo.intervalCron')
) : (
t('profiles.editInfo.intervalHint')
)}
</div>
</div>
</SettingItem>
<SettingItem title={t('profiles.editInfo.fixedInterval')}>
<Switch <Switch
size="sm" size="sm"
isSelected={values.allowFixedInterval ?? false} isSelected={values.autoUpdate ?? false}
onValueChange={(v) => { onValueChange={(v) => {
setValues({ ...values, allowFixedInterval: v }) setValues({ ...values, autoUpdate: v })
}} }}
/> />
</SettingItem> </SettingItem>
{values.autoUpdate && (
<>
<SettingItem title={t('profiles.editInfo.interval')}>
<div className="flex flex-col gap-2">
<Input
size="sm"
type="text"
className={cn(
inputWidth,
// 不合法
typeof values.interval === 'string' &&
!/^\d+$/.test(values.interval) &&
!isValidCron(values.interval, { seconds: false }) &&
'border-red-500'
)}
value={values.interval?.toString() ?? ''}
onValueChange={(v) => {
// 输入限制
if (/^[\d\s*\-,\/]*$/.test(v)) {
// 纯数字
if (/^\d+$/.test(v)) {
setValues({ ...values, interval: parseInt(v, 10) || 0 });
return;
}
// 非纯数字
try {
setValues({ ...values, interval: v });
} catch (e) {
// ignore
}
}
}}
placeholder={t('profiles.editInfo.intervalPlaceholder')}
/>
{/* 动态提示信息 */}
<div className="text-xs" style={{
color: typeof values.interval === 'string' &&
!/^\d+$/.test(values.interval) &&
!isValidCron(values.interval, { seconds: false })
? '#ef4444'
: '#6b7280'
}}>
{typeof values.interval === 'number' ? (
t('profiles.editInfo.intervalMinutes')
) : /^\d+$/.test(values.interval?.toString() || '') ? (
t('profiles.editInfo.intervalMinutes')
) : isValidCron(values.interval?.toString() || '', { seconds: false }) ? (
t('profiles.editInfo.intervalCron')
) : (
t('profiles.editInfo.intervalHint')
)}
</div>
</div>
</SettingItem>
<SettingItem title={t('profiles.editInfo.fixedInterval')}>
<Switch
size="sm"
isSelected={values.allowFixedInterval ?? false}
onValueChange={(v) => {
setValues({ ...values, allowFixedInterval: v })
}}
/>
</SettingItem>
</>
)}
</> </>
)} )}
<SettingItem title={t('profiles.editInfo.override.title')}> <SettingItem title={t('profiles.editInfo.override.title')}>

View File

@ -446,6 +446,7 @@
"profiles.editInfo.intervalCron": "Valid Cron expression", "profiles.editInfo.intervalCron": "Valid Cron expression",
"profiles.editInfo.intervalHint": "Please enter a number or a valid Cron expression (e.g.: 0 * * * *)", "profiles.editInfo.intervalHint": "Please enter a number or a valid Cron expression (e.g.: 0 * * * *)",
"profiles.editInfo.fixedInterval": "Fixed Update Interval", "profiles.editInfo.fixedInterval": "Fixed Update Interval",
"profiles.editInfo.autoUpdate": "Auto Update",
"profiles.editInfo.override.title": "Override", "profiles.editInfo.override.title": "Override",
"profiles.editInfo.override.global": "Global", "profiles.editInfo.override.global": "Global",
"profiles.editInfo.override.noAvailable": "No available overrides", "profiles.editInfo.override.noAvailable": "No available overrides",

View File

@ -451,6 +451,7 @@
"profiles.editInfo.intervalCron": "有效的Cron表达式", "profiles.editInfo.intervalCron": "有效的Cron表达式",
"profiles.editInfo.intervalHint": "请输入数字或合法的Cron表达式0 * * * *", "profiles.editInfo.intervalHint": "请输入数字或合法的Cron表达式0 * * * *",
"profiles.editInfo.fixedInterval": "固定更新间隔", "profiles.editInfo.fixedInterval": "固定更新间隔",
"profiles.editInfo.autoUpdate": "自动更新",
"profiles.editInfo.override.title": "覆写", "profiles.editInfo.override.title": "覆写",
"profiles.editInfo.override.global": "全局", "profiles.editInfo.override.global": "全局",
"profiles.editInfo.override.noAvailable": "没有可用的覆写", "profiles.editInfo.override.noAvailable": "没有可用的覆写",

View File

@ -491,6 +491,7 @@ interface IProfileItem {
extra?: ISubscriptionUserInfo extra?: ISubscriptionUserInfo
substore?: boolean substore?: boolean
allowFixedInterval?: boolean allowFixedInterval?: boolean
autoUpdate?: boolean
} }
interface ISubStoreSub { interface ISubStoreSub {