aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-06-02 00:09:50 +0000
committerFuwn <[email protected]>2026-06-02 00:09:50 +0000
commitb6da8e8474b7a1d0b951b25f4936f039197ff84b (patch)
tree7a495d3a5d2098fced9be68818e755ed6d757108 /src
parentfix(security): sanitize third-party RSS HTML before {@html} (diff)
downloaddue.moe-b6da8e8474b7a1d0b951b25f4936f039197ff84b.tar.xz
due.moe-b6da8e8474b7a1d0b951b25f4936f039197ff84b.zip
fix(updates): isolate feed failures so one feed can't break the page
onMount fetched the novel feed then the manga feed, each via `(await fetch(...)).json()` with no error handling. When a feed endpoint failed (e.g. all-novels, when wlnupdates returns HTML), `.json()` threw and aborted onMount before the other feed loaded, leaving the whole page stuck on skeletons. Load each feed through a fetchJson helper that returns null on any failure (non-ok, network, or parse error) and set the feed to null — the existing "Failed to load feed" state — when the response is missing or malformed. A failing feed now degrades gracefully while the other still renders.
Diffstat (limited to 'src')
-rw-r--r--src/routes/updates/+page.svelte19
1 files changed, 15 insertions, 4 deletions
diff --git a/src/routes/updates/+page.svelte b/src/routes/updates/+page.svelte
index 0534d0bb..bbcb087d 100644
--- a/src/routes/updates/+page.svelte
+++ b/src/routes/updates/+page.svelte
@@ -23,6 +23,7 @@ let novelFeed:
}[];
};
}
+ | null
| undefined;
let startTime: number;
let mangaEndTime: number;
@@ -32,12 +33,22 @@ let directLink = browser
: false;
let removeHeightObserver: (() => void) | undefined;
+const fetchJson = async (path: string) => {
+ try {
+ const response = await fetch(root(path));
+
+ return response.ok ? await response.json() : null;
+ } catch {
+ return null;
+ }
+};
+
onMount(async () => {
removeHeightObserver = createHeightObserver(false);
startTime = performance.now();
- const allNovels = await (await fetch(root("/api/updates/all-novels"))).json();
+ const allNovels = await fetchJson("/api/updates/all-novels");
if (allNovels?.data?.items)
for (const item of allNovels.data.items) {
@@ -45,11 +56,11 @@ onMount(async () => {
if (item.series) item.series.name = sanitizeFeedHtml(item.series.name);
}
- novelFeed = allNovels;
+ novelFeed = allNovels?.data?.items ? allNovels : null;
novelEndTime = performance.now() - startTime;
startTime = performance.now();
- const mangaFeed = await (await fetch(root("/api/updates/manga"))).json();
+ const mangaFeed = await fetchJson("/api/updates/manga");
if (mangaFeed?.items)
for (const item of mangaFeed.items) {
@@ -57,7 +68,7 @@ onMount(async () => {
item.content = sanitizeFeedHtml(item.content);
}
- feed = mangaFeed;
+ feed = mangaFeed?.items ? mangaFeed : null;
mangaEndTime = performance.now() - startTime;
});