From fdf8f05ce3b2ae554a40c4ee41fe2222c7105fdc Mon Sep 17 00:00:00 2001 From: cuyua9 <2114364329@qq.com> Date: Tue, 5 May 2026 22:13:24 +0800 Subject: [PATCH] fix(kiwix): self-heal missing library XML --- admin/app/services/kiwix_library_service.ts | 29 +++++++++++++++++++++ admin/providers/kiwix_migration_provider.ts | 9 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/admin/app/services/kiwix_library_service.ts b/admin/app/services/kiwix_library_service.ts index c7aeabd..9143208 100644 --- a/admin/app/services/kiwix_library_service.ts +++ b/admin/app/services/kiwix_library_service.ts @@ -187,6 +187,35 @@ export class KiwixLibraryService { .filter((b) => b.id && b.path) } + private _validateLibraryXml(xmlContent: string): void { + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: '@_', + isArray: (name) => name === 'book', + }) + + const parsed = parser.parse(xmlContent) + if (!parsed?.library || typeof parsed.library !== 'object') { + throw new Error('Kiwix library XML is missing the library root element.') + } + } + + async ensureValidLibraryXml(): Promise { + try { + const content = await readFile(this.getLibraryFilePath(), 'utf-8') + this._validateLibraryXml(content) + return false + } catch (err: any) { + if (err.code && err.code !== 'ENOENT') { + throw err + } + + logger.warn('[KiwixLibraryService] Library XML is missing or invalid; rebuilding from disk.') + await this.rebuildFromDisk() + return true + } + } + async rebuildFromDisk(opts?: { excludeFilenames?: string[] }): Promise { const dirPath = join(process.cwd(), ZIM_STORAGE_PATH) await ensureDirectoryExists(dirPath) diff --git a/admin/providers/kiwix_migration_provider.ts b/admin/providers/kiwix_migration_provider.ts index 5e010b3..3e1ae61 100644 --- a/admin/providers/kiwix_migration_provider.ts +++ b/admin/providers/kiwix_migration_provider.ts @@ -22,6 +22,7 @@ export default class KiwixMigrationProvider { const Service = (await import('#models/service')).default const { SERVICE_NAMES } = await import('../constants/service_names.js') const { DockerService } = await import('#services/docker_service') + const { KiwixLibraryService } = await import('#services/kiwix_library_service') const kiwixService = await Service.query() .where('service_name', SERVICE_NAMES.KIWIX) @@ -34,9 +35,15 @@ export default class KiwixMigrationProvider { const dockerService = new DockerService() const isLegacy = await dockerService.isKiwixOnLegacyConfig() + const kiwixLibraryService = new KiwixLibraryService() if (!isLegacy) { - logger.info('[KiwixMigrationProvider] Kiwix is already in library mode — no migration needed.') + const rebuilt = await kiwixLibraryService.ensureValidLibraryXml() + if (rebuilt) { + logger.info('[KiwixMigrationProvider] Rebuilt missing or invalid Kiwix library XML.') + } else { + logger.info('[KiwixMigrationProvider] Kiwix is already in library mode — no migration needed.') + } return }