better code

Massimo Melina committed Apr 17, 2023 at 12:57 UTC 95cce4a211df19e54d03ebb808e67d43b3afb15e
1 file changed +33 -24
src/api.vfs.ts
+33 -24
@@ -5,7 +5,7 @@ import _ from 'lodash'
5 import { stat } from 'fs/promises'
6 import { ApiError, ApiHandlers } from './apiMiddleware'
7 import { dirname, extname, join, resolve } from 'path'
8 -import { dirStream, isWindowsDrive, makeMatcher, newObj } from './misc'
8 +import { dirStream, isWindowsDrive, makeMatcher } from './misc'
9 import {
10 IS_WINDOWS,
11 HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR, HTTP_CONFLICT, HTTP_NOT_ACCEPTABLE,
@@ -32,15 +32,16 @@ const apis: ApiHandlers = {
32
33 async get_vfs() {
34 return {
35 - root: vfs && await recur(vfs),
35 + root: await recur(vfs),
36 defaultPerms,
37 }
38
39 async function recur(node: VfsNode): Promise<VfsAdmin> {
40 - const stats: false | Stats = Boolean(node.source) && await stat(node.source!).catch(e => false)
41 - const isDir = !node.source || stats && stats.isDirectory()
40 + const { source } = node
41 + const stats: false | Stats = Boolean(source) && await stat(source!).catch(() => false)
42 + const isDir = !source || stats && stats.isDirectory()
43 const copyStats: Pick<VfsAdmin, 'size' | 'ctime' | 'mtime'> = stats ? _.pick(stats, ['size', 'ctime', 'mtime'])
43 - : { size: node.source ? -1 : undefined }
44 + : { size: source ? -1 : undefined }
45 if (copyStats.mtime && Number(copyStats.mtime) === Number(copyStats.ctime))
46 delete copyStats.mtime
47 const isRoot = node === vfs
@@ -48,7 +49,7 @@ const apis: ApiHandlers = {
49 ...copyStats,
50 ...node,
51 website: Boolean(node.children?.find(isSameFilenameAs('index.html')))
51 - || isDir && node.source && await stat(join(node.source, 'index.html')).then(() => true, () => undefined)
52 + || isDir && source && await stat(join(source, 'index.html')).then(() => true, () => undefined)
53 || undefined,
54 name: isRoot ? undefined : getNodeName(node),
55 type: isDir ? 'folder' : undefined,
@@ -58,11 +59,13 @@ const apis: ApiHandlers = {
59 },
60
61 async move_vfs({ from, parent }) {
61 - if (from <= '/' || !parent)
62 + if (!from || !parent)
63 return new ApiError(HTTP_BAD_REQUEST)
64 const fromNode = await urlToNodeOriginal(from)
65 if (!fromNode)
66 return new ApiError(HTTP_NOT_FOUND, 'from not found')
67 + if (fromNode === vfs)
68 + return new ApiError(HTTP_BAD_REQUEST, 'from is root')
69 const parentNode = await urlToNodeOriginal(parent)
70 if (!parentNode)
71 return new ApiError(HTTP_NOT_FOUND, 'parent not found')
@@ -82,39 +85,39 @@ const apis: ApiHandlers = {
85 const n = await urlToNodeOriginal(uri)
86 if (!n)
87 return new ApiError(HTTP_NOT_FOUND, 'path not found')
85 - props = pickProps(props, ['name','source','masks','default', 'accept', ...Object.keys(defaultPerms)])
88 + props = pickProps(props, ['name','source','masks','default','accept', ...Object.keys(defaultPerms)]) // sanitize
89 if (props.name && props.name !== getNodeName(n)) {
90 const parent = await urlToNodeOriginal(dirname(uri))
91 if (parent?.children?.find(x => getNodeName(x) === props.name))
92 return new ApiError(HTTP_CONFLICT, 'name already present')
93 }
91 - props = newObj(props, v => v === null ? undefined : v) // null is a way to serialize undefined, that will restore default values
94 if (props.masks && typeof props.masks !== 'object')
95 delete props.masks
96 Object.assign(n, props)
95 - if (getNodeName(_.omit(n, ['name'])) === n.name) // name only if necessary
96 - n.name = undefined
97 + simplifyName(n)
98 await saveVfs()
99 return n
100 },
101
102 async add_vfs({ parent, source, name }) {
102 - const n = parent ? await urlToNodeOriginal(parent) : vfs
103 - if (!n)
104 - return new ApiError(HTTP_NOT_FOUND, 'invalid parent')
105 - if (n.isTemp || !await nodeIsDirectory(n))
106 - return new ApiError(HTTP_NOT_ACCEPTABLE, 'invalid parent')
103 + if (!source && !name)
104 + return new ApiError(HTTP_BAD_REQUEST, 'name or source required')
105 + parent = parent ? await urlToNodeOriginal(parent) : vfs
106 + if (!parent)
107 + return new ApiError(HTTP_NOT_FOUND, 'parent not found')
108 + if (!await nodeIsDirectory(parent))
109 + return new ApiError(HTTP_NOT_ACCEPTABLE, 'parent not a folder')
110 if (isWindowsDrive(source))
111 source += '\\' // slash must be included, otherwise it will refer to the cwd of that drive
109 - let tryName = getNodeName({ name, source })
110 - const ext = extname(tryName)
111 - const noExt = ext ? tryName.slice(0, -ext.length) : tryName
112 + const child = { source, name }
113 + name = getNodeName(child) // could be not given as input
114 + const ext = extname(name)
115 + const noExt = ext ? name.slice(0, -ext.length) : name
116 let idx = 2
113 - while (n.children?.find(isSameFilenameAs(tryName)))
114 - tryName = `${noExt} ${idx++}${ext}`
115 - name = tryName
116 - n.children ||= []
117 - n.children.unshift({ source, name })
117 + while (parent.children?.find(isSameFilenameAs(name)))
118 + name = `${noExt} ${idx++}${ext}`
119 + child.name = name
120 + ;(parent.children ||= []).unshift({ source, name })
121 await saveVfs()
122 return { name }
123 },
@@ -209,4 +212,10 @@ function pickProps(o: any, keys: string[]) {
212 if (k in o)
213 ret[k] = o[k] === null || o[k] === '' ? undefined : o[k]
214 return ret
215 +}
216 +
217 +function simplifyName(node: VfsNode) {
218 + const { name, ...noName } = node
219 + if (getNodeName(noName) === name)
220 + delete node.name
221 }
\ No newline at end of file