mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-10-31 11:00:08 +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, | 	verbose: true, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var getOpts = function(opts){ | ||||||
|  | 	return { | ||||||
|  | 		...FILESTORE_OPTIONS, | ||||||
|  | 		...(opts ?? {}), | ||||||
|  | 	} } | ||||||
|  | 
 | ||||||
| //	func(base[, options])
 | //	func(base[, options])
 | ||||||
| //		-> true/false
 | //		-> true/false
 | ||||||
| //
 | //
 | ||||||
| //	func(base, path[, options])
 | //	func(base, path[, options])
 | ||||||
| //		-> true/false
 | //		-> true/false
 | ||||||
| //
 | //
 | ||||||
|  | // XXX not yet sure how w should handle dot-files....
 | ||||||
| // XXX should these be store methods???
 | // XXX should these be store methods???
 | ||||||
| // XXX do we need error checking in these???
 | // XXX do we need error checking in these???
 | ||||||
| var exists = | var exists = | ||||||
| @ -793,9 +800,7 @@ async function(base, sub, options){ | |||||||
| 		options = sub ?? options | 		options = sub ?? options | ||||||
| 		sub = base | 		sub = base | ||||||
| 		base = null } | 		base = null } | ||||||
| 	var {index} = Object.assign({},  | 	var {index} = getOpts(options) | ||||||
| 		FILESTORE_OPTIONS,  |  | ||||||
| 		options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	var target = base ? | 	var target = base ? | ||||||
| 		module.path.join(base, sub) | 		module.path.join(base, sub) | ||||||
| @ -813,9 +818,7 @@ async function(base, sub, options){ | |||||||
| 		options = sub ?? options | 		options = sub ?? options | ||||||
| 		sub = base | 		sub = base | ||||||
| 		base = null } | 		base = null } | ||||||
| 	var {index} = Object.assign({},  | 	var {index} = getOpts(options) | ||||||
| 		FILESTORE_OPTIONS,  |  | ||||||
| 		options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	var target = base ? | 	var target = base ? | ||||||
| 		module.path.join(base, sub) | 		module.path.join(base, sub) | ||||||
| @ -838,9 +841,7 @@ async function(base, sub, options){ | |||||||
| 		options = sub ?? options | 		options = sub ?? options | ||||||
| 		sub = base  | 		sub = base  | ||||||
| 		base = null } | 		base = null } | ||||||
| 	var {index} = Object.assign({},  | 	var {index} = getOpts(options) | ||||||
| 		FILESTORE_OPTIONS,  |  | ||||||
| 		options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	var levels = module.path.split(sub) | 	var levels = module.path.split(sub) | ||||||
| 	for(var level of levels){ | 	for(var level of levels){ | ||||||
| @ -873,9 +874,7 @@ async function(base, sub, data, options){ | |||||||
| 		data = sub | 		data = sub | ||||||
| 		sub = base | 		sub = base | ||||||
| 		base = null } | 		base = null } | ||||||
| 	var {index} = Object.assign({},  | 	var {index} = getOpts(options) | ||||||
| 		FILESTORE_OPTIONS,  |  | ||||||
| 		options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	var target = base ? | 	var target = base ? | ||||||
| 		module.path.join(base, sub) | 		module.path.join(base, sub) | ||||||
| @ -910,9 +909,7 @@ async function(base, sub, options){ | |||||||
| 		options = sub ?? options | 		options = sub ?? options | ||||||
| 		sub = base | 		sub = base | ||||||
| 		base = '' } | 		base = '' } | ||||||
| 	var {index} = Object.assign({},  | 	var {index} = getOpts(options) | ||||||
| 		FILESTORE_OPTIONS,  |  | ||||||
| 		options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	// remove leaf...
 | 	// remove leaf...
 | ||||||
| 	var target = base == '' ? | 	var target = base == '' ? | ||||||
| @ -953,10 +950,7 @@ async function(base, sub, options){ | |||||||
| var cleanup = | var cleanup = | ||||||
| module.cleanup = | module.cleanup = | ||||||
| async function(base, options){ | async function(base, options){ | ||||||
| 	var {index, clearEmptyDir, dirToFile, verbose} =  | 	var {index, clearEmptyDir, dirToFile, verbose} = getOpts(options) | ||||||
| 		Object.assign({},  |  | ||||||
| 			FILESTORE_OPTIONS,  |  | ||||||
| 			options ?? {}) |  | ||||||
| 
 | 
 | ||||||
| 	glob(module.path.join(base, '**/*')) | 	glob(module.path.join(base, '**/*')) | ||||||
| 		.on('end', async function(paths){ | 		.on('end', async function(paths){ | ||||||
| @ -986,25 +980,32 @@ async function(base, options){ | |||||||
| 						continue } | 						continue } | ||||||
| 				} } }) } | 				} } }) } | ||||||
| 
 | 
 | ||||||
