admin/fs: adding an item with existing name, append number in name

Massimo Melina committed Mar 29, 2023 at 23:46 UTC 7e42b4f88b254238b496202539876e77d29848a4
2 files changed +17 -13
src/api.vfs.ts
+9 -7
@@ -4,7 +4,7 @@ import { defaultPerms, getNodeName, isSameFilenameAs, nodeIsDirectory, saveVfs,
4 import _ from 'lodash'
5 import { stat } from 'fs/promises'
6 import { ApiError, ApiHandlers } from './apiMiddleware'
7 -import { dirname, join, resolve } from 'path'
7 +import { dirname, extname, join, resolve } from 'path'
8 import { dirStream, isWindowsDrive, matches, newObj } from './misc'
9 import {
10 IS_WINDOWS,
@@ -106,15 +106,17 @@ const apis: ApiHandlers = {
106 return new ApiError(HTTP_NOT_ACCEPTABLE, 'invalid parent')
107 if (isWindowsDrive(source))
108 source += '\\' // slash must be included, otherwise it will refer to the cwd of that drive
109 - else if (source === '/')
110 - name ||= 'root'
109 + let tryName = getNodeName({ name, source })
110 + const ext = extname(tryName)
111 + const noExt = ext ? tryName.slice(0, -ext.length) : tryName
112 + let idx = 2
113 + while (n.children?.find(isSameFilenameAs(tryName)))
114 + tryName = `${noExt} ${idx++}${ext}`
115 + name = tryName
116 n.children ||= []
112 - const sameName = name && isSameFilenameAs(name)
113 - if (n.children.find(x => source && source === x.source || sameName?.(x)))
114 - return new ApiError(HTTP_CONFLICT, 'already present')
117 n.children.unshift({ source, name })
118 await saveVfs()
117 - return {}
119 + return { name }
120 },
121
122 async del_vfs({ uris }) {
src/vfs.ts
+8 -6
@@ -133,16 +133,18 @@ export function saveVfs() {
133 }
134
135 export function getNodeName(node: VfsNode) {
136 - const { name, source: s } = node
136 + const { name, source } = node
137 if (name)
138 return name
139 - if (!s)
139 + if (!source)
140 return '' // should happen only for root
141 - if (/^[a-zA-Z]:\\?$/.test(s))
142 - return s.slice(0, 2) // exclude trailing slash
143 - const base = basename(s)
141 + if (source === '/')
142 + return 'root'
143 + if (/^[a-zA-Z]:\\?$/.test(source))
144 + return source.slice(0, 2) // exclude trailing slash
145 + const base = basename(source)
146 if (/^[./\\]*$/.test(base)) // if empty or special-chars-only
145 - return basename(resolve(s)) // resolve to try to get more
147 + return basename(resolve(source)) // resolve to try to get more
148 return base
149 }
150