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