From 63ed763499a0a16185a9cbb303eb494b7366f59b Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Tue, 29 Jul 2025 18:07:40 +0800 Subject: [PATCH] api: handle downloading books of sanitized titles (#1705) --- .../src/pages/api/deepl/translate.ts | 3 +- .../src/pages/api/storage/download.ts | 37 ++++++++++++++++--- apps/readest-app/src/store/themeStore.ts | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/apps/readest-app/src/pages/api/deepl/translate.ts b/apps/readest-app/src/pages/api/deepl/translate.ts index 2b387e86..61578e07 100644 --- a/apps/readest-app/src/pages/api/deepl/translate.ts +++ b/apps/readest-app/src/pages/api/deepl/translate.ts @@ -167,9 +167,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { }); return res.status(200).json({ translations }); } catch (error) { - console.error('Error proxying DeepL request:', error); if (error instanceof Error && error.message.includes(ErrorCodes.DAILY_QUOTA_EXCEEDED)) { return res.status(429).json({ error: ErrorCodes.DAILY_QUOTA_EXCEEDED }); + } else { + console.error('Error proxying DeepL request:', error); } return res.status(500).json({ error: ErrorCodes.INTERNAL_SERVER_ERROR }); } diff --git a/apps/readest-app/src/pages/api/storage/download.ts b/apps/readest-app/src/pages/api/storage/download.ts index 11620870..93e47003 100644 --- a/apps/readest-app/src/pages/api/storage/download.ts +++ b/apps/readest-app/src/pages/api/storage/download.ts @@ -25,25 +25,52 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) // Verify the file belongs to the user const supabase = createSupabaseClient(token); - const { data: fileRecord, error: fileError } = await supabase + const result = await supabase .from('files') - .select('user_id') + .select('user_id, file_key') .eq('user_id', user.id) .eq('file_key', fileKey) // index idx_files_file_key_deleted_at on public.files .is('deleted_at', null) .limit(1) .single(); + const { error: fileError } = result; + let { data: fileRecord } = result; + if (fileError || !fileRecord) { - return res.status(404).json({ error: 'File not found' }); + // Fallback for corrupted file names, using book hash and file extension to match fileKey + if (fileKey.includes('Readest/Book')) { + const parts = fileKey.split('/'); + if (parts.length === 5) { + const bookHash = parts[3]!; + const filename = parts[4]!; + const fileExtension = filename.split('.').pop() || ''; + + const { data: fileRecords, error: fileError } = await supabase + .from('files') + .select('user_id, file_key') + .eq('user_id', user.id) + .eq('book_hash', bookHash) + .is('deleted_at', null); + + if (!fileError && fileRecords && fileRecords.length > 0) { + const matchedFile = fileRecords.find((f) => f.file_key.endsWith(`.${fileExtension}`)); + if (matchedFile) { + fileRecord = matchedFile; + } + } else { + return res.status(404).json({ error: 'File not found' }); + } + } + } } - if (fileRecord.user_id !== user.id) { + if (fileRecord?.user_id !== user.id) { return res.status(403).json({ error: 'Unauthorized access to the file' }); } try { - const downloadUrl = await getDownloadSignedUrl(fileKey, 1800); + const downloadUrl = await getDownloadSignedUrl(fileRecord.file_key, 1800); res.status(200).json({ downloadUrl, diff --git a/apps/readest-app/src/store/themeStore.ts b/apps/readest-app/src/store/themeStore.ts index b54cba15..8c0de1b1 100644 --- a/apps/readest-app/src/store/themeStore.ts +++ b/apps/readest-app/src/store/themeStore.ts @@ -60,7 +60,7 @@ export const useThemeStore = create((set, get) => { set({ systemIsDarkMode: mediaQuery.matches, isDarkMode }); }; - mediaQuery.addEventListener('change', handleSystemThemeChange); + mediaQuery?.addEventListener('change', handleSystemThemeChange); } return {