report failing plugin for onDirEntry

Massimo Melina committed May 9, 2026 at 17:30 UTC ee281752da1858eaec4d9b241666cf028a716cb6
1 file changed +11 -10
src/api.get_file_list.ts
+11 -10
@@ -1,12 +1,12 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import {
4 - applyParentToChild, getNodeName, hasDefaultFile, hasPermission, masksCouldGivePermission, nodeIsFolder,
4 + applyParentToChild, getNodeName, hasDefaultFile, hasPermission, masksCouldGivePermission, nodeIsFolder, nodeStats,
5 statusCodeForMissingPerm, urlToNode, VfsNode, walkNode
6 } from './vfs'
7 import { ApiError, ApiHandler } from './apiMiddleware'
8 import { mapPlugins } from './plugins'
9 -import { apiAssertTypes, asyncGeneratorToArray, pattern2filter, statWithTimeout, WHO_NO_ONE } from './misc'
9 +import { apiAssertTypes, asyncGeneratorToArray, pattern2filter, WHO_NO_ONE } from './misc'
10 import { HTTP_METHOD_NOT_ALLOWED, HTTP_NOT_FOUND } from './const'
11 import Koa from 'koa'
12 import { getCommentFor, areCommentsEnabled } from './comments'
@@ -46,7 +46,7 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, c, onl
46 limit = Number(limit)
47 const { filterName, filterComment, fileMask, depth } = paramsToFilter(rest)
48 const walker = walkNode(node, { ctx: admin ? undefined : ctx, onlyFolders, onlyFiles, depth })
49 - const onDirEntryHandlers = mapPlugins(plug => plug.onDirEntry)
49 + const onDirEntryHandlers = mapPlugins((plug, id) => plug.onDirEntry && { id, cb: plug.onDirEntry })
50 const can_upload = admin || hasPermission(node, 'can_upload', ctx)
51 const can_delete = admin || hasPermission(node, 'can_delete', ctx)
52 const fakeChild = await applyParentToChild({ source: 'dummy-file', original: undefined }, node) // used to check permission; simple but can produce false results; 'original' to simulate a non-vfs node
@@ -87,15 +87,16 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, c, onl
87 continue
88 const cbParams = { entry, ctx, listUri: uri, node: sub }
89 try {
90 - const res = await Promise.all(onDirEntryHandlers.map(cb => cb(cbParams)))
90 + const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) =>
91 + Promise.resolve().then(() => cb(cbParams)).catch(error => { throw { id, error } })))
92 if (res.some(x => x === false))
93 continue
93 - if ((await events.emitAsync('dirEntry', cbParams))?.isDefaultPrevented())
94 - continue
94 }
96 - catch(e) {
97 - console.warn("A plugin is causing problems on dirEntry:", e)
95 + catch(e: any) {
96 + console.warn(`Plugin ${e?.id || '?'} is causing problems on onDirEntry:`, e?.error ?? e)
97 }
98 + if ((await events.emitAsync('dirEntry', cbParams))?.isDefaultPrevented())
99 + continue
100 if (offset) {
101 --offset
102 continue
@@ -120,10 +121,10 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, c, onl
121 const [web, comment, st] = await Promise.all([
122 hasDefaultFile(node, ctx).then(x => x ? true : undefined),
123 node.comment ?? getCommentFor(source),
123 - source ? (node.stats || statWithTimeout(source).catch(e => {
124 + nodeStats(node).catch(e => {
125 if (!isFolder || !node.children?.length) // folders with virtual children, keep them
126 throw e
126 - })) : undefined
127 + })
128 ])
129 // permissions of entries are sent as a difference with permissions of parent
130 const pl = node.can_list === WHO_NO_ONE ? 'l'