|  | var backup = | ||||||
|  | module.backup = { | ||||||
|  | 	// XXX backup config???
 | ||||||
|  | 
 | ||||||
| 	//
 | 	//
 | ||||||
| // 	backing(<base>[, <options>])
 | 	// 	.create(<base>[, <options>])
 | ||||||
| // 	backing(<base>, '**'[, <options>])
 | 	// 	.create(<base>, '**'[, <options>])
 | ||||||
| // 	backing(<base>, '**', Date.timeStamp()[, <options>])
 | 	// 	.create(<base>, '**', Date.timeStamp()[, <options>])
 | ||||||
| 	// 		-> <list>
 | 	// 		-> <list>
 | ||||||
| 	//
 | 	//
 | ||||||
| // 	backing(<base>, <path>[, <version>][, <options>])
 | 	// 	.create(<base>, <path>[, <version>][, <options>])
 | ||||||
| 	// 		-> <list>
 | 	// 		-> <list>
 | ||||||
| 	//
 | 	//
 | ||||||
| // 	backing(<base>, <path>, false[, <options>])
 | 	// 	.create(<base>, <path>, false[, <options>])
 | ||||||
| 	// 		-> <list>
 | 	// 		-> <list>
 | ||||||
| 	//
 | 	//
 | ||||||
|  | 	// .create(..) and .restore(..) are completely symmetrical.
 | ||||||
|  | 	//
 | ||||||
| 	// NOTE: backing up ** will include nested backups but will skip the 
 | 	// NOTE: backing up ** will include nested backups but will skip the 
 | ||||||
| // 		root backup...
 | 	// 		root backup but will ignore the root backup dir...
 | ||||||
| // NOTE: currently this ignores only the first element of the options.backup
 | 	//
 | ||||||
| // 		path.
 | 	// XXX since these are *almost* identical in structure, can we reuse one 
 | ||||||
| var backup = | 	// 		to implement the other???
 | ||||||
| module.backup = | 	// 		..or can we implement these in a manner similar to "cp A B" vs. "cp B A"???
 | ||||||
| async function(base, sub='**', version=Date.timeStamp(), options){ | 	create: async function(base, sub='**', version=Date.timeStamp(), options){ | ||||||
|  | 		var that = this | ||||||
| 		if(typeof(sub) == 'object'){ | 		if(typeof(sub) == 'object'){ | ||||||
| 			options = sub | 			options = sub | ||||||
| 			sub = '**' } | 			sub = '**' } | ||||||
| @ -1012,10 +1013,7 @@ async function(base, sub='**', version=Date.timeStamp(), options){ | |||||||
| 			options = version | 			options = version | ||||||
| 			version = Date.timeStamp() } | 			version = Date.timeStamp() } | ||||||
| 		// options...
 | 		// options...
 | ||||||
| 	var {index, backup, verbose, recursive, cleanBackup} = | 		var {index, backup, verbose, recursive, cleanBackup, __batch} = options = getOpts(options) | ||||||
| 		Object.assign({},  |  | ||||||
| 			FILESTORE_OPTIONS,  |  | ||||||
| 			options ?? {}) |  | ||||||
| 		recursive = recursive ?? false | 		recursive = recursive ?? false | ||||||
| 
 | 
 | ||||||
| 		var _backup = backup =  | 		var _backup = backup =  | ||||||
| @ -1025,22 +1023,20 @@ async function(base, sub='**', version=Date.timeStamp(), options){ | |||||||
| 		backup =  | 		backup =  | ||||||
| 			module.path.join( | 			module.path.join( | ||||||
| 				base, | 				base, | ||||||
| 			module.path.relative(sub, backup)) | 				module.path.relative(module.path.dirname(sub), backup)) | ||||||
| 
 | 
 | ||||||
| 		// ** or * -- backup each file in path...
 | 		// ** or * -- backup each file in path...
 | ||||||
| 		if(/[\\\/]*\*\*?$/.test(sub)){ | 		if(/[\\\/]*\*\*?$/.test(sub)){ | ||||||
|  | 			if(sub.endsWith('**')){ | ||||||
|  | 				options.recursive = true } | ||||||
|  | 			options.__batch = true | ||||||
|  | 
 | ||||||
| 			if(cleanBackup  | 			if(cleanBackup  | ||||||
| 					&& fs.existsSync(backup)){ | 					&& fs.existsSync(backup)){ | ||||||
| 				verbose | 				verbose | ||||||
| 				&& console.log('backup(..): cleaning:', backup) | 					&& console.log('.create(..): cleaning:', backup) | ||||||
| 				await fs.promises.rm(backup, {recursive: true}) } | 				await fs.promises.rm(backup, {recursive: true}) } | ||||||
| 		if(sub.endsWith('**')){ |  | ||||||
| 			options = { |  | ||||||
| 				...(options ?? {}),  |  | ||||||
| 				recursive: true, |  | ||||||
| 			} } |  | ||||||
| 			sub = sub.replace(/[\\\/]*\*\*?$/, '') | 			sub = sub.replace(/[\\\/]*\*\*?$/, '') | ||||||
| 		// XXX should we ignore only the first element (current) or the sub-path???
 |  | ||||||
| 			var b = module.path.split(_backup) | 			var b = module.path.split(_backup) | ||||||
| 				.filter(function(p){  | 				.filter(function(p){  | ||||||
| 					return p != '' }) | 					return p != '' }) | ||||||
| @ -1051,7 +1047,7 @@ async function(base, sub='**', version=Date.timeStamp(), options){ | |||||||
| 				.filter(function(file){ | 				.filter(function(file){ | ||||||
| 					return !file.includes(b) }) | 					return !file.includes(b) }) | ||||||
| 				.map(async function(file){ | 				.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...
 | 				// keep only the paths we backed up...
 | ||||||
| 				.filter(function(e){  | 				.filter(function(e){  | ||||||
| 					return !!e })  | 					return !!e })  | ||||||
| @ -1064,38 +1060,162 @@ async function(base, sub='**', version=Date.timeStamp(), options){ | |||||||
| 			// nothing to backup...
 | 			// nothing to backup...
 | ||||||
| 			if(!fs.existsSync(target)){ | 			if(!fs.existsSync(target)){ | ||||||
| 				verbose | 				verbose | ||||||
| 				&& console.log('backup(..): target does not exist:', target) | 					&& console.log('.create(..): target does not exist:', target) | ||||||
| 				return } | 				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 ? | 			var to = full ? | ||||||
| 				backup +'/'+ sub | 				backup +'/'+ sub | ||||||
| 				: backup +'/'+ module.path.basename(sub) | 				: backup +'/'+ module.path.basename(sub) | ||||||
| 			var todir = module.path.dirname(to) | 			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 | 						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.mkdir(todir, {recursive: true})  | ||||||
| 			await fs.promises.cp(target, to, {force: true, recursive}) | 			await fs.promises.cp(target, to, {force: true, recursive}) | ||||||
| 		return to } } | 			return to } }, | ||||||
|  | 	restore: async function(base, sub, version, options){ | ||||||
|  | 		var that = this | ||||||
| 		// XXX
 | 		// XXX
 | ||||||
| var restore = | 		var {index, backup, verbose, recursive, preBackup, __batch} = options = getOpts(options) | ||||||
| module.restore = | 		recursive = recursive ?? false | ||||||
| async function(base, sub, version, options){ | 
 | ||||||
|  | 		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
 | ||||||
|  | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
 | ||||||
|  | 
 | ||||||
| // XXX might be a good idea to support ro mode on top level explicitly...
 | // 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)...
 | // XXX add monitor API + cache + live mode (auto on when lock detected)...
 | ||||||
| var FileStoreRO = | var FileStoreRO = | ||||||
| @ -1175,7 +1295,7 @@ module.FileStore = { | |||||||
| 	// XXX should these be generic???
 | 	// XXX should these be generic???
 | ||||||
| 	// XXX add versioning...
 | 	// XXX add versioning...
 | ||||||
| 	backup: async function(path='**', options={}){ | 	backup: async function(path='**', options={}){ | ||||||
| 		return module.backup( | 		return backup.create( | ||||||
| 			this.__path__, path,  | 			this.__path__, path,  | ||||||
| 			{ | 			{ | ||||||
| 				index: this.__directory_text__, | 				index: this.__directory_text__, | ||||||
| @ -1183,7 +1303,7 @@ module.FileStore = { | |||||||
| 				...options, | 				...options, | ||||||
| 			}) }, | 			}) }, | ||||||
| 	restore: async function(path='**', options={}){ | 	restore: async function(path='**', options={}){ | ||||||
| 		return module.restore( | 		return backup.restore( | ||||||
| 			this.__path__, path,  | 			this.__path__, path,  | ||||||
| 			{ | 			{ | ||||||
| 				index: this.__directory_text__, | 				index: this.__directory_text__, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user