Skip to content

Commit f54e9f6

Browse files
committed
feat: enhance scheduled tasks functionality with new update options and improved logging
1 parent 6d5e278 commit f54e9f6

File tree

12 files changed

+119
-33
lines changed

12 files changed

+119
-33
lines changed

frontend/src/constant/app.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ export const ScheduledTaskOptions = [
103103
{ label: 'scheduledtask.update::plugin', value: ScheduledTasksType.UpdatePlugin },
104104
{ label: 'scheduledtask.run::plugin', value: ScheduledTasksType.RunPlugin },
105105
{ label: 'scheduledtask.run::script', value: ScheduledTasksType.RunScript },
106+
{
107+
label: 'scheduledtask.update::all::subscription',
108+
value: ScheduledTasksType.UpdateAllSubscription,
109+
},
110+
{ label: 'scheduledtask.update::all::ruleset', value: ScheduledTasksType.UpdateAllRuleset },
111+
{ label: 'scheduledtask.update::all::plugin', value: ScheduledTasksType.UpdateAllPlugin },
106112
]
107113

108114
export const DefaultSubscribeScript = `const onSubscribe = async (proxies, subscription) => {\n return { proxies, subscription }\n}`

frontend/src/enums/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export enum ScheduledTasksType {
4949
UpdateSubscription = 'update::subscription',
5050
UpdateRuleset = 'update::ruleset',
5151
UpdatePlugin = 'update::plugin',
52+
UpdateAllSubscription = 'update::all::subscription',
53+
UpdateAllRuleset = 'update::all::ruleset',
54+
UpdateAllPlugin = 'update::all::plugin',
5255
RunPlugin = 'run::plugin',
5356
RunScript = 'run::script',
5457
}

frontend/src/lang/locale/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,9 @@ export default {
535535
'update::subscription': 'update::subscription',
536536
'update::ruleset': 'update::ruleset',
537537
'update::plugin': 'update::plugin',
538+
'update::all::subscription': 'update::all::subscription',
539+
'update::all::ruleset': 'update::all::ruleset',
540+
'update::all::plugin': 'update::all::plugin',
538541
'run::plugin': 'run::plugin',
539542
'run::script': 'run::script',
540543
},

frontend/src/lang/locale/zh.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,9 @@ export default {
534534
'update::subscription': '更新订阅',
535535
'update::ruleset': '更新规则集',
536536
'update::plugin': '更新插件',
537+
'update::all::subscription': '更新所有订阅',
538+
'update::all::ruleset': '更新所有规则集',
539+
'update::all::plugin': '更新所有插件',
537540
'run::plugin': '运行插件',
538541
'run::script': '运行脚本',
539542
},

frontend/src/stores/logs.ts

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

