mirror of
https://github.com/flynx/pWiki.git
synced 2025-10-29 18:10:09 +00:00
backups working...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
62a6d3de07
commit
8adedd5724
244
pwiki2.js
244
pwiki2.js
@ -778,12 +778,19 @@ var FILESTORE_OPTIONS = {
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
var getOpts = function(opts){
|
||||
return {
|
||||
...FILESTORE_OPTIONS,
|
||||
...(opts ?? {}),
|
||||
} }
|
||||
|
||||
// func(base[, options])
|
||||
// -> true/false
|
||||
//
|
||||
// func(base, path[, options])
|
||||
// -> true/false
|
||||
//
|
||||
// XXX not yet sure how w should handle dot-files....
|
||||
// XXX should these be store methods???
|
||||
// XXX do we need error checking in these???
|
||||
var exists =
|
||||
@ -793,9 +800,7 @@ async function(base, sub, options){
|
||||
options = sub ?? options
|
||||
sub = base
|
||||
base = null }
|
||||
var {index} = Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index} = getOpts(options)
|
||||
|
||||
var target = base ?
|
||||
module.path.join(base, sub)
|
||||
@ -813,9 +818,7 @@ async function(base, sub, options){
|
||||
options = sub ?? options
|
||||
sub = base
|
||||
base = null }
|
||||
var {index} = Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index} = getOpts(options)
|
||||
|
||||
var target = base ?
|
||||
module.path.join(base, sub)
|
||||
@ -838,9 +841,7 @@ async function(base, sub, options){
|
||||
options = sub ?? options
|
||||
sub = base
|
||||
base = null }
|
||||
var {index} = Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index} = getOpts(options)
|
||||
|
||||
var levels = module.path.split(sub)
|
||||
for(var level of levels){
|
||||
@ -873,9 +874,7 @@ async function(base, sub, data, options){
|
||||
data = sub
|
||||
sub = base
|
||||
base = null }
|
||||
var {index} = Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index} = getOpts(options)
|
||||
|
||||
var target = base ?
|
||||
module.path.join(base, sub)
|
||||
@ -910,9 +909,7 @@ async function(base, sub, options){
|
||||
options = sub ?? options
|
||||
sub = base
|
||||
base = '' }
|
||||
var {index} = Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index} = getOpts(options)
|
||||
|
||||
// remove leaf...
|
||||
var target = base == '' ?
|
||||
@ -953,10 +950,7 @@ async function(base, sub, options){
|
||||
var cleanup =
|
||||
module.cleanup =
|
||||
async function(base, options){
|
||||
var {index, clearEmptyDir, dirToFile, verbose} =
|
||||
Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index, clearEmptyDir, dirToFile, verbose} = getOpts(options)
|
||||
|
||||
glob(module.path.join(base, '**/*'))
|
||||
.on('end', async function(paths){
|
||||
@ -986,25 +980,32 @@ async function(base, options){
|
||||
continue }
|
||||
} } }) }
|
||||
|
||||
var backup =
|
||||
module.backup = {
|
||||
// XXX backup config???
|
||||
|
||||
//
|
||||
// backing(<base>[, <options>])
|
||||
// backing(<base>, '**'[, <options>])
|
||||
// backing(<base>, '**', Date.timeStamp()[, <options>])
|
||||
// .create(<base>[, <options>])
|
||||
// .create(<base>, '**'[, <options>])
|
||||
// .create(<base>, '**', Date.timeStamp()[, <options>])
|
||||
// -> <list>
|
||||
//
|
||||
// backing(<base>, <path>[, <version>][, <options>])
|
||||
// .create(<base>, <path>[, <version>][, <options>])
|
||||
// -> <list>
|
||||
//
|
||||
// backing(<base>, <path>, false[, <options>])
|
||||
// .create(<base>, <path>, false[, <options>])
|
||||
// -> <list>
|
||||
//
|
||||
// .create(..) and .restore(..) are completely symmetrical.
|
||||
//
|
||||
// NOTE: backing up ** will include nested backups but will skip the
|
||||
// root backup...
|
||||
// NOTE: currently this ignores only the first element of the options.backup
|
||||
// path.
|
||||
var backup =
|
||||
module.backup =
|
||||
async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
// root backup but will ignore the root backup dir...
|
||||
//
|
||||
// XXX since these are *almost* identical in structure, can we reuse one
|
||||
// to implement the other???
|
||||
// ..or can we implement these in a manner similar to "cp A B" vs. "cp B A"???
|
||||
create: async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
var that = this
|
||||
if(typeof(sub) == 'object'){
|
||||
options = sub
|
||||
sub = '**' }
|
||||
@ -1012,10 +1013,7 @@ async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
options = version
|
||||
version = Date.timeStamp() }
|
||||
// options...
|
||||
var {index, backup, verbose, recursive, cleanBackup} =
|
||||
Object.assign({},
|
||||
FILESTORE_OPTIONS,
|
||||
options ?? {})
|
||||
var {index, backup, verbose, recursive, cleanBackup, __batch} = options = getOpts(options)
|
||||
recursive = recursive ?? false
|
||||
|
||||
var _backup = backup =
|
||||
@ -1025,22 +1023,20 @@ async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
backup =
|
||||
module.path.join(
|
||||
base,
|
||||
module.path.relative(sub, backup))
|
||||
module.path.relative(module.path.dirname(sub), backup))
|
||||
|
||||
// ** or * -- backup each file in path...
|
||||
if(/[\\\/]*\*\*?$/.test(sub)){
|
||||
if(sub.endsWith('**')){
|
||||
options.recursive = true }
|
||||
options.__batch = true
|
||||
|
||||
if(cleanBackup
|
||||
&& fs.existsSync(backup)){
|
||||
verbose
|
||||
&& console.log('backup(..): cleaning:', backup)
|
||||
&& console.log('.create(..): cleaning:', backup)
|
||||
await fs.promises.rm(backup, {recursive: true}) }
|
||||
if(sub.endsWith('**')){
|
||||
options = {
|
||||
...(options ?? {}),
|
||||
recursive: true,
|
||||
} }
|
||||
sub = sub.replace(/[\\\/]*\*\*?$/, '')
|
||||
// XXX should we ignore only the first element (current) or the sub-path???
|
||||
var b = module.path.split(_backup)
|
||||
.filter(function(p){
|
||||
return p != '' })
|
||||
@ -1051,7 +1047,7 @@ async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
.filter(function(file){
|
||||
return !file.includes(b) })
|
||||
.map(async function(file){
|
||||
return await module.backup(base, sub +'/'+ file, version, options) })
|
||||
return await that.create(base, sub +'/'+ file, version, options) })
|
||||
// keep only the paths we backed up...
|
||||
.filter(function(e){
|
||||
return !!e })
|
||||
@ -1064,38 +1060,162 @@ async function(base, sub='**', version=Date.timeStamp(), options){
|
||||
// nothing to backup...
|
||||
if(!fs.existsSync(target)){
|
||||
verbose
|
||||
&& console.log('backup(..): target does not exist:', target)
|
||||
&& console.log('.create(..): target does not exist:', target)
|
||||
return }
|
||||
|
||||
if(!recursive){
|
||||
var stat = await fs.promises.stat(target)
|
||||
if(stat.isDirectory()){
|
||||
sub += '/'+index
|
||||
target += '/'+index
|
||||
// nothing to backup...
|
||||
if(!fs.existsSync(target)){
|
||||
verbose
|
||||
&& console.log('backup(..): nothing to backup:', target)
|
||||
return } } }
|
||||
|
||||
var to = full ?
|
||||
backup +'/'+ sub
|
||||
: backup +'/'+ module.path.basename(sub)
|
||||
var todir = module.path.dirname(to)
|
||||
|
||||
if(!recursive){
|
||||
var stat = await fs.promises.stat(target)
|
||||
if(stat.isDirectory()){
|
||||
target += '/'+index
|
||||
to += '/'+index
|
||||
// nothing to backup...
|
||||
if(!fs.existsSync(target)){
|
||||
verbose
|
||||
&& console.log('backup(..):', sub, '->', to)
|
||||
&& !__batch
|
||||
&& console.log('.create(..): nothing to backup:', target)
|
||||
return } } }
|
||||
|
||||
verbose
|
||||
&& console.log('.create(..):', sub, '->', to)
|
||||
await fs.promises.mkdir(todir, {recursive: true})
|
||||
await fs.promises.cp(target, to, {force: true, recursive})
|
||||
return to } }
|
||||
return to } },
|
||||
restore: async function(base, sub, version, options){
|
||||
var that = this
|
||||
// XXX
|
||||
var restore =
|
||||
module.restore =
|
||||
async function(base, sub, version, options){
|
||||
var {index, backup, verbose, recursive, preBackup, __batch} = options = getOpts(options)
|
||||
recursive = recursive ?? false
|
||||
|
||||
var _backup = backup =
|
||||
version ?
|
||||
module.path.join(backup, version)
|
||||
: backup
|
||||
backup =
|
||||
module.path.join(
|
||||
base,
|
||||
module.path.relative(
|
||||
module.path.dirname(sub),
|
||||
backup))
|
||||
|
||||
// check if we can restore...
|
||||
if(!fs.existsSync(backup)){
|
||||
verbose
|
||||
&& console.log('restore(..): no backup version:', version)
|
||||
return }
|
||||
|
||||
// XXX should we use the same options...
|
||||
preBackup
|
||||
&& await this.create(base, sub, options ?? {})
|
||||
|
||||
// ** or * -- backup each file in path...
|
||||
// NOTE: when restoring there is no difference between ** and *...
|
||||
if(/[\\\/]*\*\*?$/.test(sub)){
|
||||
if(sub.endsWith('**')){
|
||||
options.recursive = true }
|
||||
// restore...
|
||||
// NOTE: we have already made a full backup so no need to
|
||||
// redo it down the line...
|
||||
options.preBackup = false
|
||||
options.__batch = true
|
||||
|
||||
sub = sub.replace(/[\\\/]*\*\*?$/, '')
|
||||
var to = module.path.join(base, sub)
|
||||
var b = module.path.split(_backup)
|
||||
.filter(function(p){
|
||||
return p != '' })
|
||||
.shift()
|
||||
// cleanup...
|
||||
// NOTE: we need this stage as the file list we are backing up
|
||||
// and the one in the target dir can differ, and a single-page
|
||||
// .restore(..) will only remove collisions...
|
||||
await fs.promises.readdir(base +'/'+ sub)
|
||||
.iter()
|
||||
// skip backups...
|
||||
.filter(function(file){
|
||||
return !file.includes(b) })
|
||||
.map(async function(file){
|
||||
var p = module.path.join(base, sub, file)
|
||||
verbose
|
||||
&& console.log('restore(..): removing:', p)
|
||||
await fs.promises.rm(p, {recursive: true})
|
||||
return p })
|
||||
return fs.promises.readdir(backup)
|
||||
.iter()
|
||||
.map(async function(file){
|
||||
return await that.restore(base, sub+'/'+file, version, options) })
|
||||
// keep only the paths we backed up...
|
||||
.filter(function(e){
|
||||
return !!e })
|
||||
|
||||
// single page...
|
||||
} else {
|
||||
var index_file = ''
|
||||
var full = _backup[0] == '/'
|
||||
var source = full ?
|
||||
module.path.join(backup, sub)
|
||||
: module.path.join(backup, module.path.basename(sub))
|
||||
if(!fs.existsSync(source)){
|
||||
verbose
|
||||
&& console.log('restore(..): source not present in backup:', source)
|
||||
return }
|
||||
var to = module.path.join(base, sub)
|
||||
if(fs.existsSync(to)){
|
||||
var stat = await fs.promises.stat(to)
|
||||
if(stat.isDirectory()){
|
||||
var f = module.path.join(to, index)
|
||||
if(fs.existsSync(f)){
|
||||
verbose
|
||||
&& console.log('restore(..): removing:', f)
|
||||
await fs.promises.rm(f) }
|
||||
} else {
|
||||
verbose
|
||||
&& console.log('restore(..): removing:', to)
|
||||
await fs.promises.rm(to) } }
|
||||
|
||||
if(!recursive){
|
||||
// handle dir text...
|
||||
var stat = await fs.promises.stat(source)
|
||||
if(stat.isDirectory()){
|
||||
source += '/'+index
|
||||
to += '/'+index
|
||||
if(!fs.existsSync(source)){
|
||||
verbose
|
||||
&& !__batch
|
||||
&& console.log('restore(..): source not present in backup:', source)
|
||||
return } } }
|
||||
|
||||
verbose
|
||||
&& console.log('restore(..): restoring:', to)
|
||||
await fs.promises.cp(source, to, {recursive: true})
|
||||
return source } },
|
||||
//
|
||||
// Get backup versions...
|
||||
// listbackups(<base>[, <options>])
|
||||
// listbackups(<base>, '*'[, <options>])
|
||||
// -> <list>
|
||||
//
|
||||
// Get backup versions containing <path>...
|
||||
// listbackups(<base>, <path>[, <options>])
|
||||
// -> <list>
|
||||
//
|
||||
list: async function(base, sub, options){
|
||||
var that = this
|
||||
if(typeof(sub) == 'object'){
|
||||
options = sub
|
||||
sub = '*' }
|
||||
var {index, backup} = getOpts(options)
|
||||
// XXX
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// XXX might be a good idea to support ro mode on top level explicitly...
|
||||
// XXX add monitor API + cache + live mode (auto on when lock detected)...
|
||||
var FileStoreRO =
|
||||
@ -1175,7 +1295,7 @@ module.FileStore = {
|
||||
// XXX should these be generic???
|
||||
// XXX add versioning...
|
||||
backup: async function(path='**', options={}){
|
||||
return module.backup(
|
||||
return backup.create(
|
||||
this.__path__, path,
|
||||
{
|
||||
index: this.__directory_text__,
|
||||
@ -1183,7 +1303,7 @@ module.FileStore = {
|
||||
...options,
|
||||
}) },
|
||||
restore: async function(path='**', options={}){
|
||||
return module.restore(
|
||||
return backup.restore(
|
||||
this.__path__, path,
|
||||
{
|
||||
index: this.__directory_text__,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user