Skip to content

Commit 3b069de

Browse files
authored
fix: handle radar rules update failures (#1174)
- Validate remote rules response status and payload - Prevent invalid response from polluting stored rules - Show error toast when manual update fails
1 parent ec2f091 commit 3b069de

6 files changed

Lines changed: 96 additions & 33 deletions

File tree

public/_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@
134134
"updateSuccessful": {
135135
"message": "Update successful"
136136
},
137+
"updateFailed": {
138+
"message": "Update failed"
139+
},
137140
"updateTip": {
138141
"message": "Automatically update every two hours, you can also manually update."
139142
},

public/_locales/zh_CN/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@
134134
"updateSuccessful": {
135135
"message": "更新成功"
136136
},
137+
"updateFailed": {
138+
"message": "更新失败"
139+
},
137140
"updateTip": {
138141
"message": "每两小时自动更新一次,你也可以手动更新"
139142
},

src/background/messages/refreshRules.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,21 @@ const handler = async (
44
_message?: unknown,
55
_sender?: chrome.runtime.MessageSender,
66
) => {
7-
await refreshRules()
8-
return true
7+
try {
8+
await refreshRules()
9+
return {
10+
success: true,
11+
}
12+
} catch (error) {
13+
console.error(error)
14+
return {
15+
success: false,
16+
error:
17+
error instanceof Error
18+
? error.message
19+
: "Unknown error while refreshing rules",
20+
}
21+
}
922
}
1023

1124
export default handler

src/background/rules.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ export const getDisplayedRules = () => getLocalStorage("displayedRules")
3030
export const setDisplayedRules = (displayedRules) =>
3131
setLocalStorage("displayedRules", displayedRules)
3232

33+
const refreshRulesSafely = () => {
34+
refreshRules().catch((error) => {
35+
console.error(error)
36+
})
37+
}
38+
3339
chrome.alarms.onAlarm.addListener((alarm) => {
3440
if (alarm.name === "refreshRulesAlarm") {
35-
refreshRules()
41+
refreshRulesSafely()
3642
}
3743
})
3844

@@ -41,7 +47,7 @@ export async function initSchedule() {
4147
const rules = await getLocalStorage("rules")
4248
if (!rules) {
4349
setTimeout(() => {
44-
refreshRules()
50+
refreshRulesSafely()
4551
}, 60 * 1000)
4652
}
4753

src/lib/rules.ts

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
import _ from "lodash"
2-
32
import MD5 from "md5.js"
3+
44
import { getConfig } from "./config"
55
import { defaultRules } from "./radar-rules"
66
import type { Rules } from "./types"
77
import { getRadarRulesUrl } from "./utils"
88

99
export function parseRules(rules: string, forceJSON?: boolean) {
10-
let incomeRules = rules
11-
if (incomeRules) {
12-
if (typeof rules === "string") {
13-
try {
14-
incomeRules = JSON.parse(rules)
15-
} catch (error) {}
10+
let incomeRules: unknown = rules
11+
12+
if (typeof rules === "string") {
13+
try {
14+
incomeRules = JSON.parse(rules)
15+
} catch (error) {
16+
incomeRules = forceJSON ? {} : rules
1617
}
1718
}
18-
return _.mergeWith(defaultRules, incomeRules, (objValue, srcValue) => {
19+
20+
if (!_.isPlainObject(incomeRules)) {
21+
incomeRules = {}
22+
}
23+
24+
return _.mergeWith({}, defaultRules, incomeRules, (objValue, srcValue) => {
1925
if (_.isFunction(srcValue)) {
2026
return srcValue
2127
} else if (_.isFunction(objValue)) {
@@ -37,22 +43,34 @@ export function getRulesCount(rules: Rules) {
3743
return index
3844
}
3945

40-
export function getRemoteRules() {
41-
return new Promise<string>(async (resolve, reject) => {
42-
const config = await getConfig()
43-
try {
44-
let url = getRadarRulesUrl(config.rsshubDomain)
45-
46-
if (config.rsshubAccessControl.accessKey) {
47-
const path = new URL(url).pathname
48-
const code = new MD5().update(path + config.rsshubAccessControl.accessKey).digest("hex")
49-
url = `${url}?code=${code}`
50-
}
46+
export async function getRemoteRules() {
47+
const config = await getConfig()
48+
let url = getRadarRulesUrl(config.rsshubDomain)
5149

52-
const res = await fetch(url)
53-
resolve(res.text())
54-
} catch (error) {
55-
reject(error)
56-
}
57-
})
58-
}
50+
if (config.rsshubAccessControl.accessKey) {
51+
const path = new URL(url).pathname
52+
const code = new MD5()
53+
.update(path + config.rsshubAccessControl.accessKey)
54+
.digest("hex")
55+
url = `${url}?code=${code}`
56+
}
57+
58+
const res = await fetch(url)
59+
if (!res.ok) {
60+
throw new Error(`Failed to fetch remote rules: ${res.status}`)
61+
}
62+
63+
const text = await res.text()
64+
let parsed: unknown
65+
try {
66+
parsed = JSON.parse(text)
67+
} catch {
68+
throw new Error("Invalid remote rules response")
69+
}
70+
71+
if (!_.isPlainObject(parsed)) {
72+
throw new Error("Invalid remote rules payload")
73+
}
74+
75+
return text
76+
}

src/options/routes/General.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,30 @@ function General() {
275275
setRulesUpdating(true)
276276
sendToBackground({
277277
name: "refreshRules",
278-
}).then((res) => {
279-
setRulesUpdating(false)
280-
toast.success(chrome.i18n.getMessage("updateSuccessful"))
281278
})
279+
.then((res?: { success?: boolean; error?: string }) => {
280+
if (!res?.success) {
281+
toast.error(
282+
res?.error ||
283+
chrome.i18n.getMessage("updateFailed") ||
284+
"Update failed",
285+
)
286+
return
287+
}
288+
289+
toast.success(chrome.i18n.getMessage("updateSuccessful"))
290+
})
291+
.catch((error: unknown) => {
292+
toast.error(
293+
error instanceof Error
294+
? error.message
295+
: chrome.i18n.getMessage("updateFailed") ||
296+
"Update failed",
297+
)
298+
})
299+
.finally(() => {
300+
setRulesUpdating(false)
301+
})
282302
}}
283303
>
284304
{rulesUpdating && (

0 commit comments

Comments
 (0)