plugin.onDirEntry will let you mess with listing

Massimo Melina committed Jan 12, 2022 at 16:10 UTC 5d9553835aae276974482afff346a35c183db589
2 files changed +28 -4
src/api.file_list.ts
+11 -1
@@ -4,6 +4,7 @@ import createSSE from './sse'
4 import { basename } from 'path'
5 import { ApiError, ApiHandler } from './apis'
6 import { stat } from 'fs/promises'
7 +import { mapPlugins } from './plugins'
8
9 export const file_list:ApiHandler = async ({ path, offset, limit, search, omit, sse }, ctx) => {
10 let node = await vfs.urlToNode(path || '/', ctx)
@@ -19,6 +20,7 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
20 const match = (s?:string) => !s || !search || re.test(s)
21 const walker = walkNode(node, ctx, search ? Infinity : 0)
22 const sseSrv = sse ? createSSE(ctx) : null
23 + const onDirEntryHandlers = mapPlugins(plug => plug.onDirEntry)
24 const res = produceEntries()
25 return !sseSrv && { list: await res }
26
@@ -32,6 +34,14 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
34 const entry = await nodeToDirEntry(sub)
35 if (!entry)
36 continue
37 + const cbParams = { entry, ctx, listPath:path, node:sub }
38 + try {
39 + if (onDirEntryHandlers.some(cb => cb(cbParams) === false))
40 + continue
41 + }
42 + catch(e) {
43 + console.log('a plugin with onDirEntry is causing problems:', e)
44 + }
45 if (offset) {
46 --offset
47 continue
@@ -55,7 +65,7 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
65 }
66 }
67
58 -interface DirEntry { n:string, s?:number, m?:Date, c?:Date }
68 +export interface DirEntry { n:string, s?:number, m?:Date, c?:Date }
69
70 async function nodeToDirEntry(node: VfsNode): Promise<DirEntry | null> {
71 try {
src/plugins.ts
+17 -3
@@ -8,13 +8,20 @@ import mime from 'mime-types'
8 import Koa from 'koa'
9 import { getOrSet, onProcessExit, wantArray } from './misc'
10 import { getConfig, subscribeConfig } from './config'
11 +import { DirEntry } from './api.file_list'
12 +import { VfsNode } from './vfs'
13
14 const PATH = 'plugins'
15
16 const plugins: Record<string, Plugin> = {}
17
16 -export function mapPlugins<T>(cb:(plugin:Readonly<Plugin>, pluginKey:string)=> T) {
17 - return _.map(plugins, cb)
18 +export function mapPlugins<T>(cb:(plugin:Readonly<Plugin>, pluginName:string)=> T) {
19 + return _.map(plugins, (pl,plName) => {
20 + try { return cb(pl,plName) }
21 + catch(e) {
22 + console.log('plugin error', plName, String(e))
23 + }
24 + }).filter(x => x !== undefined) as Exclude<T,undefined>[]
25 }
26
27 export function pluginsMiddleware(): Koa.Middleware {
@@ -62,6 +69,10 @@ subscribeConfig({ k:'disable_plugins', defaultValue:[] }, () => {
69 }
70 })
71
72 +// return false to ask to exclude this entry from results
73 +interface OnDirEntryParams { entry:DirEntry, ctx:Koa.Context, node:VfsNode }
74 +type OnDirEntry = (params:OnDirEntryParams) => void | false
75 +
76 class Plugin {
77 js: any
78 constructor(readonly k:string, private data:any, private unwatch:()=>void){
@@ -93,6 +104,9 @@ class Plugin {
104 get frontend_js(): undefined | string[] {
105 return this.data?.frontend_js
106 }
107 + get onDirEntry(): undefined | OnDirEntry {
108 + return this.data?.onDirEntry
109 + }
110
111 unload() {
112 console.log('unloading plugin', this.k)
@@ -122,7 +136,7 @@ async function rescan() {
136 f = resolve(f) // without this, import won't work
137 const unwatch = watchLoad(f, async () => {
138 try {
125 - console.log('loading plugin', k)
139 + console.log(plugins[k] ? 'reloading plugin' : 'loading plugin', k)
140 const data = await import(f)
141 deleteModule(require.resolve(f)) // avoid caching
142 new Plugin(k, data, unwatch)