admin/fs: don't show hidden files
Massimo Melina committed
Apr 13, 2022 at 23:23 UTC
ebbc8a9c6051bc2afab32f2ead8744a1c6559bbf
3 files changed
+41
-42
server/src/api.vfs.ts
+2
-11
@@ -5,8 +5,7 @@ import _ from 'lodash'
5
import { stat } from 'fs/promises'
6
import { ApiError, ApiHandlers } from './apiMiddleware'
7
import { dirname, join } from 'path'
8
-import glob from 'fast-glob'
9
-import { enforceFinal, isWindowsDrive, objSameKeys } from './misc'
8
+import { dirStream, enforceFinal, isWindowsDrive, objSameKeys } from './misc'
9
import { exec } from 'child_process'
10
import { promisify } from 'util'
11
import { FORBIDDEN, IS_WINDOWS } from './const'
@@ -128,17 +127,9 @@ const apis: ApiHandlers = {
127
try {
128
if (isWindowsDrive(path))
129
path = enforceFinal('/', path)
131
- const dirStream = glob.stream('*', {
132
- cwd: path,
133
- dot: true,
134
- onlyFiles: false,
135
- suppressErrors: true,
136
- })
137
- for await (let name of dirStream) {
130
+ for await (const name of dirStream(path)) {
131
if (ctx.req.aborted)
132
return
140
- if (name instanceof Buffer)
141
- name = name.toString('utf8')
133
try {
134
const full = join(path, name)
135
const stats = await stat(full)
server/src/misc.ts
+37
@@ -7,6 +7,9 @@ import { watch } from 'fs'
7
import _ from 'lodash'
8
import { Readable } from 'stream'
9
import Koa from 'koa'
10
+import glob from 'fast-glob'
11
+import { IS_WINDOWS } from './const'
12
+import { execFile } from 'child_process'
13
14
export type Callback<IN=void, OUT=void> = (x:IN) => OUT
15
export type Dict<T = any> = Record<string, T>
@@ -229,3 +232,37 @@ export function isLocalHost(s: string | Koa.Context) {
232
s = s.socket.remoteAddress || '' // don't use .ip as it is subject to proxied ips
233
return s === '127.0.0.1' || s === '::1' || s === '::ffff:127.0.0.1'
234
}
235
+
236
+export async function* dirStream(path: string) {
237
+ const dirStream = glob.stream('*', {
238
+ cwd: path,
239
+ dot: true,
240
+ onlyFiles: false,
241
+ suppressErrors: true,
242
+ })
243
+ const skip = await getItemsToSkip(path)
244
+ for await (let path of dirStream) {
245
+ if (path instanceof Buffer)
246
+ path = path.toString('utf8')
247
+ if (skip?.includes(path))
248
+ continue
249
+ yield path
250
+ }
251
+
252
+ async function getItemsToSkip(path: string) {
253
+ if (!IS_WINDOWS) return
254
+ const out = await run('dir', ['/ah', '/b', path.replace(/\//g, '\\')])
255
+ return out.split('\r\n').slice(0,-1)
256
+ }
257
+}
258
+
259
+export function run(cmd: string, args: string[] = []): Promise<string> {
260
+ return new Promise((resolve, reject) =>
261
+ execFile('cmd', ['/c', cmd, ...args], (err, stdout) => {
262
+ if (err)
263
+ reject(err)
264
+ else
265
+ resolve(stdout)
266
+ }))
267
+}
268
+
server/src/vfs.ts
+2
-31
@@ -3,7 +3,7 @@
3
import fs from 'fs/promises'
4
import { basename } from 'path'
5
import { isMatch } from 'micromatch'
6
-import { dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys } from './misc'
6
+import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys } from './misc'
7
import Koa from 'koa'
8
import glob from 'fast-glob'
9
import _ from 'lodash'
@@ -12,7 +12,6 @@ import { FORBIDDEN, IS_WINDOWS } from './const'
12
import events from './events'
13
import { getCurrentUsernameExpanded } from './perm'
14
import { with_ } from './misc'
15
-import { execFile } from 'child_process'
15
16
const WHO_ANYONE = true
17
const WHO_NO_ONE = false
@@ -156,20 +155,9 @@ export async function* walkNode(parent:VfsNode, ctx: Koa.Context, depth:number=0
155
return
156
try {
157
const base = enforceFinal('/', source)
159
- const dirStream = glob.stream('*', {
160
- dot: true,
161
- onlyFiles: false,
162
- cwd: base,
163
- suppressErrors: true,
164
- })
165
- const skip = await getItemsToSkip(base)
166
- for await (let path of dirStream) {
158
+ for await (const path of dirStream(base)) {
159
if (ctx.req.aborted)
160
return
169
- if (path instanceof Buffer)
170
- path = path.toString('utf8')
171
- if (skip?.includes(path))
172
- continue
161
let { rename } = parent
162
const renamed = rename?.[path]
163
yield* workItem({
@@ -205,23 +193,6 @@ export async function* walkNode(parent:VfsNode, ctx: Koa.Context, depth:number=0
193
catch{} // stat failed in nodeIsDirectory, ignore
194
}
195
}
208
-
209
-async function getItemsToSkip(path: string) {
210
- if (!IS_WINDOWS) return
211
- const out = await run('dir', ['/ah', '/b', path.replace(/\//g, '\\')])
212
- return out.split('\r\n').slice(0,-1)
213
-}
214
-
215
-function run(cmd: string, args: string[] = []): Promise<string> {
216
- return new Promise((resolve, reject) =>
217
- execFile('cmd', ['/c', cmd, ...args], (err, stdout) => {
218
- if (err)
219
- reject(err)
220
- else
221
- resolve(stdout)
222
- }))
223
-}
224
-
196
function applyMasks(item: VfsNode, parent: VfsNode, name: string) {
197
const { masks } = parent
198
if (!masks) return