fix(frontend-admin): SSE/AbortController hygiene + signal-chain through proxy (0.87.9)

Three frontend findings (medium):

1. **`hooks.server.ts` didn't chain client-disconnect into the upstream
   fetch.** Closing an admin tab left the backend's `broadcast::Receiver`
   + spawned tokio task running until next backend restart. Listen for
   `event.request.signal.abort` and forward to the timeout controller.

2. **`admin/analysis/+page.svelte` missing visibility/pagehide/pageshow
   handlers.** Mobile Safari throttles SSE in background tabs; bfcache
   leaves a dead connection on back-nav. Apply the close/open-stream
   pattern from `admin/crawler/+page.svelte`.

3. **`loadCoverage` had no AbortController.** Debounced search
   keystrokes raced. Per-loader controller plumbed through
   `getAnalysisMangaCoverage`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-22 21:52:33 +02:00
parent c0281f7e9b
commit 747bdeda46
6 changed files with 92 additions and 14 deletions

View File

@@ -89,6 +89,22 @@ export const handle: Handle = async ({ event, resolve }) => {
? null
: setTimeout(() => ctrl.abort(), PROXY_TIMEOUT_MS);
// Chain the SvelteKit client's disconnect signal into the upstream
// fetch so closing an admin tab tears down the backend SSE
// subscription immediately. Without this, the backend's
// `broadcast::Receiver` + spawned tokio task leak per closed tab
// until backend restart — every page refresh adds another.
// `AbortSignal.any` would be cleaner but it's Node 20.3+; chain a
// listener so we work on older runtimes (vitest's bundled jsdom
// env included). If the client signal is already aborted (e.g.
// very fast reload), fire ctrl immediately.
if (event.request.signal.aborted) {
ctrl.abort();
} else {
event.request.signal.addEventListener('abort', () => ctrl.abort(), {
once: true
});
}
const init: RequestInit & { duplex?: 'half' } = {
method: event.request.method,
headers,