From fbfd7fd6c69bb76af741f0ecca3182f06a36aa7a Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Sun, 5 Apr 2026 04:56:47 +0800 Subject: [PATCH] fix(hardcover): use Tauri HTTP plugin to bypass CORS and coerce search result IDs to numbers, closes #3751 (#3752) The Hardcover GraphQL API rejects requests from tauri://localhost origin on iOS. Switch to tauriFetch (like KOSyncClient) to bypass CORS in Tauri environments. Also coerce bookId/editionId/pages from search results to numbers since the search API returns string IDs but the GraphQL mutations expect 32-bit integers. Co-authored-by: Claude Opus 4.6 (1M context) --- .../src/services/hardcover/HardcoverClient.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/readest-app/src/services/hardcover/HardcoverClient.ts b/apps/readest-app/src/services/hardcover/HardcoverClient.ts index 4e580205..458ffb81 100644 --- a/apps/readest-app/src/services/hardcover/HardcoverClient.ts +++ b/apps/readest-app/src/services/hardcover/HardcoverClient.ts @@ -1,5 +1,7 @@ import { Book, BookConfig, BookNote } from '@/types/book'; import { getContentMd5 } from '@/utils/misc'; +import { fetch as tauriFetch } from '@tauri-apps/plugin-http'; +import { isTauriAppPlatform } from '@/services/environment'; import { HardcoverSyncMapStore } from './HardcoverSyncMapStore'; import { QUERY_GET_USER_ID, @@ -31,8 +33,6 @@ type BookContext = { } | null; }; -const isTauriEnv = () => typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window; - export class HardcoverClient { private minRequestIntervalMs = 1150; private directEndpoint = 'https://api.hardcover.app/v1/graphql'; @@ -51,7 +51,7 @@ export class HardcoverClient { } private get endpoint() { - return isTauriEnv() ? this.directEndpoint : this.proxyEndpoint; + return isTauriAppPlatform() ? this.directEndpoint : this.proxyEndpoint; } private formatDate(date: Date): string { @@ -96,7 +96,8 @@ export class HardcoverClient { ): Promise { await this.throttleRequest(); - const res = await fetch(this.endpoint, { + const fetchFn = isTauriAppPlatform() ? tauriFetch : window.fetch; + const res = await fetchFn(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -247,11 +248,19 @@ export class HardcoverClient { document?: { id?: number; pages?: number; featured_edition_id?: number }; }; - const bookId = hit.id ?? hit.document?.id; - if (!bookId) return null; + const rawBookId = hit.id ?? hit.document?.id; + if (!rawBookId) return null; - const editionId = hit.featured_edition_id ?? hit.document?.featured_edition_id ?? bookId; - const pages = hit.pages ?? hit.document?.pages ?? null; + const bookId = Number(rawBookId); + const editionId = Number( + hit.featured_edition_id ?? hit.document?.featured_edition_id ?? bookId, + ); + const pages = + hit.pages != null + ? Number(hit.pages) + : hit.document?.pages != null + ? Number(hit.document.pages) + : null; return { editionId,