Skip to content

Commit 8bea5d4

Browse files
committed
feat: Prompt for core restart on active configuration changes
1 parent 9cb6818 commit 8bea5d4

File tree

18 files changed

+232
-344
lines changed

18 files changed

+232
-344
lines changed

frontend/src/App.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ const kernelApiStore = Stores.useKernelApiStore()
2323
const subscribesStore = Stores.useSubscribesStore()
2424
const scheduledTasksStore = Stores.useScheduledTasksStore()
2525
26+
const handleRestartCore = async () => {
27+
try {
28+
await kernelApiStore.restartCore()
29+
} catch (e: any) {
30+
message.error(e.message || e)
31+
}
32+
}
33+
2634
EventsOn('onLaunchApp', async ([arg]: string[]) => {
2735
if (!arg) return
2836
@@ -142,4 +150,14 @@ envStore.setupEnv().then(async () => {
142150
/>
143151

144152
<CommandView v-if="!loading" />
153+
154+
<div v-if="kernelApiStore.needRestart" class="fixed right-32 bottom-32">
155+
<Button
156+
@click="handleRestartCore"
157+
v-tips="'home.overview.restart'"
158+
:loading="kernelApiStore.restarting"
159+
icon="restart"
160+
class="rounded-full w-42 h-42 shadow"
161+
/>
162+
</div>
145163
</template>

frontend/src/api/kernel.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import { useAppSettingsStore, useProfilesStore } from '@/stores'
22
import { Request } from '@/utils/request'
33

4-
import type {
5-
CoreApiConfig,
6-
CoreApiConnections,
7-
CoreApiProviders,
8-
CoreApiProvidersRules,
9-
CoreApiProxies,
10-
} from '@/types/kernel'
4+
import type { CoreApiConfig, CoreApiProxies, CoreApiConnections } from '@/types/kernel'
115

126
export enum Api {
137
Configs = '/configs',
148
Memory = '/memory',
159
Proxies = '/proxies',
16-
Providers = '/providers/proxies',
1710
ProxyDelay = '/proxies/{0}/delay',
1811
Connections = '/connections',
1912
Traffic = '/traffic',
2013
Logs = '/logs',
2114
GEO = '/configs/geo',
22-
ProvidersRules = '/providers/rules',
2315
}
2416

2517
const setupKernelApi = () => {
@@ -51,8 +43,6 @@ export const setConfigs = (body = {}) => request.patch<null>(Api.Configs, body)
5143

5244
export const getProxies = () => request.get<CoreApiProxies>(Api.Proxies)
5345

54-
export const getProviders = () => request.get<CoreApiProviders>(Api.Providers)
55-
5646
export const getConnections = () => request.get<CoreApiConnections>(Api.Connections)
5747

5848
export const deleteConnection = (id: string) => request.delete<null>(Api.Connections + '/' + id)
@@ -71,13 +61,3 @@ export const getProxyDelay = (proxy: string, url: string) => {
7161
}
7262

7363
export const updateGEO = () => request.post<{ message: string } | null>(Api.GEO)
74-
75-
export const getProvidersRules = () => request.get<CoreApiProvidersRules>(Api.ProvidersRules)
76-
77-
export const updateProvidersRules = (ruleset: string) => {
78-
return request.put<null>(Api.ProvidersRules + '/' + ruleset)
79-
}
80-
81-
export const updateProvidersProxies = (provider: string) => {
82-
return request.put<null>(Api.Providers + '/' + provider)
83-
}

frontend/src/stores/kernelApi.ts

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { defineStore } from 'pinia'
22
import { computed, ref, watch } from 'vue'
33

4-
import { getConfigs, setConfigs, getProxies, getProviders, Api } from '@/api/kernel'
4+
import { getConfigs, setConfigs, getProxies, Api } from '@/api/kernel'
55
import { ProcessInfo, KillProcess, ExecBackground, ReadFile, WriteFile, RemoveFile } from '@/bridge'
66
import { CorePidFilePath, CoreStopOutputKeyword, CoreWorkingDirectory } from '@/constant'
77
import { Branch } from '@/enums/app'
8+
import { RuleType } from '@/enums/kernel'
89
import {
910
useAppSettingsStore,
1011
useProfilesStore,
1112
useLogsStore,
1213
useEnvStore,
1314
usePluginsStore,
15+
useSubscribesStore,
16+
useRulesetsStore,
1417
} from '@/stores'
1518
import {
1619
generateConfigFile,
@@ -21,6 +24,7 @@ import {
2124
message,
2225
getKernelRuntimeArgs,
2326
getKernelRuntimeEnv,
27+
eventBus,
2428
} from '@/utils'
2529

2630
import type {
@@ -39,6 +43,8 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
3943
const logsStore = useLogsStore()
4044
const pluginsStore = usePluginsStore()
4145
const profilesStore = useProfilesStore()
46+
const subscribesStore = useSubscribesStore()
47+
const rulesetsStore = useRulesetsStore()
4248
const appSettingsStore = useAppSettingsStore()
4349

4450
/** RESTful API */
@@ -51,18 +57,12 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
5157
mode: '',
5258
tun: {
5359
enable: false,
54-
stack: 'gVisor',
60+
stack: '',
5561
device: '',
5662
},
5763
})
5864

5965
const proxies = ref<Record<string, CoreApiProxy>>({})
60-
const providers = ref<{
61-
[key: string]: {
62-
name: string
63-
proxies: CoreApiProxy[]
64-
}
65-
}>({})
6666

6767
const refreshConfig = async () => {
6868
config.value = await getConfigs()
@@ -74,8 +74,7 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
7474
}
7575

7676
const refreshProviderProxies = async () => {
77-
const [{ providers: a }, { proxies: b }] = await Promise.all([getProviders(), getProxies()])
78-
providers.value = a
77+
const { proxies: b } = await getProxies()
7978
proxies.value = b
8079
}
8180

@@ -207,6 +206,7 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
207206
const starting = ref(false)
208207
const stopping = ref(false)
209208
const restarting = ref(false)
209+
const needRestart = ref(false)
210210
const coreStateLoading = ref(true)
211211
let isCoreStartedByThisInstance = false
212212
let { promise: coreStoppedPromise, resolve: coreStoppedResolver } = Promise.withResolvers()
@@ -255,6 +255,7 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
255255

256256
corePid.value = pid
257257
running.value = true
258+
needRestart.value = false
258259
isCoreStartedByThisInstance = true
259260
coreStoppedPromise = new Promise((r) => (coreStoppedResolver = r))
260261

@@ -361,6 +362,67 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
361362
return undefined
362363
}
363364

365+
eventBus.on('profileChange', ({ id }) => {
366+
if (running.value && id === appSettingsStore.app.kernel.profile) {
367+
needRestart.value = true
368+
}
369+
})
370+
371+
eventBus.on('subscriptionChange', ({ id }) => {
372+
if (running.value && profilesStore.currentProfile) {
373+
const inUse = profilesStore.currentProfile.proxyGroupsConfig.some(
374+
(group) => group.use.includes(id) || group.proxies.some((proxy) => proxy.type === id),
375+
)
376+
if (inUse) {
377+
needRestart.value = true
378+
}
379+
}
380+
})
381+
382+
eventBus.on('subscriptionsChange', () => {
383+
if (running.value && profilesStore.currentProfile) {
384+
const enabledSubs = subscribesStore.subscribes.flatMap((v) => (v.disabled ? [] : v.id))
385+
const inUse = profilesStore.currentProfile.proxyGroupsConfig.some(
386+
(group) =>
387+
group.use.some((sub) => enabledSubs.includes(sub)) ||
388+
group.proxies.some((proxy) => enabledSubs.includes(proxy.type)),
389+
)
390+
if (inUse) {
391+
needRestart.value = true
392+
}
393+
}
394+
})
395+
396+
const collectRulesetIDs = () => {
397+
if (!profilesStore.currentProfile) return []
398+
const l1 = Object.keys(profilesStore.currentProfile.dnsConfig['nameserver-policy']).flatMap(
399+
(v) => (v.startsWith('rule-set:') ? v.slice('rule-set:'.length) : []),
400+
)
401+
const l2 = profilesStore.currentProfile.rulesConfig.flatMap((v) =>
402+
v.type === RuleType.RuleSet ? v.payload : [],
403+
)
404+
return [...l1, ...l2]
405+
}
406+
407+
eventBus.on('rulesetChange', ({ id }) => {
408+
if (running.value && profilesStore.currentProfile) {
409+
const inUse = collectRulesetIDs().includes(id)
410+
if (inUse) {
411+
needRestart.value = true
412+
}
413+
}
414+
})
415+
416+
eventBus.on('rulesetsChange', () => {
417+
if (running.value && profilesStore.currentProfile) {
418+
const enabledRulesets = rulesetsStore.rulesets.flatMap((v) => (v.disabled ? [] : v.id))
419+
const inUse = collectRulesetIDs().some((v) => enabledRulesets.includes(v))
420+
if (inUse) {
421+
needRestart.value = true
422+
}
423+
}
424+
})
425+
364426
const watchSources = computed(() => {
365427
const source = [config.value.mode, config.value.tun.enable]
366428
if (!appSettingsStore.app.addGroupToMenu) return source.join('')
@@ -387,10 +449,10 @@ export const useKernelApiStore = defineStore('kernelApi', () => {
387449
starting,
388450
stopping,
389451
restarting,
452+
needRestart,
390453
coreStateLoading,
391454
config,
392455
proxies,
393-
providers,
394456
refreshConfig,
395457
updateConfig,
396458
refreshProviderProxies,

frontend/src/stores/profiles.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { defineStore } from 'pinia'
2-
import { ref } from 'vue'
2+
import { computed, ref } from 'vue'
33
import { parse } from 'yaml'
44

55
import { ReadFile, WriteFile } from '@/bridge'
66
import { ProfilesFilePath } from '@/constant'
77
import { ProxyGroup, RulesetBehavior, RulesetFormat, RuleType } from '@/enums/kernel'
8-
import { debounce, ignoredError, stringifyNoFolding } from '@/utils'
8+
import { useAppSettingsStore } from '@/stores'
9+
import { debounce, eventBus, ignoredError, stringifyNoFolding } from '@/utils'
910

1011
export type ProfileType = {
1112
id: string
@@ -139,7 +140,10 @@ export type ProfileType = {
139140
}
140141

141142
export const useProfilesStore = defineStore('profiles', () => {
143+
const appSettingsStore = useAppSettingsStore()
144+
142145
const profiles = ref<ProfileType[]>([])
146+
const currentProfile = computed(() => getProfileById(appSettingsStore.app.kernel.profile))
143147

144148
const setupProfiles = async () => {
145149
const data = await ignoredError(ReadFile, ProfilesFilePath)
@@ -188,6 +192,8 @@ export const useProfilesStore = defineStore('profiles', () => {
188192
profiles.value.splice(idx, 0, backup)
189193
throw error
190194
}
195+
196+
eventBus.emit('profileChange', { id })
191197
}
192198

193199
const editProfile = async (id: string, p: ProfileType) => {
@@ -200,12 +206,15 @@ export const useProfilesStore = defineStore('profiles', () => {
200206
profiles.value.splice(idx, 1, backup)
201207
throw error
202208
}
209+
210+
eventBus.emit('profileChange', { id })
203211
}
204212

205213
const getProfileById = (id: string) => profiles.value.find((v) => v.id === id)
206214

207215
return {
208216
profiles,
217+
currentProfile,
209218
setupProfiles,
210219
saveProfiles,
211220
addProfile,

frontend/src/stores/rulesets.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
omitArray,
1313
stringifyNoFolding,
1414
asyncPool,
15+
eventBus,
1516
} from '@/utils'
1617

1718
export interface RuleSet {
@@ -75,6 +76,8 @@ export const useRulesetsStore = defineStore('rulesets', () => {
7576
rulesets.value.splice(idx, 0, backup)
7677
throw error
7778
}
79+
80+
eventBus.emit('rulesetChange', { id })
7881
}
7982

8083
const editRuleset = async (id: string, r: RuleSet) => {
@@ -87,6 +90,8 @@ export const useRulesetsStore = defineStore('rulesets', () => {
8790
rulesets.value.splice(idx, 1, backup)
8891
throw error
8992
}
93+
94+
eventBus.emit('rulesetChange', { id })
9095
}
9196

9297
const _doUpdateRuleset = async (r: RuleSet) => {
@@ -100,11 +105,10 @@ export const useRulesetsStore = defineStore('rulesets', () => {
100105
const { body: b } = await HttpGet(r.url)
101106
body = b
102107
} else if (r.type === 'Manual') {
103-
isExist = await FileExists(r.path)
104-
if (isExist) {
105-
body = await ReadFile(r.path)
106-
} else {
108+
body = await ReadFile(r.path).catch(() => '')
109+
if (!body) {
107110
body = stringifyNoFolding(EmptyRuleSet)
111+
isExist = false
108112
}
109113
}
110114

@@ -144,10 +148,13 @@ export const useRulesetsStore = defineStore('rulesets', () => {
144148
r.updating = true
145149
await _doUpdateRuleset(r)
146150
await saveRulesets()
147-
return `Ruleset [${r.name}] updated successfully.`
148151
} finally {
149152
r.updating = false
150153
}
154+
155+
eventBus.emit('rulesetChange', { id })
156+
157+
return `Ruleset [${r.name}] updated successfully.`
151158
}
152159

153160
const updateRulesets = async () => {
@@ -170,6 +177,8 @@ export const useRulesetsStore = defineStore('rulesets', () => {
170177
)
171178

172179
if (needSave) saveRulesets()
180+
181+
eventBus.emit('rulesetsChange', undefined)
173182
}
174183

175184
const rulesetHubLoading = ref(false)

0 commit comments

Comments
 (0)