4-
type TaskLogType = {
4+
interface TaskLogRecord<T = any> {
55
name: string
66
startTime: number
77
endTime: number
8-
result: string[]
8+
result: T
99
}
1010

1111
export const useLogsStore = defineStore('logs', () => {
1212
const kernelLogs = ref<string[]>([])
13-
const scheduledtasksLogs = ref<TaskLogType[]>([])
13+
const scheduledtasksLogs = ref<TaskLogRecord[]>([])
1414

1515
const recordKernelLog = (msg: string) => {
1616
kernelLogs.value.unshift(msg)
1717
}
1818

19-
const recordScheduledTasksLog = (log: TaskLogType) => scheduledtasksLogs.value.unshift(log)
19+
const recordScheduledTasksLog = (log: TaskLogRecord) => scheduledtasksLogs.value.unshift(log)
2020

2121
const isTasksLogEmpty = computed(() => scheduledtasksLogs.value.length === 0)
2222

frontend/src/stores/plugins.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,22 +264,30 @@ export const usePluginsStore = defineStore('plugins', () => {
264264
let needSave = false
265265

266266
const update = async (plugin: Plugin) => {
267+
const result = { ok: true, id: plugin.id, name: plugin.name, result: '' }
267268
try {
268269
plugin.updating = true
269270
await _doUpdatePlugin(plugin)
270271
needSave = true
272+
result.result = `Plugin [${plugin.name}] updated successfully.`
273+
} catch (error: any) {
274+
result.ok = false
275+
result.result = `Failed to update plugin [${plugin.name}]. Reason: ${error.message || error}`
271276
} finally {
272277
plugin.updating = false
273278
}
279+
return result
274280
}
275281

276-
await asyncPool(
282+
const result = await asyncPool(
277283
5,
278284
plugins.value.filter((v) => !v.disabled),
279285
update,
280286
)
281287

282288
if (needSave) await savePlugins()
289+
290+
return result.flatMap((v) => (v.ok && v.value) || [])
283291
}
284292

285293
const pluginHubLoading = ref(false)

frontend/src/stores/rulesets.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,22 @@ export const useRulesetsStore = defineStore('rulesets', () => {
160160
let needSave = false
161161

162162
const update = async (r: RuleSet) => {
163+
const result = { ok: true, id: r.id, name: r.name, result: '' }
163164
try {
164165
r.updating = true
165166
await _doUpdateRuleset(r)
166167
needSave = true
168+
result.result = `Rule-Set [${r.name}] updated successfully.`
169+
} catch (error: any) {
170+
result.ok = false
171+
result.result = `Failed to update rule-set [${r.name}]. Reason: ${error.message || error}`
167172
} finally {
168173
r.updating = false
169174
}
175+
return result
170176
}
171177

172-
await asyncPool(
178+
const result = await asyncPool(
173179
5,
174180
rulesets.value.filter((v) => !v.disabled),
175181
update,
@@ -178,6 +184,8 @@ export const useRulesetsStore = defineStore('rulesets', () => {
178184
if (needSave) await saveRulesets()
179185

180186
eventBus.emit('rulesetsChange', undefined)
187+
188+
return result.flatMap((v) => (v.ok && v.value) || [])
181189
}
182190

183191
const rulesetHubLoading = ref(false)

frontend/src/stores/scheduledtasks.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ref } from 'vue'
44
import { parse } from 'yaml'
55

66
import { ReadFile, WriteFile, Notify } from '@/bridge'
7-
import { ScheduledTasksFilePath } from '@/constant'
7+
import { ScheduledTasksFilePath } from '@/constant/app'
88
import { ScheduledTasksType, PluginTriggerEvent } from '@/enums/app'
99
import { useSubscribesStore, useRulesetsStore, usePluginsStore, useLogsStore } from '@/stores'
1010
import { ignoredError, stringifyNoFolding } from '@/utils'
@@ -33,30 +33,41 @@ export const useScheduledTasksStore = defineStore('scheduledtasks', () => {
3333
const logsStore = useLogsStore()
3434

3535
task.lastTime = Date.now()
36-
editScheduledTask(id, task)
3736

3837
const startTime = Date.now()
3938
const result = await getTaskFn(task)()
4039

41-
task.notification && Notify(task.name, result.join('\n'))
40+
if (task.notification) {
41+
const successes = result.filter((v) => v.ok).length
42+
const failures = result.length - successes
43+
const details = result.flatMap((v) => v.result).join('\n')
44+
const content = `Successes: ${successes}; Failures: ${failures}. \n\n${details}`
45+
Notify(task.name, content)
46+
}
4247

4348
logsStore.recordScheduledTasksLog({
4449
name: task.name,
4550
startTime,
4651
endTime: Date.now(),
47-
result,
52+
result: result,
4853
})
54+
55+
await editScheduledTask(id, task)
4956
}
5057

51-
const withOutput = (list: string[], fn: (id: string) => Promise<string>) => {
58+
const withOutput = <T>(list: string[], fn: (id: string) => Promise<T>) => {
5259
return async () => {
53-
const output: string[] = []
60+
const output: { ok: boolean; result: T }[] = []
5461
for (const id of list) {
5562
try {
56-
const res = await fn(id)
57-
output.push(res)
63+
const result = await fn(id)
64+
if (Array.isArray(result)) {
65+
output.push(...result)
66+
} else {
67+
output.push({ ok: true, result })
68+
}
5869
} catch (error: any) {
59-
output.push(error.message || error)
70+
output.push({ ok: false, result: error.message || error })
6071
}
6172
}
6273
return output
@@ -77,6 +88,18 @@ export const useScheduledTasksStore = defineStore('scheduledtasks', () => {
7788
const pluginsStores = usePluginsStore()
7889
return withOutput(task.plugins, pluginsStores.updatePlugin)
7990
}
91+
case ScheduledTasksType.UpdateAllSubscription: {
92+
const subscribesStore = useSubscribesStore()
93+
return withOutput(['0'], () => subscribesStore.updateSubscribes())
94+
}
95+
case ScheduledTasksType.UpdateAllRuleset: {
96+
const rulesetsStore = useRulesetsStore()
97+
return withOutput(['1'], () => rulesetsStore.updateRulesets())
98+
}
99+
case ScheduledTasksType.UpdateAllPlugin: {
100+
const pluginsStores = usePluginsStore()
101+
return withOutput(['2'], () => pluginsStores.updatePlugins())
102+
}
80103
case ScheduledTasksType.RunPlugin: {
81104
const pluginsStores = usePluginsStore()
82105
return withOutput(task.plugins, async (id: string) =>

frontend/src/stores/subscribes.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ export const useSubscribesStore = defineStore('subscribes', () => {
222222
s.updating = true
223223
await _doUpdateSub(s)
224224
await saveSubscribes()
225+
} catch (error: any) {
226+
throw `Failed to update subscription [${s.name}]. Reason: ${error.message || error}`
225227
} finally {
226228
s.updating = false
227229
}
@@ -235,16 +237,22 @@ export const useSubscribesStore = defineStore('subscribes', () => {
235237
let needSave = false
236238

237239
const update = async (s: Subscription) => {
240+
const result = { ok: true, id: s.id, name: s.name, result: '' }
238241
try {
239242
s.updating = true
240243
await _doUpdateSub(s)
241244
needSave = true
245+
result.result = `Subscription [${s.name}] updated successfully.`
246+
} catch (error: any) {
247+
result.ok = false
248+
result.result = `Failed to update subscription [${s.name}]. Reason: ${error.message || error}`
242249
} finally {
243250
s.updating = false
244251
}
252+
return result
245253
}
246254

247-
await asyncPool(
255+
const result = await asyncPool(
248256
5,
249257
subscribes.value.filter((v) => !v.disabled),
250258
update,
@@ -253,6 +261,8 @@ export const useSubscribesStore = defineStore('subscribes', () => {
253261
if (needSave) await saveSubscribes()
254262

255263
eventBus.emit('subscriptionsChange', undefined)
264+
265+
return result.flatMap((v) => (v.ok && v.value) || [])
256266
}
257267

258268
const getSubscribeById = (id: string) => subscribes.value.find((v) => v.id === id)

frontend/src/utils/others.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,20 @@ export const getValue = <T = unknown>(obj: unknown, expr: string): T | undefined
8383
}, obj) as T
8484
}
8585

86-
type IteratorFn<T> = (item: T, array: T[]) => Promise<any>
86+
type IteratorFn<T, K> = (item: T, array: T[]) => Promise<K>
8787
type PoolController = { pause: () => void; resume: () => void; cancel: () => void }
8888
interface RunPoolOptions {
8989
shouldPause?: () => Promise<void>
9090
shouldCancel?: () => boolean
9191
}
9292

93-
async function runPool<T>(
93+
async function runPool<T, K>(
9494
poolLimit: number,
9595
array: T[],
96-
iteratorFn: IteratorFn<T>,
96+
iteratorFn: IteratorFn<T, K>,
9797
options: RunPoolOptions = {},
9898
) {
99-
const results: Promise<any>[] = []
99+
const results: Promise<{ ok: true; value: K } | { ok: false; error: Error }>[] = []
100100
const activePromises = new Set<Promise<any>>()
101101
const { shouldPause, shouldCancel } = options
102102

@@ -111,8 +111,8 @@ async function runPool<T>(
111111

112112
const promise = Promise.resolve()
113113
.then(() => iteratorFn(item, array))
114-
.then((value) => ({ ok: true, value }))
115-
.catch((error) => ({ ok: false, error }))
114+
.then<{ ok: true; value: K }>((value) => ({ ok: true, value }))
115+
.catch<{ ok: false; error: Error }>((error) => ({ ok: false, error }))
116116

117117
results.push(promise)
118118

@@ -127,15 +127,22 @@ async function runPool<T>(
127127
}
128128
}
129129

130-
const settled = await Promise.all(results)
131-
return settled.filter((r) => r.ok).map((r) => r.value)
130+
return await Promise.all(results)
132131
}
133132

134-
export const asyncPool = <T>(poolLimit: number, array: T[], iteratorFn: IteratorFn<T>) => {
133+
export const asyncPool = <T, K = any>(
134+
poolLimit: number,
135+
array: T[],
136+
iteratorFn: IteratorFn<T, K>,
137+
) => {
135138
return runPool(poolLimit, array, iteratorFn)
136139
}
137140

138-
export const createAsyncPool = <T>(poolLimit: number, array: T[], iteratorFn: IteratorFn<T>) => {
141+
export const createAsyncPool = <T, K>(
142+
poolLimit: number,
143+
array: T[],
144+
iteratorFn: IteratorFn<T, K>,
145+
) => {
139146
let paused = false
140147
let cancelled = false
141148
let resumeResolve: (() => void) | null = null
@@ -179,6 +186,7 @@ export const getGitHubApiAuthorization = () => {
179186
return appSettings.app.githubApiToken ? `Bearer ${appSettings.app.githubApiToken}` : ''
180187
}
181188

189+
// System ScheduledTask Helper
182190
export const getTaskSchXmlString = async (delay = 30) => {
183191
const { appPath } = useEnvStore().env
184192

@@ -286,11 +294,11 @@ export const stringifyNoFolding = (content: any) => {
286294
return stringify(content, { lineWidth: 0, minContentWidth: 0 })
287295
}
288296

289-
const regexCache = new Map()
297+
const regexCache = new Map<string, RegExp>()
290298

291299
export const buildSmartRegExp = (pattern: string, flags = '') => {
292300
const key = pattern + '::' + flags
293-
if (regexCache.has(key)) return regexCache.get(key)
301+
if (regexCache.has(key)) return regexCache.get(key)!
294302

295303
let r
296304
try {

0 commit comments

Comments
 (0)