diff --git a/components/FileListing.tsx b/components/FileListing.tsx index 8043f76..f49c6b5 100644 --- a/components/FileListing.tsx +++ b/components/FileListing.tsx @@ -297,7 +297,11 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => { // Folder recursive download const handleFolderDownload = (path: string, id: string, name?: string) => () => { const files = (async function* () { - for await (const { meta: c, path: p, isFolder } of traverseFolder(path)) { + for await (const { meta: c, path: p, isFolder, error } of traverseFolder(path)) { + if (error) { + toast.error(`Failed to download folder ${p}: ${error.status} ${error.message} Skipped it to continue.`) + continue + } yield { name: c?.name, url: c ? c['@microsoft.graph.downloadUrl'] : undefined, diff --git a/components/MultiFileDownloader.tsx b/components/MultiFileDownloader.tsx index 1eb0bef..b4f5b60 100644 --- a/components/MultiFileDownloader.tsx +++ b/components/MultiFileDownloader.tsx @@ -161,12 +161,14 @@ export async function downloadTreelikeMultipleFiles({ * @param path Folder to be traversed * @returns Array of items representing folders and files of traversed folder in BFS order and excluding root folder. * Due to BFS, folder items are ALWAYS in front of its children items. + * Error key in the item will contain the error when there is a handleable error. */ export async function* traverseFolder(path: string): AsyncGenerator< { path: string meta: any isFolder: boolean + error?: { status: number; message: string } }, void, undefined @@ -178,7 +180,22 @@ export async function* traverseFolder(path: string): AsyncGenerator< const itemLists = await Promise.all( folderPaths.map(fp => (async fp => { - const data = await fetcher(`/api?path=${fp}`, hashedToken ?? undefined) + let data: any + try { + data = await fetcher(`/api?path=${fp}`, hashedToken ?? undefined) + } catch (error: any) { + // 4xx errors are identified as handleable errors + if (Math.floor(error.status / 100) === 4) { + return { + path: fp, + isFolder: true, + error: { status: error.status, message: error.message.error }, + } + } else { + throw error + } + } + if (data && data.folder) { return data.folder.value.map((c: any) => { const p = `${fp === '/' ? '' : fp}/${encodeURIComponent(c.name)}` @@ -191,8 +208,16 @@ export async function* traverseFolder(path: string): AsyncGenerator< ) ) - const items = itemLists.flat() as { path: string; meta: any; isFolder: boolean }[] - yield* items - folderPaths = items.filter(i => i.isFolder).map(i => i.path) + const items = itemLists.flat() as { + path: string + meta: any + isFolder: boolean + error?: { status: number; message: string } + }[] + yield * items + folderPaths = items + .filter(({ error }) => !error) + .filter(i => i.isFolder) + .map(i => i.path) } }