diff --git a/apps/readest-app/src/__tests__/services/app-service.test.ts b/apps/readest-app/src/__tests__/services/app-service.test.ts index ae8c799d..3ee139f9 100644 --- a/apps/readest-app/src/__tests__/services/app-service.test.ts +++ b/apps/readest-app/src/__tests__/services/app-service.test.ts @@ -269,6 +269,22 @@ describe('BaseAppService', () => { const result = await service.resolveFilePath('', 'Data'); expect(result).toBe('/base/books'); }); + + // base 'None' carries a complete, absolute source path (in-place / + // external books point `book.filePath` outside Books//). Its prefix + // is empty, so the path must be returned verbatim. Prepending a separator + // produced `/C:\Users\...` on Windows (and `//Users/...` on POSIX), which + // the native upload guard (transfer_file.rs `ensure_path_allowed`) rejects + // as "path not in filesystem scope" — issue #4720. + test('returns an absolute path unchanged when the prefix is empty (base None)', async () => { + vi.mocked(mockFs.getPrefix).mockResolvedValue(''); + + const windowsPath = "C:\\Users\\me\\Documents\\books\\Alice's Adventures.epub"; + expect(await service.resolveFilePath(windowsPath, 'None')).toBe(windowsPath); + + const posixPath = '/Users/me/Documents/books/Alice.epub'; + expect(await service.resolveFilePath(posixPath, 'None')).toBe(posixPath); + }); }); describe('settings', () => { diff --git a/apps/readest-app/src/app/library/components/ImportFromFolderDialog.tsx b/apps/readest-app/src/app/library/components/ImportFromFolderDialog.tsx index 4a8c79fb..e1f831c3 100644 --- a/apps/readest-app/src/app/library/components/ImportFromFolderDialog.tsx +++ b/apps/readest-app/src/app/library/components/ImportFromFolderDialog.tsx @@ -434,7 +434,7 @@ const ImportFromFolderDialog: React.FC = ({