api: handle downloading books of sanitized titles (#1705)

This commit is contained in:
Huang Xin
2025-07-29 18:07:40 +08:00
parent 213fc0d21e
commit 63ed763499
3 changed files with 35 additions and 7 deletions
@@ -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 });
}
@@ -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,
+1 -1
View File
@@ -60,7 +60,7 @@ export const useThemeStore = create<ThemeState>((set, get) => {
set({ systemIsDarkMode: mediaQuery.matches, isDarkMode });
};
mediaQuery.addEventListener('change', handleSystemThemeChange);
mediaQuery?.addEventListener('change', handleSystemThemeChange);
}
return {