api.file_list: support for { offset, limit }

Massimo Melina committed Dec 19, 2021 at 21:43 UTC 19d45f6cbf8acdb47cf6553915e1bb79dbac21ba
3 files changed +31 -17
dev-notes.md
+1
@@ -8,6 +8,7 @@
8 - when reloading vfs wait for it to be unlocked
9 - search
10 - sticky breadcrumbs
11 +- frontend: ensure icons are loaded before going on
12 - frontend: dialogs
13 - vfs: serve an html for a folder?
14 - "default" property for vfsNode?
src/apis.ts
+30 -6
@@ -1,11 +1,12 @@
1 import Koa from 'koa'
2 import { directPermOnNode, vfs, VfsNode } from './vfs'
3 -import { globDir } from './misc'
3 +import { complySlashes, enforceFinal } from './misc'
4 import { Stats } from 'fs'
5 import { stat } from 'fs/promises'
6 import _ from 'lodash'
7 import { getCurrentUser, verifyLogin } from './perm'
8 import { sessions } from './sessions'
9 +import glob from 'fast-glob'
10
11 export const SESSION_COOKIE = 'hfs_$id'
12
@@ -37,7 +38,7 @@ export function apiMw(apis: ApiHandlers) : Koa.Middleware {
38 }
39
40 export const frontEndApis: ApiHandlers = {
40 - async file_list({ path }, ctx) {
41 + async file_list({ path, offset, limit }, ctx) {
42 let node = await vfs.urlToNode(path || '/', ctx)
43 if (!node)
44 return
@@ -45,11 +46,34 @@ export const frontEndApis: ApiHandlers = {
46 const list = await Promise.all((node.children ||[]).map(node =>
47 !node.hidden && directPermOnNode(node,who) && nodeToFile(node) ))
48 _.remove(list, x => !x)
48 - const source = node.source
49 - if (source) {
50 - const res = await globDir(source, [node.hide, node.remove])
51 - list.push( ...res.map(x => statToFile(node!.rename?.[x.name] || x.name, x.stats!)) )
49 + if (offset)
50 + offset -= list.splice(0, offset).length
51 + const { source } = node
52 + if (list.length > limit)
53 + list.splice(limit)
54 + else if (source) {
55 + const base = enforceFinal('/', complySlashes(source)) // fast-glob lib wants forward-slashes
56 + const ignore = [node.hide, node.remove].flat().filter(Boolean).map(x => base!+x)
57 + const dirStream = glob.stream(base+'*', {
58 + dot: true,
59 + onlyFiles: false,
60 + ignore,
61 + })
62 + for await (let path of dirStream) {
63 + if (offset) {
64 + offset--
65 + continue
66 + }
67 + if (limit === list.length)
68 + break
69 + if (path instanceof Buffer)
70 + path = path.toString('utf8')
71 + const stats = await stat(path)
72 + const name = path.slice(base.length)
73 + list.push(statToFile(node!.rename?.[name] || name, stats))
74 + }
75 }
76 +
77 return { list }
78 },
79 async login({ user, password }, ctx) {
src/misc.ts
-11
@@ -23,17 +23,6 @@ export function prefix(pre:string, v:string|number, post:string='') {
23 return v ? pre+v+post : ''
24 }
25
26 -export async function globDir(path: string, ignore?: any[]) {
27 - path = enforceFinal('/', complySlashes(path)) // fast-glob lib wants forward-slashes
28 - return await glob(path + '*', {
29 - stats: true,
30 - dot: true,
31 - markDirectories: true,
32 - onlyFiles: false,
33 - ignore: ignore?.flat().filter(Boolean).map(x => path!+x),
34 - })
35 -}
36 -
26 export function setHidden(dest: object, src:object) {
27 Object.defineProperties(dest, objSameKeys(src, value => ({ enumerable:false, value })))
28 }