fix: vfs changes were not saved
Massimo Melina committed
Feb 10, 2022 at 12:13 UTC
46ffe43a736373d7fdc4caba6c842743b7cb058b
4 files changed
+26
-19
src/adminApis.ts
+1
-1
@@ -15,7 +15,7 @@ export const adminApis: ApiHandlers = {
15
16
async set_config({ values }) {
17
if (values)
18
- setConfig(values, true)
18
+ await setConfig(values)
19
return {}
20
},
21
src/api.vfs.ts
+3
-8
@@ -1,9 +1,8 @@
1
-import { getNodeName, nodeIsDirectory, vfs, VfsNode } from './vfs'
1
+import { getNodeName, nodeIsDirectory, saveVfs, vfs, VfsNode } from './vfs'
2
import _ from 'lodash'
3
import { stat } from 'fs/promises'
4
import { ApiError, ApiHandlers } from './apis'
5
import { dirname } from 'path'
6
-import { saveConfigAsap } from './config'
6
import glob, { Entry } from 'fast-glob'
7
import { enforceFinal, isWindows, isWindowsDrive } from './misc'
8
import { exec } from 'child_process'
@@ -17,10 +16,6 @@ type VfsAdmin = {
16
children?: VfsAdmin[]
17
} & Omit<VfsNode, 'type' | 'children'>
18
20
-function saveVfs() {
21
- saveConfigAsap()
22
-}
23
-
19
const apis: ApiHandlers = {
20
21
async get_vfs() {
@@ -55,7 +50,7 @@ const apis: ApiHandlers = {
50
Object.assign(n, pickProps(props, ['name','source','hidden','forbid','perm','hide','remove']))
51
if (getNodeName(_.omit(n, ['name'])) === n.name) // name only if necessary
52
delete n.name
58
- saveVfs()
53
+ await saveVfs()
54
return n
55
},
56
@@ -67,7 +62,7 @@ const apis: ApiHandlers = {
62
return new ApiError(403, 'invalid under')
63
const a = n.children || (n.children = [])
64
a.unshift({ source, name })
70
- saveVfs()
65
+ await saveVfs()
66
return {}
67
},
68
src/config.ts
+16
-8
@@ -19,10 +19,10 @@ let state: Record<string, any> = {}
19
const emitter = new EventEmitter()
20
emitter.setMaxListeners(10_000)
21
const path = argv.config || process.env.HFS_CONFIG || PATH
22
-watchLoad(path, setConfig, {
22
+watchLoad(path, values => setConfig(values, false), {
23
failedOnFirstAttempt(){
24
console.log("No config file, using defaults")
25
- setConfig({})
25
+ setConfig({}, false)
26
}
27
})
28
@@ -31,6 +31,8 @@ interface ConfigProps<T> {
31
caster?:(argV:string)=> T
32
}
33
export function defineConfig<T>(k: string, definition: ConfigProps<T>) {
34
+ if (definition.defaultValue !== undefined)
35
+ definition.defaultValue = _.cloneDeep(definition.defaultValue)
36
configProps[k] = definition
37
if (!definition.caster)
38
if (typeof definition.defaultValue === 'number')
@@ -49,7 +51,7 @@ export function subscribeConfig<T>({ k, ...definition }:{ k:string } & ConfigPro
51
if (started) {
52
let v = state[k]
53
if (v === undefined)
52
- v = defaultValue
54
+ v = _.cloneDeep(defaultValue)
55
if (v !== undefined)
56
cb(v)
57
}
@@ -68,15 +70,18 @@ export function getWholeConfig({ omit=[], only=[] }: { omit:string[], only:strin
70
return _.cloneDeep(copy)
71
}
72
71
-export function setConfig(newCfg: Record<string,any>, partial=false) {
73
+// pass a value to `save` to force saving decision, or leave undefined for auto
74
+export function setConfig(newCfg: Record<string,any>, save?: boolean) {
75
if (!newCfg)
76
newCfg = {}
77
for (const k in newCfg)
78
check(k)
79
const oldKeys = Object.keys(state)
80
oldKeys.push(...Object.keys(configProps))
78
- if (partial)
79
- return saveConfigAsap()
81
+ if (save) {
82
+ saveConfigAsap().then()
83
+ return
84
+ }
85
for (const k of oldKeys)
86
if (!newCfg.hasOwnProperty(k))
87
check(k)
@@ -86,16 +91,19 @@ export function setConfig(newCfg: Record<string,any>, partial=false) {
91
const oldV = started ? getConfig(k) : state[k] // from second time consider also defaultValue
92
const newV = newCfg[k]
93
const { caster, defaultValue } = configProps[k] ?? {}
89
- let v = newV === undefined ? defaultValue : newV
94
+ let v = newV ?? _.cloneDeep(defaultValue) // if we have an object we may get into troubles letting others change ours
95
if (caster)
96
v = caster(v)
97
const j = JSON.stringify(v)
98
if (j === JSON.stringify(oldV)) return // no change
94
- if (j === JSON.stringify(defaultValue)) // if we move away from the default value and then come back, we restore the initial state (undefined)
99
+ if (newV === undefined // optimization: we know in this case it's equal to the default
100
+ || j === JSON.stringify(defaultValue)) // if we move away from the default value and then come back, we restore the initial state (undefined)
101
delete state[k]
102
else
103
state[k] = v
104
emitter.emit('new.'+k, v, oldV)
105
+ if (save === undefined)
106
+ saveConfigAsap().then()
107
}
108
}
109
src/vfs.ts
+6
-2
@@ -5,7 +5,7 @@ import { dirTraversal, enforceFinal, isDirectory, isWindows, onlyTruthy } from '
5
import Koa from 'koa'
6
import glob from 'fast-glob'
7
import _ from 'lodash'
8
-import { subscribeConfig } from './config'
8
+import { setConfig, subscribeConfig } from './config'
9
10
export interface VfsNode {
11
isTemp?: true, // this node was spawned by a source-d node and is not part of the vfs tree
@@ -72,9 +72,13 @@ export class Vfs {
72
}
73
74
export const vfs = new Vfs()
75
-subscribeConfig<VfsNode>({ k: 'vfs', defaultValue: {} }, data =>
75
+subscribeConfig<VfsNode>({ k: 'vfs', defaultValue: vfs.root }, data =>
76
vfs.root = data)
77
78
+export function saveVfs() {
79
+ return setConfig({ vfs: _.cloneDeep(vfs.root) }, true)
80
+}
81
+
82
function findChildByName(name:string, node:VfsNode) {
83
const { rename } = node
84
if (rename) // @ts-ignore