From 6cc9867a63fb62e44e12ee4a460baa7c3f72ae31 Mon Sep 17 00:00:00 2001
From: neon <18559658+neonsolstice@users.noreply.github.com>
Date: Tue, 12 May 2026 05:30:04 +0000
Subject: [PATCH] fix: remove KoreaderBookSyncCard, fix missing initial library
load, add mediaSrc CSP, return null from getBookProgress instead of 404
---
.../components/detail/tabs/DetailsTab.vue | 8 --
.../__tests__/useLibraryBooks.spec.ts | 8 +-
.../book/composables/useLibraryBooks.ts | 6 +-
.../components/KoreaderBookSyncCard.vue | 80 -------------------
.../__tests__/useKoreaderBookProgress.spec.ts | 16 +++-
.../composables/useKoreaderBookProgress.ts | 4 -
.../composables/__tests__/useFoliate.spec.ts | 8 +-
.../reader/epub/composables/useFoliate.ts | 2 +
client/src/views/HomeView.vue | 6 +-
server/src/main.ts | 1 +
.../modules/koreader/koreader.controller.ts | 22 +----
11 files changed, 31 insertions(+), 130 deletions(-)
delete mode 100644 client/src/features/koreader/components/KoreaderBookSyncCard.vue
diff --git a/client/src/features/book/components/detail/tabs/DetailsTab.vue b/client/src/features/book/components/detail/tabs/DetailsTab.vue
index 70f48838f..3750859e6 100644
--- a/client/src/features/book/components/detail/tabs/DetailsTab.vue
+++ b/client/src/features/book/components/detail/tabs/DetailsTab.vue
@@ -15,7 +15,6 @@ import { STATUS_OPTIONS, STATUS_ICONS, STATUS_COLORS, useBookStatus } from '@/fe
import BookDownloadButton from '@/features/book/components/BookDownloadButton.vue'
import DiscoverRow from '@/features/book/components/detail/DiscoverRow.vue'
import BookCoverPlaceholder from '@/features/book/components/BookCoverPlaceholder.vue'
-import KoreaderBookSyncCard from '@/features/koreader/components/KoreaderBookSyncCard.vue'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
@@ -1404,13 +1403,6 @@ watch(
-
-
Synopsis
diff --git a/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts b/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts
index e2328a4f5..0c7b19cf4 100644
--- a/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts
+++ b/client/src/features/book/composables/__tests__/useLibraryBooks.spec.ts
@@ -293,10 +293,11 @@ describe('useLibraryBooks', () => {
it('includes filter, collapseSeries, and trimmed q in the request body', async () => {
const { useLibraryBooks } = await import('../useLibraryBooks')
- const libraryId = ref
(7)
+ const libraryId = ref(null)
const collapseEnabled = ref(true)
const q = ref(' needle ')
- const { filter, sort, load } = useLibraryBooks(libraryId, collapseEnabled, q)
+ const { filter, sort } = useLibraryBooks(libraryId, collapseEnabled, q)
+
filter.value = {
type: 'group',
join: 'AND',
@@ -305,7 +306,8 @@ describe('useLibraryBooks', () => {
sort.value = [{ field: 'author', dir: 'desc' }]
mockOkResponse(makePage([], 0))
- await load(true)
+ libraryId.value = 7
+ await new Promise((r) => setTimeout(r, 0))
const [url, init] = fetchMock.mock.calls[0]!
expect(url).toBe('/api/v1/libraries/7/books')
diff --git a/client/src/features/book/composables/useLibraryBooks.ts b/client/src/features/book/composables/useLibraryBooks.ts
index 5019770a1..5299e109c 100644
--- a/client/src/features/book/composables/useLibraryBooks.ts
+++ b/client/src/features/book/composables/useLibraryBooks.ts
@@ -141,12 +141,8 @@ export function useLibraryBooks(libraryId: Ref, collapseEnabled:
pagination.page = 0
}
- watch(libraryId, (newId, oldId) => {
- if (newId !== oldId) load(true)
- })
-
watch(
- sort,
+ [libraryId, sort] as const,
() => {
void load(true)
},
diff --git a/client/src/features/koreader/components/KoreaderBookSyncCard.vue b/client/src/features/koreader/components/KoreaderBookSyncCard.vue
deleted file mode 100644
index 3ce3f393c..000000000
--- a/client/src/features/koreader/components/KoreaderBookSyncCard.vue
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
- KOReader Sync
-
-
-
-
-
- Progress
- {{ formatPercentage(displayProgress.canonicalPercentage) }}
-
-
-
- Chapter
- {{ displayProgress.canonicalChapterTitle }}
-
-
- Source
- {{ displayProgress.canonicalSource === 'koreader' ? 'KOReader' : 'Web Reader' }}
-
-
- Updated
- {{ formatDate(displayProgress.canonicalUpdatedAt) }}
-
-
-
-
-
Devices
-
- {{ device.device }}
- {{ formatPercentage(device.percentage) }}
-
-
-
-
-
diff --git a/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts b/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts
index 8306a8f1f..992528cc1 100644
--- a/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts
+++ b/client/src/features/koreader/composables/__tests__/useKoreaderBookProgress.spec.ts
@@ -80,8 +80,20 @@ describe('useKoreaderBookProgress', () => {
expect(loading.value).toBe(false)
})
- it('fetchBookProgress sets bookProgress to null on 404', async () => {
- apiMock.mockResolvedValueOnce(makeResponse({}, { ok: false, status: 404 }))
+ it('fetchBookProgress sets bookProgress to null when server returns null (no koreader data)', async () => {
+ apiMock.mockResolvedValueOnce(makeResponse(null))
+
+ const { useKoreaderBookProgress } = await import('../useKoreaderBookProgress')
+ const { bookProgress, fetchBookProgress } = useKoreaderBookProgress()
+ bookProgress.value = makeBookProgress()
+
+ await fetchBookProgress(42)
+
+ expect(bookProgress.value).toBeNull()
+ })
+
+ it('fetchBookProgress sets bookProgress to null on error response', async () => {
+ apiMock.mockResolvedValueOnce(makeResponse({}, { ok: false, status: 500 }))
const { useKoreaderBookProgress } = await import('../useKoreaderBookProgress')
const { bookProgress, fetchBookProgress } = useKoreaderBookProgress()
diff --git a/client/src/features/koreader/composables/useKoreaderBookProgress.ts b/client/src/features/koreader/composables/useKoreaderBookProgress.ts
index eb5db357f..5d06da40b 100644
--- a/client/src/features/koreader/composables/useKoreaderBookProgress.ts
+++ b/client/src/features/koreader/composables/useKoreaderBookProgress.ts
@@ -10,10 +10,6 @@ export function useKoreaderBookProgress() {
loading.value = true
try {
const res = await api(`/api/v1/koreader/books/${bookId}/progress`)
- if (res.status === 404) {
- bookProgress.value = null
- return
- }
if (!res.ok) throw new Error('Failed to fetch book progress')
bookProgress.value = await res.json()
} catch {
diff --git a/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts b/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts
index 7a435dc10..1908727a5 100644
--- a/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts
+++ b/client/src/features/reader/epub/composables/__tests__/useFoliate.spec.ts
@@ -98,21 +98,21 @@ describe('useFoliate.open', () => {
expect(mockGoToFraction).toHaveBeenCalledWith(0.42)
})
- it('does not navigate when cfi is null and fraction is 0', async () => {
+ it('navigates to position 0 when cfi is null and fraction is 0', async () => {
const foliate = useFoliate(() => container)
await foliate.open(1, 1, 'epub', null, 0)
- expect(mockGoTo).not.toHaveBeenCalled()
+ expect(mockGoTo).toHaveBeenCalledWith(0)
expect(mockGoToFraction).not.toHaveBeenCalled()
})
- it('does not navigate when cfi is null and fraction is undefined', async () => {
+ it('navigates to position 0 when cfi is null and fraction is undefined', async () => {
const foliate = useFoliate(() => container)
await foliate.open(1, 1, 'epub', null, undefined)
- expect(mockGoTo).not.toHaveBeenCalled()
+ expect(mockGoTo).toHaveBeenCalledWith(0)
expect(mockGoToFraction).not.toHaveBeenCalled()
})
diff --git a/client/src/features/reader/epub/composables/useFoliate.ts b/client/src/features/reader/epub/composables/useFoliate.ts
index 2fd987c4c..5b2d9b5f5 100644
--- a/client/src/features/reader/epub/composables/useFoliate.ts
+++ b/client/src/features/reader/epub/composables/useFoliate.ts
@@ -136,6 +136,8 @@ export function useFoliate(
await view.goTo(cfi).catch(() => {})
} else if (fallbackFraction !== undefined && fallbackFraction > 0) {
view.goToFraction?.(fallbackFraction)
+ } else {
+ await view.goTo(0).catch(() => {})
}
} catch (e) {
console.error('[useFoliate]', e)
diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue
index 8e2c71a4a..05f6ca008 100644
--- a/client/src/views/HomeView.vue
+++ b/client/src/views/HomeView.vue
@@ -1,5 +1,5 @@