fix: load backup library data if main library data is unavailable, closes #1672 (#1689)

This commit is contained in:
Huang Xin
2025-07-27 22:48:58 +08:00
committed by GitHub
parent 6eeb8e7580
commit d01897d62a
3 changed files with 60 additions and 11 deletions
+10 -4
View File
@@ -144,8 +144,12 @@ export async function POST(req: NextRequest) {
.insert(dbRec)
.select()
.single();
console.log('Inserted record:', inserted);
if (insertError) return { error: insertError.message };
if (insertError) {
console.log(`Failed to insert ${table} record:`, JSON.stringify(dbRec));
return {
error: insertError.message,
};
}
authoritativeRecords.push(inserted);
} else {
const clientUpdatedAt = dbRec.updated_at ? new Date(dbRec.updated_at).getTime() : 0;
@@ -166,8 +170,10 @@ export async function POST(req: NextRequest) {
.match(matchConditions)
.select()
.single();
console.log('Updated record:', updated);
if (updateError) return { error: updateError.message };
if (updateError) {
console.log(`Failed to update ${table} record:`, JSON.stringify(dbRec));
return { error: updateError.message };
}
authoritativeRecords.push(updated);
} else {
authoritativeRecords.push(serverData);
+47 -7
View File
@@ -16,6 +16,7 @@ import {
formatAuthors,
getFilename,
getPrimaryLanguage,
getLibraryBackupFilename,
} from '@/utils/book';
import { partialMD5 } from '@/utils/md5';
import { BookDoc, DocumentLoader, EXTS } from '@/libs/document';
@@ -537,17 +538,44 @@ export abstract class BaseAppService implements AppService {
: this.getCoverImageUrl(book);
}
private async loadJSONFile(
filename: string,
): Promise<{ success: boolean; data?: unknown; error?: unknown }> {
try {
const txt = await this.fs.readFile(filename, 'Books', 'text');
if (!txt || typeof txt !== 'string' || txt.trim().length === 0) {
return { success: false, error: 'File is empty or invalid' };
}
try {
const data = JSON.parse(txt as string);
return { success: true, data };
} catch (parseError) {
return { success: false, error: `JSON parse error: ${parseError}` };
}
} catch (error) {
return { success: false, error };
}
}
async loadLibraryBooks(): Promise<Book[]> {
console.log('Loading library books...');
let books: Book[] = [];
const libraryFilename = getLibraryFilename();
const backupFilename = getLibraryBackupFilename();
try {
const txt = await this.fs.readFile(libraryFilename, 'Books', 'text');
books = JSON.parse(txt as string);
} catch {
await this.fs.createDir('', 'Books', true);
await this.fs.writeFile(libraryFilename, 'Books', '[]');
const mainResult = await this.loadJSONFile(libraryFilename);
if (mainResult.success) {
books = mainResult.data as Book[];
} else {
const backupResult = await this.loadJSONFile(backupFilename);
if (backupResult.success) {
books = backupResult.data as Book[];
console.warn('Loaded library from backup file:', backupFilename);
} else {
await this.fs.createDir('', 'Books', true);
await this.fs.writeFile(libraryFilename, 'Books', '[]');
await this.fs.writeFile(backupFilename, 'Books', '[]');
}
}
await Promise.all(
@@ -564,7 +592,19 @@ export abstract class BaseAppService implements AppService {
async saveLibraryBooks(books: Book[]): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const libraryBooks = books.map(({ coverImageUrl, ...rest }) => rest);
await this.fs.writeFile(getLibraryFilename(), 'Books', JSON.stringify(libraryBooks));
const jsonData = JSON.stringify(libraryBooks, null, 2);
const libraryFilename = getLibraryFilename();
const backupFilename = getLibraryBackupFilename();
const saveResults = await Promise.allSettled([
this.fs.writeFile(backupFilename, 'Books', jsonData),
this.fs.writeFile(libraryFilename, 'Books', jsonData),
]);
const backupSuccess = saveResults[0].status === 'fulfilled';
const mainSuccess = saveResults[1].status === 'fulfilled';
if (!backupSuccess || !mainSuccess) {
throw new Error('Failed to save library books');
}
}
private imageToArrayBuffer(imageUrl?: string, imageFile?: string): Promise<ArrayBuffer> {
+3
View File
@@ -11,6 +11,9 @@ export const getDir = (book: Book) => {
export const getLibraryFilename = () => {
return 'library.json';
};
export const getLibraryBackupFilename = () => {
return 'library_backup.json';
};
export const getRemoteBookFilename = (book: Book) => {
// S3 storage: https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/object-keys.html
if (getStorageType() === 'r2') {