fix: cover proxy routing, session filtering, library tab deep-link, profile UX
Some checks failed
Release / Test backend (push) Successful in 1m3s
Release / Test UI (push) Successful in 58s
Release / Build and push images (push) Successful in 5m55s
Release / Deploy to prod (push) Failing after 48s
Release / Deploy to homelab (push) Successful in 21s
Release / Gitea Release (push) Successful in 29s

- Catalogue/cover: rewrite raw scraped cover URLs to /api/cover/{domain}/{slug}
  in handleCatalogue so all covers route through the backend proxy; fix broken
  cdn.novelfire.net fallback in handleGetCover to read stored URL from PocketBase
- Catalogue/profile: add Svelte 5 onerror handlers on cover <img> tags to show
  letter-initial placeholder when image fails to load
- Library page: read ?status URL param to initialise activeShelf tab on load so
  /books?status=reading correctly pre-selects the Reading tab
- Sessions: filter bot/tool user-agents (curl, python, wget, etc.) and debug-IP
  sessions from listUserSessions display; also purge them in pruneStaleUserSessions
- Profile: show email under username, quick stats chips (streak/chapters/completed)
  in header, reading count on Library row, dedicated Sign out row, history covers
  routed through /api/cover proxy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Admin
2026-04-17 13:32:48 +05:00
parent 1f987be75a
commit 8c47aa3a11
5 changed files with 145 additions and 19 deletions

View File

@@ -1431,10 +1431,34 @@ export async function isSessionRevoked(authSessionId: string): Promise<boolean>
}
/**
* List all active sessions for a user.
* Returns true for user-agents that are clearly automated tools (curl, scrapers,
* debug logins, etc.) that should not appear in the user-facing sessions list.
* These sessions still exist in the DB so auth checks continue to work.
*/
function isBotUserAgent(ua: string): boolean {
if (!ua) return false;
const lower = ua.toLowerCase();
return (
lower.startsWith('curl/') ||
lower.startsWith('python') ||
lower.startsWith('wget/') ||
lower.startsWith('go-http-client') ||
lower.startsWith('axios/') ||
lower.startsWith('node-fetch') ||
lower.startsWith('undici') ||
lower.startsWith('okhttp') ||
lower.startsWith('java/')
);
}
/**
* List all active sessions for a user, excluding non-browser/tool sessions
* (curl, debug-login artifacts, scrapers, etc.) from the displayed list.
* The records still exist in the DB so auth validity checks are unaffected.
*/
export async function listUserSessions(userId: string): Promise<UserSession[]> {
return listAll<UserSession>('user_sessions', `user_id="${userId}"`, '-last_seen');
const all = await listAll<UserSession>('user_sessions', `user_id="${userId}"`, '-last_seen');
return all.filter((s) => !isBotUserAgent(s.user_agent) && s.ip !== 'debug');
}
/**
@@ -1453,9 +1477,11 @@ async function pruneStaleUserSessions(
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
const toDelete = new Set<string>();
// Mark stale sessions
// Mark stale sessions and debug/tool sessions for deletion
for (const s of all) {
if (s.last_seen < cutoff) toDelete.add(s.id);
if (s.last_seen < cutoff || s.ip === 'debug' || isBotUserAgent(s.user_agent)) {
toDelete.add(s.id);
}
}
// Mark excess sessions beyond the cap (oldest first — list is sorted -last_seen)