mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-10-31 02:50:08 +00:00 
			
		
		
		
	backups working...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									62a6d3de07
								
							
						
					
					
						commit
						8adedd5724
					
				
							
								
								
									
										260
									
								
								pwiki2.js
									
									
									
									
									
								
							
							
						
						
									
										260
									
								
								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 } | ||||
| 				} } }) } | ||||
| 
 | ||||
| //
 | ||||
| // 	backing(<base>[, <options>])
 | ||||
| // 	backing(<base>, '**'[, <options>])
 | ||||
| // 	backing(<base>, '**', Date.timeStamp()[, <options>])
 | ||||
| // 		-> <list>
 | ||||
| //
 | ||||
| // 	backing(<base>, <path>[, <version>][, <options>])
 | ||||
| // 		-> <list>
 | ||||
| //
 | ||||
| // 	backing(<base>, <path>, false[, <options>])
 | ||||
| // 		-> <list>
 | ||||
| //
 | ||||
| // 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){ | ||||
| module.backup = { | ||||
| 	// XXX backup config???
 | ||||
| 
 | ||||
| 	//
 | ||||
| 	// 	.create(<base>[, <options>])
 | ||||
| 	// 	.create(<base>, '**'[, <options>])
 | ||||
| 	// 	.create(<base>, '**', Date.timeStamp()[, <options>])
 | ||||
| 	// 		-> <list>
 | ||||
| 	//
 | ||||
| 	// 	.create(<base>, <path>[, <version>][, <options>])
 | ||||
| 	// 		-> <list>
 | ||||
| 	//
 | ||||
| 	// 	.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 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 } } | ||||
| // XXX
 | ||||
| var restore = | ||||
| module.restore = | ||||
| async function(base, sub, version, options){ | ||||
| 			return to } }, | ||||
| 	restore: async function(base, sub, version, options){ | ||||
| 		var that = this | ||||
| 		// XXX
 | ||||
| 		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