Skip to content

Commit 658b6ab

Browse files
committed
feat: add context property to PluginType and update related components for profiles and subscriptions
1 parent 10c9e55 commit 658b6ab

File tree

7 files changed

+172
-41
lines changed

7 files changed

+172
-41
lines changed

frontend/src/lang/locale/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ export default {
481481
path: 'Save Path',
482482
type: 'Type',
483483
menus: 'Menus',
484+
context: 'Context',
484485
configuration: ' Configuration',
485486
menuKey: 'Menu Title',
486487
menuValue: 'Trigger function name',

frontend/src/lang/locale/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ export default {
479479
path: '保存路径',
480480
type: '类型',
481481
menus: '菜单',
482+
context: '上下文',
482483
configuration: '配置',
483484
menuKey: '菜单名称',
484485
menuValue: '触发方法名',

frontend/src/stores/plugins.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,19 @@ export type PluginType = {
3737
path: string
3838
triggers: PluginTrigger[]
3939
menus: Record<string, string>
40+
context: {
41+
profiles: Recordable
42+
subscriptions: Recordable
43+
rulesets: Recordable
44+
plugins: Recordable
45+
scheduledtasks: Recordable
46+
}
4047
configuration: PluginConfiguration[]
4148
disabled: boolean
4249
install: boolean
4350
installed: boolean
4451
status: number // 0: Normal 1: Running 2: Stopped
4552
// Not Config
46-
key?: string
4753
updating?: boolean
4854
loading?: boolean
4955
running?: boolean
@@ -94,14 +100,24 @@ export const usePluginsStore = defineStore('plugins', () => {
94100
data && (plugins.value = parse(data))
95101

96102
for (let i = 0; i < plugins.value.length; i++) {
97-
const { id, triggers, path } = plugins.value[i]
103+
const { id, triggers, path, context } = plugins.value[i]
98104
const code = await ignoredError(Readfile, path)
99105
if (code) {
100106
PluginsCache[id] = { plugin: plugins.value[i], code }
101107
triggers.forEach((trigger) => {
102108
PluginsTriggerMap[trigger].observers.push(id)
103109
})
104110
}
111+
112+
if (!context) {
113+
plugins.value[i].context = {
114+
profiles: {},
115+
subscriptions: {},
116+
rulesets: {},
117+
plugins: {},
118+
scheduledtasks: {},
119+
}
120+
}
105121
}
106122
}
107123

@@ -148,7 +164,7 @@ export const usePluginsStore = defineStore('plugins', () => {
148164
}
149165

150166
const savePlugins = debounce(async () => {
151-
const p = omitArray(plugins.value, ['key', 'updating', 'loading', 'running'])
167+
const p = omitArray(plugins.value, ['updating', 'loading', 'running'])
152168
await Writefile(PluginsFilePath, stringify(p))
153169
}, 100)
154170

frontend/src/views/PluginsView/components/PluginForm.vue

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ const plugin = ref<PluginType>({
3030
path: `data/plugins/plugin-${pluginID}.js`,
3131
triggers: [PluginTrigger.OnManual],
3232
menus: {},
33+
context: {
34+
profiles: {},
35+
subscriptions: {},
36+
rulesets: {},
37+
plugins: {},
38+
scheduledtasks: {},
39+
},
3340
configuration: [],
3441
disabled: false,
3542
install: false,
@@ -47,8 +54,6 @@ const handleSubmit = async () => {
4754
loading.value = true
4855
try {
4956
if (props.isUpdate) {
50-
// Refresh the key to re-render the view
51-
plugin.value.key = sampleID()
5257
await pluginsStore.editPlugin(props.id, plugin.value)
5358
} else {
5459
await pluginsStore.addPlugin(plugin.value)
@@ -188,6 +193,26 @@ if (props.isUpdate) {
188193
:placeholder="[t('plugin.menuKey'), t('plugin.menuValue')]"
189194
/>
190195
</div>
196+
<div
197+
:class="{ 'flex-start': Object.keys(plugin.context.profiles).length !== 0 }"
198+
class="form-item"
199+
>
200+
<div class="name">{{ t('plugin.context') }} - {{ t('router.profiles') }}</div>
201+
<KeyValueEditor
202+
v-model="plugin.context.profiles"
203+
:placeholder="[t('plugin.menuKey'), t('plugin.menuValue')]"
204+
/>
205+
</div>
206+
<div
207+
:class="{ 'flex-start': Object.keys(plugin.context.subscriptions).length !== 0 }"
208+
class="form-item"
209+
>
210+
<div class="name">{{ t('plugin.context') }} - {{ t('router.subscriptions') }}</div>
211+
<KeyValueEditor
212+
v-model="plugin.context.subscriptions"
213+
:placeholder="[t('plugin.menuKey'), t('plugin.menuValue')]"
214+
/>
215+
</div>
191216
<Divider>{{ t('plugin.configuration') }}</Divider>
192217
<div v-draggable="[plugin.configuration, { ...DraggableOptions, handle: '.drag' }]">
193218
<template v-for="(conf, index) in plugin.configuration" :key="conf.id">

frontend/src/views/PluginsView/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ const onSortUpdate = debounce(pluginsStore.savePlugins, 1000)
226226
>
227227
<Card
228228
v-for="p in pluginsStore.plugins"
229-
:key="p.id + p.key"
229+
:key="p.id"
230230
:title="p.name"
231231
:disabled="p.disabled"
232232
v-menu="generateMenus(p)"

frontend/src/views/ProfilesView/index.vue

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
useAppSettingsStore,
1313
useKernelApiStore,
1414
useSubscribesStore,
15+
usePluginsStore,
1516
} from '@/stores'
1617
1718
import ProfileForm from './components/ProfileForm.vue'
@@ -28,8 +29,29 @@ const profilesStore = useProfilesStore()
2829
const subscribesStore = useSubscribesStore()
2930
const appSettingsStore = useAppSettingsStore()
3031
const kernelApiStore = useKernelApiStore()
32+
const pluginsStore = usePluginsStore()
3133
32-
const secondaryMenus: Menu[] = [
34+
const menuList: Menu[] = [
35+
...[
36+
'profile.step.name',
37+
'profile.step.general',
38+
'profile.step.inbounds',
39+
'profile.step.outbounds',
40+
'profile.step.route',
41+
'profile.step.dns',
42+
'profile.step.mixin-script',
43+
].map((v, i) => {
44+
return {
45+
label: v,
46+
handler: (id: string) => {
47+
const p = profilesStore.getProfileById(id)
48+
p && handleEditProfile(p, i)
49+
},
50+
}
51+
}),
52+
]
53+
54+
const secondaryMenusList: Menu[] = [
3355
{
3456
label: 'profiles.start',
3557
handler: async (id: string) => {
@@ -81,33 +103,58 @@ const secondaryMenus: Menu[] = [
81103
},
82104
]
83105
84-
const menus: Menu[] = [
85-
...[
86-
'profile.step.name',
87-
'profile.step.general',
88-
'profile.step.inbounds',
89-
'profile.step.outbounds',
90-
'profile.step.route',
91-
'profile.step.dns',
92-
'profile.step.mixin-script',
93-
].map((v, i) => {
94-
return {
95-
label: v,
96-
handler: (id: string) => {
97-
const p = profilesStore.getProfileById(id)
98-
p && handleEditProfile(p, i)
106+
const generateMenus = (profile: IProfile) => {
107+
const moreMenus: Menu[] = secondaryMenusList.map((v) => ({
108+
...v,
109+
handler: () => v.handler?.(profile.id),
110+
}))
111+
const builtInMenus: Menu[] = [
112+
...menuList.map((v) => ({ ...v, handler: () => v.handler?.(profile.id) })),
113+
{
114+
label: '',
115+
separator: true,
116+
},
117+
{
118+
label: 'common.more',
119+
children: moreMenus,
120+
},
121+
]
122+
123+
const contextMenus = pluginsStore.plugins.filter(
124+
(plugin) => Object.keys(plugin.context.profiles).length !== 0,
125+
)
126+
127+
if (contextMenus.length !== 0) {
128+
moreMenus.push(
129+
{
130+
label: '',
131+
separator: true,
99132
},
100-
}
101-
}),
102-
{
103-
label: '',
104-
separator: true,
105-
},
106-
{
107-
label: 'common.more',
108-
children: secondaryMenus,
109-
},
110-
]
133+
...contextMenus.reduce((prev, plugin) => {
134+
const menus = Object.entries(plugin.context.profiles)
135+
return prev.concat(
136+
menus.map(([title, fn]) => {
137+
return {
138+
label: title,
139+
handler: async () => {
140+
try {
141+
plugin.running = true
142+
await pluginsStore.manualTrigger(plugin.id, fn as any, profile)
143+
} catch (error: any) {
144+
message.error(error)
145+
} finally {
146+
plugin.running = false
147+
}
148+
},
149+
}
150+
}),
151+
)
152+
}, [] as Menu[]),
153+
)
154+
}
155+
156+
return builtInMenus
157+
}
111158
112159
const handleAddProfile = async () => {
113160
isUpdate.value = false
@@ -193,13 +240,7 @@ const onSortUpdate = debounce(profilesStore.saveProfiles, 1000)
193240
:title="p.name"
194241
:selected="appSettingsStore.app.kernel.profile === p.id"
195242
@dblclick="handleUseProfile(p)"
196-
v-menu="
197-
menus.map((v) => ({
198-
...v,
199-
handler: () => v.handler?.(p.id),
200-
children: v.children?.map((vv) => ({ ...vv, handler: () => vv.handler?.(p.id) })),
201-
}))
202-
"
243+
v-menu="generateMenus(p)"
203244
class="item"
204245
>
205246
<template #title-prefix>

frontend/src/views/SubscribesView/index.vue

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
useSubscribesStore,
1414
useAppSettingsStore,
1515
useKernelApiStore,
16+
usePluginsStore,
1617
} from '@/stores'
1718
1819
import ProxiesView from './components/ProxiesView.vue'
@@ -54,6 +55,52 @@ const { message } = useMessage()
5455
const subscribeStore = useSubscribesStore()
5556
const appSettingsStore = useAppSettingsStore()
5657
const kernelApiStore = useKernelApiStore()
58+
const pluginsStore = usePluginsStore()
59+
60+
const generateMenus = (subscription: SubscribeType) => {
61+
const builtInMenus: Menu[] = menuList.map((v) => ({
62+
...v,
63+
handler: () => v.handler?.(subscription.id),
64+
}))
65+
66+
const contextMenus = pluginsStore.plugins.filter(
67+
(plugin) => Object.keys(plugin.context.subscriptions).length !== 0,
68+
)
69+
70+
if (contextMenus.length !== 0) {
71+
builtInMenus.push(
72+
{
73+
label: '',
74+
separator: true,
75+
},
76+
{
77+
label: 'common.more',
78+
children: contextMenus.reduce((prev, plugin) => {
79+
const menus = Object.entries(plugin.context.subscriptions)
80+
return prev.concat(
81+
menus.map(([title, fn]) => {
82+
return {
83+
label: title,
84+
handler: async () => {
85+
try {
86+
plugin.running = true
87+
await pluginsStore.manualTrigger(plugin.id, fn as any, subscription)
88+
} catch (error: any) {
89+
message.error(error)
90+
} finally {
91+
plugin.running = false
92+
}
93+
},
94+
}
95+
}),
96+
)
97+
}, [] as Menu[]),
98+
},
99+
)
100+
}
101+
102+
return builtInMenus
103+
}
57104
58105
const handleAddSub = async () => {
59106
subFormIsUpdate.value = false
@@ -201,7 +248,7 @@ const onSortUpdate = debounce(subscribeStore.saveSubscribes, 1000)
201248
:key="s.id"
202249
:title="s.name"
203250
:disabled="s.disabled"
204-
v-menu="menuList.map((v) => ({ ...v, handler: () => v.handler?.(s.id) }))"
251+
v-menu="generateMenus(s)"
205252
class="item"
206253
>
207254
<template #title-prefix>

0 commit comments

Comments
 (0)