Removed Search-Index and LokiJS, replaced with Mongo + Updated VueJs
This commit is contained in:
		| @@ -1,8 +1,7 @@ | |||||||
| language: node_js | language: node_js | ||||||
| node_js: | node_js: | ||||||
| - '6' | - '6' | ||||||
| - '5' | - '4' | ||||||
| - '4.4' |  | ||||||
| addons: | addons: | ||||||
|   apt: |   apt: | ||||||
|     sources: |     sources: | ||||||
|   | |||||||
							
								
								
									
										82
									
								
								agent.js
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								agent.js
									
									
									
									
									
								
							| @@ -25,18 +25,22 @@ if(!process.argv[2] || process.argv[2].length !== 40) { | |||||||
| global.WSInternalKey = process.argv[2]; | global.WSInternalKey = process.argv[2]; | ||||||
|  |  | ||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
| // Load modules | // Load global modules | ||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
|  |  | ||||||
| winston.info('[AGENT] Background Agent is initializing...'); | winston.info('[AGENT] Background Agent is initializing...'); | ||||||
|  |  | ||||||
| var appconfig = require('./models/config')('./config.yml'); | var appconfig = require('./models/config')('./config.yml'); | ||||||
| global.lcdata = require('./models/localdata').init(appconfig, 'agent'); | global.db = require('./models/mongo').init(appconfig); | ||||||
| var upl = require('./models/uploads').init(appconfig); | global.upl = require('./models/agent/uploads').init(appconfig); | ||||||
|  |  | ||||||
| global.git = require('./models/git').init(appconfig); | global.git = require('./models/git').init(appconfig); | ||||||
| global.entries = require('./models/entries').init(appconfig); | global.entries = require('./models/entries').init(appconfig); | ||||||
| global.mark = require('./models/markdown'); | global.mark = require('./models/markdown'); | ||||||
|  | global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 }); | ||||||
|  |  | ||||||
|  | // ---------------------------------------- | ||||||
|  | // Load modules | ||||||
|  | // ---------------------------------------- | ||||||
|  |  | ||||||
| var _ = require('lodash'); | var _ = require('lodash'); | ||||||
| var moment = require('moment'); | var moment = require('moment'); | ||||||
| @@ -45,10 +49,6 @@ var fs = Promise.promisifyAll(require("fs-extra")); | |||||||
| var path = require('path'); | var path = require('path'); | ||||||
| var cron = require('cron').CronJob; | var cron = require('cron').CronJob; | ||||||
|  |  | ||||||
| global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 }); |  | ||||||
|  |  | ||||||
| const mimeImgTypes = ['image/png', 'image/jpg'] |  | ||||||
|  |  | ||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
| // Start Cron | // Start Cron | ||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
| @@ -72,16 +72,17 @@ var job = new cron({ | |||||||
| 		// Prepare async job collector | 		// Prepare async job collector | ||||||
|  |  | ||||||
| 		let jobs = []; | 		let jobs = []; | ||||||
| 		let repoPath = path.resolve(ROOTPATH, appconfig.datadir.repo); | 		let repoPath = path.resolve(ROOTPATH, appconfig.paths.repo); | ||||||
| 		let dataPath = path.resolve(ROOTPATH, appconfig.datadir.db); | 		let dataPath = path.resolve(ROOTPATH, appconfig.paths.data); | ||||||
| 		let uploadsPath = path.join(repoPath, 'uploads'); | 		let uploadsPath = path.join(repoPath, 'uploads'); | ||||||
|  | 		let uploadsTempPath = path.join(dataPath, 'temp-upload'); | ||||||
|  |  | ||||||
| 		// ---------------------------------------- | 		// ---------------------------------------- | ||||||
| 		// Compile Jobs | 		// REGULAR JOBS | ||||||
| 		// ---------------------------------------- | 		// ---------------------------------------- | ||||||
|  |  | ||||||
| 		//***************************************** | 		//***************************************** | ||||||
| 		//-> Resync with Git remote | 		//-> Sync with Git remote | ||||||
| 		//***************************************** | 		//***************************************** | ||||||
|  |  | ||||||
| 		jobs.push(git.onReady.then(() => { | 		jobs.push(git.onReady.then(() => { | ||||||
| @@ -143,51 +144,30 @@ var job = new cron({ | |||||||
| 		})); | 		})); | ||||||
|  |  | ||||||
| 		//***************************************** | 		//***************************************** | ||||||
| 		//-> Refresh uploads data | 		//-> Clear failed temporary upload files | ||||||
| 		//***************************************** | 		//***************************************** | ||||||
|  |  | ||||||
| 		jobs.push(fs.readdirAsync(uploadsPath).then((ls) => { | 		jobs.push( | ||||||
|  | 			fs.readdirAsync(uploadsTempPath).then((ls) => { | ||||||
|  |  | ||||||
|  | 				let fifteenAgo = moment().subtract(15, 'minutes'); | ||||||
|  |  | ||||||
| 				return Promise.map(ls, (f) => { | 				return Promise.map(ls, (f) => { | ||||||
| 				return fs.statAsync(path.join(uploadsPath, f)).then((s) => { return { filename: f, stat: s }; }); | 					return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s }; }); | ||||||
| 			}).filter((s) => { return s.stat.isDirectory(); }).then((arrDirs) => { | 				}).filter((s) => { return s.stat.isFile(); }).then((arrFiles) => { | ||||||
|  | 					return Promise.map(arrFiles, (f) => { | ||||||
| 				let folderNames = _.map(arrDirs, 'filename'); |  | ||||||
| 				folderNames.unshift(''); |  | ||||||
|  |  | ||||||
| 				ws.emit('uploadsSetFolders', { |  | ||||||
| 					auth: WSInternalKey, |  | ||||||
| 					content: folderNames |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				let allFiles = []; |  | ||||||
|  |  | ||||||
| 				// Travel each directory |  | ||||||
|  |  | ||||||
| 				return Promise.map(folderNames, (fldName) => { |  | ||||||
| 					let fldPath = path.join(uploadsPath, fldName); |  | ||||||
| 					return fs.readdirAsync(fldPath).then((fList) => { |  | ||||||
| 						return Promise.map(fList, (f) => { |  | ||||||
| 							return upl.processFile(fldName, f).then((mData) => { |  | ||||||
| 								if(mData) { |  | ||||||
| 									allFiles.push(mData); |  | ||||||
| 								} |  | ||||||
| 							}); |  | ||||||
| 						}, {concurrency: 3}); |  | ||||||
| 					}); |  | ||||||
| 				}, {concurrency: 1}).finally(() => { |  | ||||||
|  |  | ||||||
| 					ws.emit('uploadsSetFiles', { |  | ||||||
| 						auth: WSInternalKey, |  | ||||||
| 						content: allFiles |  | ||||||
| 					}); |  | ||||||
|  |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
|  | 						if(moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) { | ||||||
|  | 							return fs.unlinkAsync(path.join(uploadsTempPath, f.filename)); | ||||||
|  | 						} else { | ||||||
| 							return true; | 							return true; | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 					}); | ||||||
| 				}); | 				}); | ||||||
|  |  | ||||||
| 		})); | 			}) | ||||||
|  | 		); | ||||||
|  |  | ||||||
| 		// ---------------------------------------- | 		// ---------------------------------------- | ||||||
| 		// Run | 		// Run | ||||||
| @@ -198,9 +178,11 @@ var job = new cron({ | |||||||
|  |  | ||||||
| 			if(!jobUplWatchStarted) { | 			if(!jobUplWatchStarted) { | ||||||
| 				jobUplWatchStarted = true; | 				jobUplWatchStarted = true; | ||||||
| 				upl.watch(); | 				upl.initialScan(); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			return true; | ||||||
|  |  | ||||||
| 		}).catch((err) => { | 		}).catch((err) => { | ||||||
| 			winston.error('[AGENT] One or more jobs have failed: ', err); | 			winston.error('[AGENT] One or more jobs have failed: ', err); | ||||||
| 		}).finally(() => { | 		}).finally(() => { | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -104,9 +104,9 @@ class Alerts { | |||||||
|  |  | ||||||
| 		if(nAlertIdx >= 0 && nAlert) { | 		if(nAlertIdx >= 0 && nAlert) { | ||||||
| 			nAlert.class += ' exit'; | 			nAlert.class += ' exit'; | ||||||
| 			self.mdl.children.$set(nAlertIdx, nAlert); | 			Vue.set(self.mdl.children, nAlertIdx, nAlert); | ||||||
| 			_.delay(() => { | 			_.delay(() => { | ||||||
| 				self.mdl.children.$remove(nAlert); | 				self.mdl.children.splice(nAlertIdx, 1); | ||||||
| 			}, 500); | 			}, 500); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,11 +6,6 @@ jQuery( document ).ready(function( $ ) { | |||||||
|  |  | ||||||
| 		$('#search-input').focus(); | 		$('#search-input').focus(); | ||||||
|  |  | ||||||
| 		Vue.transition('slide', { |  | ||||||
| 			enterClass: 'slideInDown', |  | ||||||
| 			leaveClass: 'fadeOutUp' |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		$('.searchresults').css('display', 'block'); | 		$('.searchresults').css('display', 'block'); | ||||||
|  |  | ||||||
| 		var vueHeader = new Vue({ | 		var vueHeader = new Vue({ | ||||||
| @@ -47,8 +42,8 @@ jQuery( document ).ready(function( $ ) { | |||||||
| 				}, | 				}, | ||||||
| 				searchmoveidx: (val, oldVal) => { | 				searchmoveidx: (val, oldVal) => { | ||||||
| 					if(val > 0) { | 					if(val > 0) { | ||||||
| 						vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1].document) ? | 						vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ? | ||||||
| 																				'res.' + vueHeader.searchmovearr[val - 1].document.entryPath : | 																				'res.' + vueHeader.searchmovearr[val - 1]._id : | ||||||
| 																				'sug.' + vueHeader.searchmovearr[val - 1]; | 																				'sug.' + vueHeader.searchmovearr[val - 1]; | ||||||
| 					} else { | 					} else { | ||||||
| 						vueHeader.searchmovekey = ''; | 						vueHeader.searchmovekey = ''; | ||||||
| @@ -66,8 +61,8 @@ jQuery( document ).ready(function( $ ) { | |||||||
| 					if(vueHeader.searchmoveidx < 1) { return; } | 					if(vueHeader.searchmoveidx < 1) { return; } | ||||||
| 					let i = vueHeader.searchmoveidx - 1; | 					let i = vueHeader.searchmoveidx - 1; | ||||||
|  |  | ||||||
| 					if(vueHeader.searchmovearr[i].document) { | 					if(vueHeader.searchmovearr[i]) { | ||||||
| 						window.location.assign('/' + vueHeader.searchmovearr[i].document.entryPath); | 						window.location.assign('/' + vueHeader.searchmovearr[i]._id); | ||||||
| 					} else { | 					} else { | ||||||
| 						vueHeader.searchq = vueHeader.searchmovearr[i]; | 						vueHeader.searchq = vueHeader.searchmovearr[i]; | ||||||
| 					} | 					} | ||||||
|   | |||||||
| @@ -34,9 +34,15 @@ wsPort: 8080 | |||||||
| # Data Directories | # Data Directories | ||||||
| # --------------------------------------------------------------------- | # --------------------------------------------------------------------- | ||||||
|  |  | ||||||
| datadir: | paths: | ||||||
|   repo: ./repo |   repo: ./repo | ||||||
|   db: ./data |   data: ./data | ||||||
|  |  | ||||||
|  | # --------------------------------------------------------------------- | ||||||
|  | # Database Connection String | ||||||
|  | # --------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | db: mongodb://localhost:27017/wiki | ||||||
|  |  | ||||||
| # --------------------------------------------------------------------- | # --------------------------------------------------------------------- | ||||||
| # Git Connection Info | # Git Connection Info | ||||||
|   | |||||||
| @@ -2,14 +2,16 @@ var express = require('express'); | |||||||
| var router = express.Router(); | var router = express.Router(); | ||||||
| var passport = require('passport'); | var passport = require('passport'); | ||||||
| var ExpressBrute = require('express-brute'); | var ExpressBrute = require('express-brute'); | ||||||
| var ExpressBruteLokiStore = require('express-brute-loki'); | var ExpressBruteMongoStore = require('express-brute-mongo'); | ||||||
| var moment = require('moment'); | var moment = require('moment'); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Setup Express-Brute |  * Setup Express-Brute | ||||||
|  */ |  */ | ||||||
| var EBstore = new ExpressBruteLokiStore({ | var EBstore = new ExpressBruteMongoStore((ready) => { | ||||||
|     path: './data/brute.db' | 	db.onReady.then(() => { | ||||||
|  | 		ready(db.connection.collection('bruteforce-store')); | ||||||
|  | 	}); | ||||||
| }); | }); | ||||||
| var bruteforce = new ExpressBrute(EBstore, { | var bruteforce = new ExpressBrute(EBstore, { | ||||||
| 	freeRetries: 5, | 	freeRetries: 5, | ||||||
|   | |||||||
| @@ -40,7 +40,15 @@ router.get('/t/*', (req, res, next) => { | |||||||
| router.post('/img', lcdata.uploadImgHandler, (req, res, next) => { | router.post('/img', lcdata.uploadImgHandler, (req, res, next) => { | ||||||
|  |  | ||||||
| 	let destFolder = _.chain(req.body.folder).trim().toLower().value(); | 	let destFolder = _.chain(req.body.folder).trim().toLower().value(); | ||||||
| 	let destFolderPath = lcdata.validateUploadsFolder(destFolder); |  | ||||||
|  | 	ws.emit('uploadsValidateFolder', { | ||||||
|  | 		auth: WSInternalKey, | ||||||
|  | 		content: destFolder | ||||||
|  | 	}, (destFolderPath) => { | ||||||
|  | 		 | ||||||
|  | 		if(!destFolderPath) { | ||||||
|  | 			return res.json({ ok: false, msg: 'Invalid Folder' }); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		Promise.map(req.files, (f) => { | 		Promise.map(req.files, (f) => { | ||||||
|  |  | ||||||
| @@ -96,6 +104,8 @@ router.post('/img', lcdata.uploadImgHandler, (req, res, next) => { | |||||||
|  |  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | }); | ||||||
|  |  | ||||||
| router.get('/*', (req, res, next) => { | router.get('/*', (req, res, next) => { | ||||||
|  |  | ||||||
| 	let fileName = req.params[0]; | 	let fileName = req.params[0]; | ||||||
|   | |||||||
| @@ -78,7 +78,7 @@ var paths = { | |||||||
| /** | /** | ||||||
|  * TASK - Starts server in development mode |  * TASK - Starts server in development mode | ||||||
|  */ |  */ | ||||||
| gulp.task('server', ['scripts', 'css', 'fonts'], function() { | gulp.task('server', ['scripts', 'css'/*, 'fonts'*/], function() { | ||||||
| 	nodemon({ | 	nodemon({ | ||||||
| 		script: './server', | 		script: './server', | ||||||
| 		ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'], | 		ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'], | ||||||
|   | |||||||
| @@ -32,8 +32,8 @@ module.exports = { | |||||||
| 
 | 
 | ||||||
| 		let self = this; | 		let self = this; | ||||||
| 
 | 
 | ||||||
| 		self._uploadsPath = path.resolve(ROOTPATH, appconfig.datadir.repo, 'uploads'); | 		self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads'); | ||||||
| 		self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'thumbs'); | 		self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs'); | ||||||
| 
 | 
 | ||||||
| 		return self; | 		return self; | ||||||
| 
 | 
 | ||||||
| @@ -51,20 +51,127 @@ module.exports = { | |||||||
| 			awaitWriteFinish: true | 			awaitWriteFinish: true | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
|  | 		//-> Add new upload file
 | ||||||
|  | 
 | ||||||
| 		self._watcher.on('add', (p) => { | 		self._watcher.on('add', (p) => { | ||||||
| 
 | 
 | ||||||
| 			let pInfo = lcdata.parseUploadsRelPath(p); | 			let pInfo = self.parseUploadsRelPath(p); | ||||||
| 			return self.processFile(pInfo.folder, pInfo.filename).then((mData) => { | 			return self.processFile(pInfo.folder, pInfo.filename).then((mData) => { | ||||||
| 				ws.emit('uploadsAddFiles', { | 				ws.emit('uploadsAddFiles', { | ||||||
| 					auth: WSInternalKey, | 					auth: WSInternalKey, | ||||||
| 					content: mData | 					content: mData | ||||||
| 				}); | 				}); | ||||||
| 			}).then(() => { | 			}).then(() => { | ||||||
| 				return git.commitUploads(); | 				return git.commitUploads('Uploaded ' + p); | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
|  | 		//-> Remove upload file
 | ||||||
|  | 
 | ||||||
|  | 		self._watcher.on('unlink', (p) => { | ||||||
|  | 
 | ||||||
|  | 			let pInfo = self.parseUploadsRelPath(p); | ||||||
|  | 			return self.deleteFile(pInfo.folder, pInfo.filename).then((uID) => { | ||||||
|  | 				ws.emit('uploadsRemoveFiles', { | ||||||
|  | 					auth: WSInternalKey, | ||||||
|  | 					content: uID | ||||||
|  | 				}); | ||||||
|  | 			}).then(() => { | ||||||
|  | 				return git.commitUploads('Deleted ' + p); | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Initial Uploads scan | ||||||
|  | 	 * | ||||||
|  | 	 * @return     {Promise<Void>}  Promise of the scan operation | ||||||
|  | 	 */ | ||||||
|  | 	initialScan() { | ||||||
|  | 
 | ||||||
|  | 		let self = this; | ||||||
|  | 
 | ||||||
|  | 		return fs.readdirAsync(self._uploadsPath).then((ls) => { | ||||||
|  | 
 | ||||||
|  | 			// Get all folders
 | ||||||
|  | 
 | ||||||
|  | 			return Promise.map(ls, (f) => { | ||||||
|  | 				return fs.statAsync(path.join(self._uploadsPath, f)).then((s) => { return { filename: f, stat: s }; }); | ||||||
|  | 			}).filter((s) => { return s.stat.isDirectory(); }).then((arrDirs) => { | ||||||
|  | 
 | ||||||
|  | 				let folderNames = _.map(arrDirs, 'filename'); | ||||||
|  | 				folderNames.unshift(''); | ||||||
|  | 
 | ||||||
|  | 				// Add folders to DB
 | ||||||
|  | 
 | ||||||
|  | 				return db.UplFolder.remove({}).then(() => { | ||||||
|  | 					return db.UplFolder.insertMany(_.map(folderNames, (f) => { | ||||||
|  | 						return { name: f }; | ||||||
|  | 					})); | ||||||
|  | 				}).then(() => { | ||||||
|  | 
 | ||||||
|  | 					// Travel each directory and scan files
 | ||||||
|  | 
 | ||||||
|  | 					let allFiles = []; | ||||||
|  | 
 | ||||||
|  | 					return Promise.map(folderNames, (fldName) => { | ||||||
|  | 
 | ||||||
|  | 						let fldPath = path.join(self._uploadsPath, fldName); | ||||||
|  | 						return fs.readdirAsync(fldPath).then((fList) => { | ||||||
|  | 							return Promise.map(fList, (f) => { | ||||||
|  | 								return upl.processFile(fldName, f).then((mData) => { | ||||||
|  | 									if(mData) { | ||||||
|  | 										allFiles.push(mData); | ||||||
|  | 									} | ||||||
|  | 									return true; | ||||||
|  | 								}); | ||||||
|  | 							}, {concurrency: 3}); | ||||||
|  | 						}); | ||||||
|  | 					}, {concurrency: 1}).finally(() => { | ||||||
|  | 
 | ||||||
|  | 						// Add files to DB
 | ||||||
|  | 
 | ||||||
|  | 						return db.UplFile.remove({}).then(() => { | ||||||
|  | 							if(_.isArray(allFiles) && allFiles.length > 0) { | ||||||
|  | 								return db.UplFile.insertMany(allFiles); | ||||||
|  | 							} else { | ||||||
|  | 								return true; | ||||||
|  | 							} | ||||||
|  | 						}); | ||||||
|  | 
 | ||||||
|  | 					}); | ||||||
|  | 
 | ||||||
|  | 				}); | ||||||
|  | 				 | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 		}).then(() => { | ||||||
|  | 
 | ||||||
|  | 			// Watch for new changes
 | ||||||
|  | 
 | ||||||
|  | 			return upl.watch(); | ||||||
|  | 
 | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Parse relative Uploads path | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}  f       Relative Uploads path | ||||||
|  | 	 * @return     {Object}  Parsed path (folder and filename) | ||||||
|  | 	 */ | ||||||
|  | 	parseUploadsRelPath(f) { | ||||||
|  | 
 | ||||||
|  | 		let fObj = path.parse(f); | ||||||
|  | 		return { | ||||||
|  | 			folder: fObj.dir, | ||||||
|  | 			filename: fObj.base | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	processFile(fldName, f) { | 	processFile(fldName, f) { | ||||||
| @@ -88,20 +195,21 @@ module.exports = { | |||||||
| 
 | 
 | ||||||
| 			if(s.size < 3145728) { // ignore files larger than 3MB
 | 			if(s.size < 3145728) { // ignore files larger than 3MB
 | ||||||
| 				if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { | 				if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { | ||||||
| 					return self.getImageMetadata(fPath).then((mData) => { | 					return self.getImageMetadata(fPath).then((mImgData) => { | ||||||
| 
 | 
 | ||||||
| 						let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png')); | 						let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png')); | ||||||
| 						let cacheThumbnailPathStr = path.format(cacheThumbnailPath); | 						let cacheThumbnailPathStr = path.format(cacheThumbnailPath); | ||||||
| 
 | 
 | ||||||
| 						mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']); | 						let mData = { | ||||||
| 						mData.uid = fUid; | 							_id: fUid, | ||||||
| 						mData.category = 'image'; | 							category: 'image', | ||||||
| 						mData.mime = mimeInfo.mime; | 							mime: mimeInfo.mime, | ||||||
| 						mData.folder = fldName; | 							extra: _.pick(mImgData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']), | ||||||
| 						mData.filename = f; | 							folder: null, | ||||||
| 						mData.basename = fPathObj.name; | 							filename: f, | ||||||
| 						mData.filesize = s.size; | 							basename: fPathObj.name, | ||||||
| 						mData.uploadedOn = moment().utc(); | 							filesize: s.size | ||||||
|  | 						} | ||||||
| 
 | 
 | ||||||
| 						// Generate thumbnail
 | 						// Generate thumbnail
 | ||||||
| 
 | 
 | ||||||
| @@ -124,14 +232,13 @@ module.exports = { | |||||||
| 			// Other Files
 | 			// Other Files
 | ||||||
| 			 | 			 | ||||||
| 			return { | 			return { | ||||||
| 				uid: fUid, | 				_id: fUid, | ||||||
| 				category: 'file', | 				category: 'binary', | ||||||
| 				mime: mimeInfo.mime, | 				mime: mimeInfo.mime, | ||||||
| 				folder: fldName, | 				folder: fldName, | ||||||
| 				filename: f, | 				filename: f, | ||||||
| 				basename: fPathObj.name, | 				basename: fPathObj.name, | ||||||
| 				filesize: s.size, | 				filesize: s.size | ||||||
| 				uploadedOn: moment().utc() |  | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 		}); | 		}); | ||||||
| @@ -45,7 +45,7 @@ module.exports = function(passport, appconfig) { | |||||||
|  |  | ||||||
|     db.onReady.then(() => { |     db.onReady.then(() => { | ||||||
|  |  | ||||||
|         if(db.User.count() < 1) { |         /*if(db.User.count() < 1) { | ||||||
|             winston.info('No administrator account found. Creating a new one...'); |             winston.info('No administrator account found. Creating a new one...'); | ||||||
|             if(db.User.insert({ |             if(db.User.insert({ | ||||||
|                 email: appconfig.admin, |                 email: appconfig.admin, | ||||||
| @@ -57,7 +57,7 @@ module.exports = function(passport, appconfig) { | |||||||
|             } else { |             } else { | ||||||
|                 winston.error('An error occured while creating administrator account: '); |                 winston.error('An error occured while creating administrator account: '); | ||||||
|             } |             } | ||||||
|         } |         }*/ | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								models/db.js
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								models/db.js
									
									
									
									
									
								
							| @@ -1,52 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| var loki = require('lokijs'), |  | ||||||
| 	 fs   = require("fs"), |  | ||||||
| 	 path = require("path"), |  | ||||||
| 	 Promise = require('bluebird'), |  | ||||||
| 	 _ = require('lodash'); |  | ||||||
|  |  | ||||||
| var cols = ['User', 'Entry']; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Loki.js module |  | ||||||
|  * |  | ||||||
|  * @param      {Object}  appconfig  Application config |  | ||||||
|  * @return     {Object}  LokiJS instance |  | ||||||
|  */ |  | ||||||
| module.exports = function(appconfig) { |  | ||||||
|  |  | ||||||
| 	let dbReadyResolve; |  | ||||||
| 	let dbReady = new Promise((resolve, reject) => { |  | ||||||
| 		dbReadyResolve = resolve; |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	// Initialize Loki.js |  | ||||||
|  |  | ||||||
| 	let dbModel = { |  | ||||||
| 		Store: new loki(path.join(appconfig.datadir.db, 'app.db'), { |  | ||||||
| 			env: 'NODEJS', |  | ||||||
| 			autosave: true, |  | ||||||
| 			autosaveInterval: 5000 |  | ||||||
| 		}), |  | ||||||
| 		onReady: dbReady |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	// Load Models |  | ||||||
|  |  | ||||||
| 	dbModel.Store.loadDatabase({}, () => { |  | ||||||
|  |  | ||||||
| 		_.forEach(cols, (col) => { |  | ||||||
| 			dbModel[col] = dbModel.Store.getCollection(col); |  | ||||||
| 			if(!dbModel[col]) { |  | ||||||
| 				dbModel[col] = dbModel.Store.addCollection(col); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		dbReadyResolve(); |  | ||||||
|  |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	return dbModel; |  | ||||||
|  |  | ||||||
| }; |  | ||||||
							
								
								
									
										54
									
								
								models/db/entry.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								models/db/entry.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const modb = require('mongoose'), | ||||||
|  | 			Promise = require('bluebird'), | ||||||
|  | 			_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Entry schema | ||||||
|  |  * | ||||||
|  |  * @type       {<Mongoose.Schema>} | ||||||
|  |  */ | ||||||
|  | var entrySchema = modb.Schema({ | ||||||
|  |  | ||||||
|  | 	_id: String, | ||||||
|  |  | ||||||
|  |   title: { | ||||||
|  |     type: String, | ||||||
|  |     required: true, | ||||||
|  |     minlength: 2 | ||||||
|  |   }, | ||||||
|  |   subtitle: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, | ||||||
|  |   parent: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, | ||||||
|  |   content: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | }, | ||||||
|  | { | ||||||
|  | 	timestamps: {} | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | entrySchema.index({ | ||||||
|  |   _id: 'text', | ||||||
|  |   title: 'text', | ||||||
|  |   subtitle: 'text', | ||||||
|  |   content: 'text' | ||||||
|  | }, { | ||||||
|  |   weights: { | ||||||
|  |     _id: 3, | ||||||
|  |     title: 10, | ||||||
|  |     subtitle: 5, | ||||||
|  |     content: 1 | ||||||
|  |   }, | ||||||
|  |   name: 'EntriesTextIndex' | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | module.exports = modb.model('Entry', entrySchema); | ||||||
							
								
								
									
										51
									
								
								models/db/upl-file.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								models/db/upl-file.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const modb = require('mongoose'), | ||||||
|  | 			Promise = require('bluebird'), | ||||||
|  | 			_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Upload File schema | ||||||
|  |  * | ||||||
|  |  * @type       {<Mongoose.Schema>} | ||||||
|  |  */ | ||||||
|  | var uplFileSchema = modb.Schema({ | ||||||
|  |  | ||||||
|  | 	_id: String, | ||||||
|  |  | ||||||
|  |   category: { | ||||||
|  |     type: String, | ||||||
|  |     required: true, | ||||||
|  |     default: 'binary' | ||||||
|  |   }, | ||||||
|  |   mime: { | ||||||
|  |     type: String, | ||||||
|  |     required: true, | ||||||
|  |     default: 'application/octet-stream' | ||||||
|  |   }, | ||||||
|  |   extra: { | ||||||
|  |     type: Object | ||||||
|  |   }, | ||||||
|  |   folder: { | ||||||
|  |     type: String, | ||||||
|  |     ref: 'UplFolder' | ||||||
|  |   }, | ||||||
|  |   filename: { | ||||||
|  |     type: String, | ||||||
|  |     required: true | ||||||
|  |   }, | ||||||
|  |   basename: { | ||||||
|  |     type: String, | ||||||
|  |     required: true | ||||||
|  |   }, | ||||||
|  |   filesize: { | ||||||
|  |     type: Number, | ||||||
|  |     required: true | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | }, | ||||||
|  | { | ||||||
|  | 	timestamps: {} | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | module.exports = modb.model('UplFile', uplFileSchema); | ||||||
							
								
								
									
										23
									
								
								models/db/upl-folder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								models/db/upl-folder.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const modb = require('mongoose'), | ||||||
|  | 			Promise = require('bluebird'), | ||||||
|  | 			_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Upload Folder schema | ||||||
|  |  * | ||||||
|  |  * @type       {<Mongoose.Schema>} | ||||||
|  |  */ | ||||||
|  | var uplFolderSchema = modb.Schema({ | ||||||
|  |  | ||||||
|  |   name: { | ||||||
|  |     type: String | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | }, | ||||||
|  | { | ||||||
|  | 	timestamps: {} | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | module.exports = modb.model('UplFolder', uplFolderSchema); | ||||||
							
								
								
									
										24
									
								
								models/db/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								models/db/user.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const modb = require('mongoose'), | ||||||
|  | 			Promise = require('bluebird'), | ||||||
|  | 			_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Region schema | ||||||
|  |  * | ||||||
|  |  * @type       {<Mongoose.Schema>} | ||||||
|  |  */ | ||||||
|  | var userSchema = modb.Schema({ | ||||||
|  |  | ||||||
|  | 	email: { | ||||||
|  | 		type: String, | ||||||
|  | 		required: true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | }, | ||||||
|  | { | ||||||
|  | 	timestamps: {} | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | module.exports = modb.model('User', userSchema); | ||||||
| @@ -25,8 +25,8 @@ module.exports = { | |||||||
|  |  | ||||||
| 		let self = this; | 		let self = this; | ||||||
|  |  | ||||||
| 		self._repoPath = path.resolve(ROOTPATH, appconfig.datadir.repo); | 		self._repoPath = path.resolve(ROOTPATH, appconfig.paths.repo); | ||||||
| 		self._cachePath = path.resolve(ROOTPATH, appconfig.datadir.db, 'cache'); | 		self._cachePath = path.resolve(ROOTPATH, appconfig.paths.data, 'cache'); | ||||||
|  |  | ||||||
| 		return self; | 		return self; | ||||||
|  |  | ||||||
| @@ -321,11 +321,13 @@ module.exports = { | |||||||
| 				text: mark.removeMarkdown(pageData.markdown) | 				text: mark.removeMarkdown(pageData.markdown) | ||||||
| 			}; | 			}; | ||||||
| 		}).then((content) => { | 		}).then((content) => { | ||||||
| 			ws.emit('searchAdd', { | 			return db.Entry.create({ | ||||||
| 				auth: WSInternalKey, | 				_id: content.entryPath, | ||||||
| 				content | 				title: content.meta.title || content.entryPath, | ||||||
|  | 				subtitle: content.meta.subtitle || '', | ||||||
|  | 				parent: content.parent.title || '', | ||||||
|  | 				content: content.text || '' | ||||||
| 			}); | 			}); | ||||||
| 			return true; |  | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 	}, | 	}, | ||||||
|   | |||||||
| @@ -43,10 +43,10 @@ module.exports = { | |||||||
|  |  | ||||||
| 		//-> Build repository path | 		//-> Build repository path | ||||||
| 		 | 		 | ||||||
| 		if(_.isEmpty(appconfig.datadir.repo)) { | 		if(_.isEmpty(appconfig.paths.repo)) { | ||||||
| 			self._repo.path = path.join(ROOTPATH, 'repo'); | 			self._repo.path = path.join(ROOTPATH, 'repo'); | ||||||
| 		} else { | 		} else { | ||||||
| 			self._repo.path = appconfig.datadir.repo; | 			self._repo.path = appconfig.paths.repo; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		//-> Initialize repository | 		//-> Initialize repository | ||||||
| @@ -240,14 +240,16 @@ module.exports = { | |||||||
| 	/** | 	/** | ||||||
| 	 * Commits uploads changes. | 	 * Commits uploads changes. | ||||||
| 	 * | 	 * | ||||||
|  | 	 * @param      {String}   msg     The commit message | ||||||
| 	 * @return     {Promise}  Resolve on commit success | 	 * @return     {Promise}  Resolve on commit success | ||||||
| 	 */ | 	 */ | ||||||
| 	commitUploads() { | 	commitUploads(msg) { | ||||||
|  |  | ||||||
| 		let self = this; | 		let self = this; | ||||||
|  | 		msg = msg || "Uploads repository sync"; | ||||||
|  |  | ||||||
| 		return self._git.add('uploads').then(() => { | 		return self._git.add('uploads').then(() => { | ||||||
| 			return self._git.commit("Uploads repository sync").catch((err) => { | 			return self._git.commit(msg).catch((err) => { | ||||||
| 			  if(_.includes(err.stdout, 'nothing to commit')) { return true; } | 			  if(_.includes(err.stdout, 'nothing to commit')) { return true; } | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
|   | |||||||
| @@ -1,335 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| var path = require('path'), |  | ||||||
| 	loki = require('lokijs'), |  | ||||||
| 	Promise = require('bluebird'), |  | ||||||
| 	fs = Promise.promisifyAll(require('fs-extra')), |  | ||||||
| 	multer  = require('multer'), |  | ||||||
| 	_ = require('lodash'); |  | ||||||
|  |  | ||||||
| var regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$"); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Local Data Storage |  | ||||||
|  * |  | ||||||
|  * @param      {Object}  appconfig  The application configuration |  | ||||||
|  */ |  | ||||||
| module.exports = { |  | ||||||
|  |  | ||||||
| 	_uploadsPath: './repo/uploads', |  | ||||||
| 	_uploadsThumbsPath: './data/thumbs', |  | ||||||
| 	_uploadsFolders: [], |  | ||||||
| 	_uploadsDb: null, |  | ||||||
|  |  | ||||||
| 	uploadImgHandler: null, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Initialize Local Data Storage model |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}  appconfig  The application config |  | ||||||
| 	 * @return     {Object}  Local Data Storage model instance |  | ||||||
| 	 */ |  | ||||||
| 	init(appconfig, mode = 'server') { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		self._uploadsPath = path.resolve(ROOTPATH, appconfig.datadir.repo, 'uploads'); |  | ||||||
| 		self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'thumbs'); |  | ||||||
|  |  | ||||||
| 		// Finish initialization tasks |  | ||||||
|  |  | ||||||
| 		switch(mode) { |  | ||||||
| 			case 'agent': |  | ||||||
| 				//todo |  | ||||||
| 			break; |  | ||||||
| 			case 'server': |  | ||||||
| 				self.createBaseDirectories(appconfig); |  | ||||||
| 				self.initMulter(appconfig); |  | ||||||
| 			break; |  | ||||||
| 			case 'ws': |  | ||||||
| 				self.initDb(appconfig); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return self; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Initialize Uploads DB |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}   appconfig  The application config |  | ||||||
| 	 * @return     {boolean}  Void |  | ||||||
| 	 */ |  | ||||||
| 	initDb(appconfig) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		let dbReadyResolve; |  | ||||||
| 		let dbReady = new Promise((resolve, reject) => { |  | ||||||
| 			dbReadyResolve = resolve; |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		// Initialize Loki.js |  | ||||||
|  |  | ||||||
| 		let dbModel = { |  | ||||||
| 			Store: new loki(path.join(appconfig.datadir.db, 'uploads.db'), { |  | ||||||
| 				env: 'NODEJS', |  | ||||||
| 				autosave: true, |  | ||||||
| 				autosaveInterval: 15000 |  | ||||||
| 			}), |  | ||||||
| 			onReady: dbReady |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 		// Load Models |  | ||||||
|  |  | ||||||
| 		dbModel.Store.loadDatabase({}, () => { |  | ||||||
|  |  | ||||||
| 			dbModel.Files = dbModel.Store.getCollection('Files'); |  | ||||||
| 			if(!dbModel.Files) { |  | ||||||
| 				dbModel.Files = dbModel.Store.addCollection('Files', { |  | ||||||
| 					indices: ['category', 'folder'] |  | ||||||
| 				}); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			dbReadyResolve(); |  | ||||||
|  |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		self._uploadsDb = dbModel; |  | ||||||
|  |  | ||||||
| 		return true; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Init Multer upload handlers |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}   appconfig  The application config |  | ||||||
| 	 * @return     {boolean}  Void |  | ||||||
| 	 */ |  | ||||||
| 	initMulter(appconfig) { |  | ||||||
|  |  | ||||||
| 		this.uploadImgHandler = multer({ |  | ||||||
| 			storage: multer.diskStorage({ |  | ||||||
| 				destination: (req, f, cb) => { |  | ||||||
| 					cb(null, path.resolve(ROOTPATH, appconfig.datadir.db, 'temp-upload')) |  | ||||||
| 				} |  | ||||||
| 			}), |  | ||||||
| 			fileFilter: (req, f, cb) => { |  | ||||||
|  |  | ||||||
| 				//-> Check filesize (3 MB max) |  | ||||||
|  |  | ||||||
| 				if(f.size > 3145728) { |  | ||||||
| 					return cb(null, false); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				//-> Check MIME type (quick check only) |  | ||||||
|  |  | ||||||
| 				if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) { |  | ||||||
| 					return cb(null, false); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				cb(null, true); |  | ||||||
| 			} |  | ||||||
| 		}).array('imgfile', 20); |  | ||||||
|  |  | ||||||
| 		return true; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Gets the thumbnails folder path. |  | ||||||
| 	 * |  | ||||||
| 	 * @return     {String}  The thumbs path. |  | ||||||
| 	 */ |  | ||||||
| 	getThumbsPath() { |  | ||||||
| 		return this._uploadsThumbsPath; |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Creates a base directories (Synchronous). |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}  appconfig  The application config |  | ||||||
| 	 * @return     {Void}  Void |  | ||||||
| 	 */ |  | ||||||
| 	createBaseDirectories(appconfig) { |  | ||||||
|  |  | ||||||
| 		winston.info('[SERVER] Checking data directories...'); |  | ||||||
|  |  | ||||||
| 		try { |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db)); |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './cache')); |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './thumbs')); |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.db, './temp-upload')); |  | ||||||
|  |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.repo)); |  | ||||||
| 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.datadir.repo, './uploads')); |  | ||||||
| 		} catch (err) { |  | ||||||
| 			winston.error(err); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		winston.info('[SERVER] Data and Repository directories are OK.'); |  | ||||||
|  |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Sets the uploads folders. |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Array<String>}  arrFolders  The arr folders |  | ||||||
| 	 * @return     {Void}  Void |  | ||||||
| 	 */ |  | ||||||
| 	setUploadsFolders(arrFolders) { |  | ||||||
|  |  | ||||||
| 		this._uploadsFolders = arrFolders; |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Gets the uploads folders. |  | ||||||
| 	 * |  | ||||||
| 	 * @return     {Array<String>}  The uploads folders. |  | ||||||
| 	 */ |  | ||||||
| 	getUploadsFolders() { |  | ||||||
| 		return this._uploadsFolders; |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Creates an uploads folder. |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}  folderName  The folder name |  | ||||||
| 	 * @return     {Promise}  Promise of the operation |  | ||||||
| 	 */ |  | ||||||
| 	createUploadsFolder(folderName) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		folderName = _.kebabCase(_.trim(folderName)); |  | ||||||
|  |  | ||||||
| 		if(_.isEmpty(folderName) || !regFolderName.test(folderName)) { |  | ||||||
| 			return Promise.resolve(self.getUploadsFolders()); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => { |  | ||||||
| 			if(!_.includes(self._uploadsFolders, folderName)) { |  | ||||||
| 				self._uploadsFolders.push(folderName); |  | ||||||
| 				self._uploadsFolders = _.sortBy(self._uploadsFolders); |  | ||||||
| 			} |  | ||||||
| 			return self.getUploadsFolders(); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Check if folder is valid and exists |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}  folderName  The folder name |  | ||||||
| 	 * @return     {Boolean}   True if valid |  | ||||||
| 	 */ |  | ||||||
| 	validateUploadsFolder(folderName) { |  | ||||||
|  |  | ||||||
| 		folderName = (_.includes(this._uploadsFolders, folderName)) ? folderName : ''; |  | ||||||
| 		return path.resolve(this._uploadsPath, folderName); |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Check if filename is valid and unique |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}           f       The filename |  | ||||||
| 	 * @param      {String}           fld     The containing folder |  | ||||||
| 	 * @return     {Promise<String>}  Promise of the accepted filename |  | ||||||
| 	 */ |  | ||||||
| 	validateUploadsFilename(f, fld) { |  | ||||||
|  |  | ||||||
| 		let fObj = path.parse(f); |  | ||||||
| 		let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, ''); |  | ||||||
| 		let fext = _.toLower(fObj.ext); |  | ||||||
|  |  | ||||||
| 		if(!_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) { |  | ||||||
| 			fext = '.png'; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		f = fname + fext; |  | ||||||
| 		let fpath = path.resolve(this._uploadsPath, fld, f); |  | ||||||
|  |  | ||||||
| 		return fs.statAsync(fpath).then((s) => { |  | ||||||
| 			throw new Error('File ' + f + ' already exists.'); |  | ||||||
| 		}).catch((err) => { |  | ||||||
| 			if(err.code === 'ENOENT') { |  | ||||||
| 				return f; |  | ||||||
| 			} |  | ||||||
| 			throw err; |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Parse relative Uploads path |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}  f       Relative Uploads path |  | ||||||
| 	 * @return     {Object}  Parsed path (folder and filename) |  | ||||||
| 	 */ |  | ||||||
| 	parseUploadsRelPath(f) { |  | ||||||
|  |  | ||||||
| 		let fObj = path.parse(f); |  | ||||||
| 		return { |  | ||||||
| 			folder: fObj.dir, |  | ||||||
| 			filename: fObj.base |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Sets the uploads files. |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Array<Object>}  arrFiles  The uploads files |  | ||||||
| 	 * @return     {Void}  Void |  | ||||||
| 	 */ |  | ||||||
| 	setUploadsFiles(arrFiles) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		if(_.isArray(arrFiles) && arrFiles.length > 0) { |  | ||||||
| 			self._uploadsDb.Files.clear(); |  | ||||||
| 			self._uploadsDb.Files.insert(arrFiles); |  | ||||||
| 			self._uploadsDb.Files.ensureIndex('category', true); |  | ||||||
| 			self._uploadsDb.Files.ensureIndex('folder', true); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Adds one or more uploads files. |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Array<Object>}  arrFiles  The uploads files |  | ||||||
| 	 * @return     {Void}  Void |  | ||||||
| 	 */ |  | ||||||
| 	addUploadsFiles(arrFiles) { |  | ||||||
| 		if(_.isArray(arrFiles) || _.isPlainObject(arrFiles)) { |  | ||||||
| 			this._uploadsDb.Files.insert(arrFiles); |  | ||||||
| 		} |  | ||||||
| 		return; |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Gets the uploads files. |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}  cat     Category type |  | ||||||
| 	 * @param      {String}  fld     Folder |  | ||||||
| 	 * @return     {Array<Object>}  The files matching the query |  | ||||||
| 	 */ |  | ||||||
| 	getUploadsFiles(cat, fld) { |  | ||||||
|  |  | ||||||
| 		return this._uploadsDb.Files.chain().find({ |  | ||||||
| 			'$and': [{ 'category' : cat	},{ 'folder' : fld }] |  | ||||||
| 		}).simplesort('filename').data(); |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| }; |  | ||||||
							
								
								
									
										64
									
								
								models/mongo.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								models/mongo.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const modb = require('mongoose'), | ||||||
|  | 			fs   = require("fs"), | ||||||
|  | 			path = require("path"), | ||||||
|  | 			_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * MongoDB module | ||||||
|  |  * | ||||||
|  |  * @param      {Object}  appconfig  Application config | ||||||
|  |  * @return     {Object}  MongoDB wrapper instance | ||||||
|  |  */ | ||||||
|  | module.exports = { | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initialize DB | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}  appconfig  The application config | ||||||
|  | 	 * @return     {Object}  DB instance | ||||||
|  | 	 */ | ||||||
|  | 	init(appconfig) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  |  | ||||||
|  | 		let dbModelsPath = path.resolve(ROOTPATH, 'models', 'db'); | ||||||
|  |  | ||||||
|  | 		modb.Promise = require('bluebird'); | ||||||
|  |  | ||||||
|  | 		// Event handlers | ||||||
|  |  | ||||||
|  | 		modb.connection.on('error', (err) => { | ||||||
|  | 			winston.error('[' + PROCNAME + '] Failed to connect to MongoDB instance.'); | ||||||
|  | 		}); | ||||||
|  | 		modb.connection.once('open', function() { | ||||||
|  | 			winston.log('[' + PROCNAME + '] Connected to MongoDB instance.'); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		// Store connection handle | ||||||
|  |  | ||||||
|  | 		self.connection = modb.connection; | ||||||
|  | 		self.ObjectId = modb.Types.ObjectId; | ||||||
|  |  | ||||||
|  | 		// Load DB Models | ||||||
|  |  | ||||||
|  | 		fs | ||||||
|  | 		.readdirSync(dbModelsPath) | ||||||
|  | 		.filter(function(file) { | ||||||
|  | 			return (file.indexOf(".") !== 0); | ||||||
|  | 		}) | ||||||
|  | 		.forEach(function(file) { | ||||||
|  | 			let modelName = _.upperFirst(_.camelCase(_.split(file,'.')[0])); | ||||||
|  | 			self[modelName] = require(path.join(dbModelsPath, file)); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		// Connect | ||||||
|  |  | ||||||
|  | 		self.onReady = modb.connect(appconfig.db); | ||||||
|  |  | ||||||
|  | 		return self; | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | }; | ||||||
							
								
								
									
										198
									
								
								models/search.js
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								models/search.js
									
									
									
									
									
								
							| @@ -1,198 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| var Promise = require('bluebird'), |  | ||||||
| 	_ = require('lodash'), |  | ||||||
| 	path = require('path'), |  | ||||||
| 	searchIndex = require('search-index'), |  | ||||||
| 	stopWord = require('stopword'); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Search Model |  | ||||||
|  */ |  | ||||||
| module.exports = { |  | ||||||
|  |  | ||||||
| 	_si: null, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Initialize Search model |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}  appconfig  The application config |  | ||||||
| 	 * @return     {Object}  Search model instance |  | ||||||
| 	 */ |  | ||||||
| 	init(appconfig) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
| 		let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search'); |  | ||||||
|  |  | ||||||
| 		searchIndex({ |  | ||||||
| 			deletable: true, |  | ||||||
| 			fieldedSearch: true, |  | ||||||
| 			indexPath: dbPath, |  | ||||||
| 			logLevel: 'error', |  | ||||||
| 			stopwords: stopWord.getStopwords(appconfig.lang).sort() |  | ||||||
| 		}, (err, si) => { |  | ||||||
| 			if(err) { |  | ||||||
| 				winston.error('Failed to initialize search-index.', err); |  | ||||||
| 			} else { |  | ||||||
| 				self._si = Promise.promisifyAll(si); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		return self; |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	find(terms) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
| 		terms = _.chain(terms) |  | ||||||
| 							.deburr() |  | ||||||
| 							.toLower() |  | ||||||
| 							.trim() |  | ||||||
| 							.replace(/[^a-z0-9 ]/g, '') |  | ||||||
| 							.value(); |  | ||||||
|  |  | ||||||
| 		let arrTerms = _.chain(terms) |  | ||||||
| 										.split(' ') |  | ||||||
| 										.filter((f) => { return !_.isEmpty(f); }) |  | ||||||
| 										.value(); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 		return self._si.searchAsync({ |  | ||||||
| 			query: { |  | ||||||
| 				AND: [{ '*': arrTerms }] |  | ||||||
| 			}, |  | ||||||
| 			pageSize: 10 |  | ||||||
| 		}).get('hits').then((hits) => { |  | ||||||
|  |  | ||||||
| 			if(hits.length < 5) { |  | ||||||
| 				return self._si.matchAsync({ |  | ||||||
| 					beginsWith: terms, |  | ||||||
| 					threshold: 3, |  | ||||||
| 					limit: 5, |  | ||||||
| 					type: 'simple' |  | ||||||
| 				}).then((matches) => { |  | ||||||
|  |  | ||||||
| 					return { |  | ||||||
| 						match: hits, |  | ||||||
| 						suggest: matches |  | ||||||
| 					}; |  | ||||||
|  |  | ||||||
| 				}); |  | ||||||
| 			} else { |  | ||||||
| 				return { |  | ||||||
| 					match: hits, |  | ||||||
| 					suggest: [] |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		}).catch((err) => { |  | ||||||
|  |  | ||||||
| 			if(err.type === 'NotFoundError') { |  | ||||||
| 				return { |  | ||||||
| 					match: [], |  | ||||||
| 					suggest: [] |  | ||||||
| 				}; |  | ||||||
| 			} else { |  | ||||||
| 				winston.error(err); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Add a document to the index |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {Object}   content  Document content |  | ||||||
| 	 * @return     {Promise}  Promise of the add operation |  | ||||||
| 	 */ |  | ||||||
| 	add(content) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		return self.delete(content.entryPath).then(() => { |  | ||||||
|  |  | ||||||
| 			return self._si.addAsync({ |  | ||||||
| 				entryPath: content.entryPath, |  | ||||||
| 				title: content.meta.title, |  | ||||||
| 				subtitle: content.meta.subtitle || '', |  | ||||||
| 				parent: content.parent.title || '', |  | ||||||
| 				content: content.text || '' |  | ||||||
| 			}, { |  | ||||||
| 				fieldOptions: [{ |  | ||||||
| 					fieldName: 'entryPath', |  | ||||||
| 					searchable: true, |  | ||||||
| 					weight: 2 |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					fieldName: 'title', |  | ||||||
| 					nGramLength: [1, 2], |  | ||||||
| 					searchable: true, |  | ||||||
| 					weight: 3 |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					fieldName: 'subtitle', |  | ||||||
| 					searchable: true, |  | ||||||
| 					weight: 1, |  | ||||||
| 					store: false |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					fieldName: 'parent', |  | ||||||
| 					searchable: false, |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					fieldName: 'content', |  | ||||||
| 					searchable: true, |  | ||||||
| 					weight: 0, |  | ||||||
| 					store: false |  | ||||||
| 				}] |  | ||||||
| 			}).then(() => { |  | ||||||
| 				winston.info('Entry ' + content.entryPath + ' added/updated to index.'); |  | ||||||
| 				return true; |  | ||||||
| 			}).catch((err) => { |  | ||||||
| 				winston.error(err); |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 		}).catch((err) => { |  | ||||||
| 			winston.error(err); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 	}, |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Delete an entry from the index |  | ||||||
| 	 * |  | ||||||
| 	 * @param      {String}   The     entry path |  | ||||||
| 	 * @return     {Promise}  Promise of the operation |  | ||||||
| 	 */ |  | ||||||
| 	delete(entryPath) { |  | ||||||
|  |  | ||||||
| 		let self = this; |  | ||||||
|  |  | ||||||
| 		return self._si.searchAsync({ |  | ||||||
| 			query: { |  | ||||||
| 				AND: [{ 'entryPath': [entryPath] }] |  | ||||||
| 			} |  | ||||||
| 		}).then((results) => { |  | ||||||
|  |  | ||||||
| 			if(results.totalHits > 0) { |  | ||||||
| 				let delIds = _.map(results.hits, 'id'); |  | ||||||
| 				return self._si.delAsync(delIds); |  | ||||||
| 			} else { |  | ||||||
| 				return true; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		}).catch((err) => { |  | ||||||
|  |  | ||||||
| 			if(err.type === 'NotFoundError') { |  | ||||||
| 				return true; |  | ||||||
| 			} else { |  | ||||||
| 				winston.error(err); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| }; |  | ||||||
							
								
								
									
										152
									
								
								models/server/local.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								models/server/local.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | var path = require('path'), | ||||||
|  | 	Promise = require('bluebird'), | ||||||
|  | 	fs = Promise.promisifyAll(require('fs-extra')), | ||||||
|  | 	multer  = require('multer'), | ||||||
|  | 	_ = require('lodash'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Local Data Storage | ||||||
|  |  * | ||||||
|  |  * @param      {Object}  appconfig  The application configuration | ||||||
|  |  */ | ||||||
|  | module.exports = { | ||||||
|  |  | ||||||
|  | 	_uploadsPath: './repo/uploads', | ||||||
|  | 	_uploadsThumbsPath: './data/thumbs', | ||||||
|  |  | ||||||
|  | 	uploadImgHandler: null, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initialize Local Data Storage model | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}  appconfig  The application config | ||||||
|  | 	 * @return     {Object}  Local Data Storage model instance | ||||||
|  | 	 */ | ||||||
|  | 	init(appconfig) { | ||||||
|  |  | ||||||
|  | 		this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads'); | ||||||
|  | 		this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs'); | ||||||
|  |  | ||||||
|  | 		this.createBaseDirectories(appconfig); | ||||||
|  | 		this.initMulter(appconfig); | ||||||
|  |  | ||||||
|  | 		return this; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Init Multer upload handlers | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}   appconfig  The application config | ||||||
|  | 	 * @return     {boolean}  Void | ||||||
|  | 	 */ | ||||||
|  | 	initMulter(appconfig) { | ||||||
|  |  | ||||||
|  | 		this.uploadImgHandler = multer({ | ||||||
|  | 			storage: multer.diskStorage({ | ||||||
|  | 				destination: (req, f, cb) => { | ||||||
|  | 					cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload')) | ||||||
|  | 				} | ||||||
|  | 			}), | ||||||
|  | 			fileFilter: (req, f, cb) => { | ||||||
|  |  | ||||||
|  | 				//-> Check filesize (3 MB max) | ||||||
|  |  | ||||||
|  | 				if(f.size > 3145728) { | ||||||
|  | 					return cb(null, false); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				//-> Check MIME type (quick check only) | ||||||
|  |  | ||||||
|  | 				if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) { | ||||||
|  | 					return cb(null, false); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				cb(null, true); | ||||||
|  | 			} | ||||||
|  | 		}).array('imgfile', 20); | ||||||
|  |  | ||||||
|  | 		return true; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Creates a base directories (Synchronous). | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}  appconfig  The application config | ||||||
|  | 	 * @return     {Void}  Void | ||||||
|  | 	 */ | ||||||
|  | 	createBaseDirectories(appconfig) { | ||||||
|  |  | ||||||
|  | 		winston.info('[SERVER] Checking data directories...'); | ||||||
|  |  | ||||||
|  | 		try { | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data)); | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache')); | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs')); | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload')); | ||||||
|  |  | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo)); | ||||||
|  | 			fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads')); | ||||||
|  | 		} catch (err) { | ||||||
|  | 			winston.error(err); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		winston.info('[SERVER] Data and Repository directories are OK.'); | ||||||
|  |  | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the uploads path. | ||||||
|  | 	 * | ||||||
|  | 	 * @return     {String}  The uploads path. | ||||||
|  | 	 */ | ||||||
|  | 	getUploadsPath() { | ||||||
|  | 		return this._uploadsPath; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the thumbnails folder path. | ||||||
|  | 	 * | ||||||
|  | 	 * @return     {String}  The thumbs path. | ||||||
|  | 	 */ | ||||||
|  | 	getThumbsPath() { | ||||||
|  | 		return this._uploadsThumbsPath; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Check if filename is valid and unique | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}           f       The filename | ||||||
|  | 	 * @param      {String}           fld     The containing folder | ||||||
|  | 	 * @return     {Promise<String>}  Promise of the accepted filename | ||||||
|  | 	 */ | ||||||
|  | 	validateUploadsFilename(f, fld) { | ||||||
|  |  | ||||||
|  | 		let fObj = path.parse(f); | ||||||
|  | 		let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, ''); | ||||||
|  | 		let fext = _.toLower(fObj.ext); | ||||||
|  |  | ||||||
|  | 		if(!_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) { | ||||||
|  | 			fext = '.png'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		f = fname + fext; | ||||||
|  | 		let fpath = path.resolve(this._uploadsPath, fld, f); | ||||||
|  |  | ||||||
|  | 		return fs.statAsync(fpath).then((s) => { | ||||||
|  | 			throw new Error('File ' + f + ' already exists.'); | ||||||
|  | 		}).catch((err) => { | ||||||
|  | 			if(err.code === 'ENOENT') { | ||||||
|  | 				return f; | ||||||
|  | 			} | ||||||
|  | 			throw err; | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | }; | ||||||
							
								
								
									
										133
									
								
								models/ws/search.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								models/ws/search.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Promise = require('bluebird'), | ||||||
|  | 			_ = require('lodash'), | ||||||
|  | 			path = require('path'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Search Model | ||||||
|  |  */ | ||||||
|  | module.exports = { | ||||||
|  |  | ||||||
|  | 	_si: null, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initialize Search model | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}  appconfig  The application config | ||||||
|  | 	 * @return     {Object}  Search model instance | ||||||
|  | 	 */ | ||||||
|  | 	init(appconfig) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  |  | ||||||
|  | 		return self; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	find(terms) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  | 		terms = _.chain(terms) | ||||||
|  | 							.deburr() | ||||||
|  | 							.toLower() | ||||||
|  | 							.trim() | ||||||
|  | 							.replace(/[^a-z0-9 ]/g, '') | ||||||
|  | 							.split(' ') | ||||||
|  | 							.filter((f) => { return !_.isEmpty(f); }) | ||||||
|  | 							.join(' ') | ||||||
|  | 							.value(); | ||||||
|  |  | ||||||
|  | 		return db.Entry.find( | ||||||
|  | 			{ $text: { $search: terms } }, | ||||||
|  | 			{ score: { $meta: "textScore" }, title: 1 } | ||||||
|  | 		) | ||||||
|  | 		.sort({ score: { $meta: "textScore" } }) | ||||||
|  | 		.limit(10) | ||||||
|  | 		.exec() | ||||||
|  | 		.then((hits) => { | ||||||
|  |  | ||||||
|  | 			/*if(hits.length < 5) { | ||||||
|  | 				return self._si.matchAsync({ | ||||||
|  | 					beginsWith: terms, | ||||||
|  | 					threshold: 3, | ||||||
|  | 					limit: 5, | ||||||
|  | 					type: 'simple' | ||||||
|  | 				}).then((matches) => { | ||||||
|  |  | ||||||
|  | 					return { | ||||||
|  | 						match: hits, | ||||||
|  | 						suggest: matches | ||||||
|  | 					}; | ||||||
|  |  | ||||||
|  | 				}); | ||||||
|  | 			} else {*/ | ||||||
|  | 				return { | ||||||
|  | 					match: hits, | ||||||
|  | 					suggest: [] | ||||||
|  | 				}; | ||||||
|  | 			//} | ||||||
|  |  | ||||||
|  | 		}).catch((err) => { | ||||||
|  |  | ||||||
|  | 			if(err.type === 'NotFoundError') { | ||||||
|  | 				return { | ||||||
|  | 					match: [], | ||||||
|  | 					suggest: [] | ||||||
|  | 				}; | ||||||
|  | 			} else { | ||||||
|  | 				winston.error(err); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Delete an entry from the index | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}   The     entry path | ||||||
|  | 	 * @return     {Promise}  Promise of the operation | ||||||
|  | 	 */ | ||||||
|  | 	delete(entryPath) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  | 		/*let hasResults = false; | ||||||
|  |  | ||||||
|  | 		return new Promise((resolve, reject) => { | ||||||
|  |  | ||||||
|  | 			self._si.search({ | ||||||
|  | 				query: { | ||||||
|  | 					AND: { 'entryPath': [entryPath] } | ||||||
|  | 				} | ||||||
|  | 			}).on('data', (results) => { | ||||||
|  |  | ||||||
|  | 				hasResults = true; | ||||||
|  |  | ||||||
|  | 				if(results.totalHits > 0) { | ||||||
|  | 					let delIds = _.map(results.hits, 'id'); | ||||||
|  | 					self._si.del(delIds).on('end', () => { return resolve(true); }); | ||||||
|  | 				} else { | ||||||
|  | 					resolve(true); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			}).on('error', (err) => { | ||||||
|  |  | ||||||
|  | 				if(err.type === 'NotFoundError') { | ||||||
|  | 					resolve(true); | ||||||
|  | 				} else { | ||||||
|  | 					winston.error(err); | ||||||
|  | 					reject(err); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 			}).on('end', () => { | ||||||
|  | 				if(!hasResults) { | ||||||
|  | 					resolve(true); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 		});*/ | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | }; | ||||||
							
								
								
									
										160
									
								
								models/ws/uploads.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								models/ws/uploads.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | var path = require('path'), | ||||||
|  | 	Promise = require('bluebird'), | ||||||
|  | 	fs = Promise.promisifyAll(require('fs-extra')), | ||||||
|  | 	multer  = require('multer'), | ||||||
|  | 	_ = require('lodash'); | ||||||
|  |  | ||||||
|  | var regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$"); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Uploads | ||||||
|  |  */ | ||||||
|  | module.exports = { | ||||||
|  |  | ||||||
|  | 	_uploadsPath: './repo/uploads', | ||||||
|  | 	_uploadsThumbsPath: './data/thumbs', | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initialize Local Data Storage model | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Object}  appconfig  The application config | ||||||
|  | 	 * @return     {Object}  Uploads model instance | ||||||
|  | 	 */ | ||||||
|  | 	init(appconfig) { | ||||||
|  |  | ||||||
|  | 		this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads'); | ||||||
|  | 		this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs'); | ||||||
|  |  | ||||||
|  | 		return this; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the thumbnails folder path. | ||||||
|  | 	 * | ||||||
|  | 	 * @return     {String}  The thumbs path. | ||||||
|  | 	 */ | ||||||
|  | 	getThumbsPath() { | ||||||
|  | 		return this._uploadsThumbsPath; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Sets the uploads folders. | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Array<String>}  arrFolders  The arr folders | ||||||
|  | 	 * @return     {Void}  Void | ||||||
|  | 	 */ | ||||||
|  | 	setUploadsFolders(arrFolders) { | ||||||
|  |  | ||||||
|  | 		this._uploadsFolders = arrFolders; | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the uploads folders. | ||||||
|  | 	 * | ||||||
|  | 	 * @return     {Array<String>}  The uploads folders. | ||||||
|  | 	 */ | ||||||
|  | 	getUploadsFolders() { | ||||||
|  | 		return this._uploadsFolders; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Creates an uploads folder. | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}  folderName  The folder name | ||||||
|  | 	 * @return     {Promise}  Promise of the operation | ||||||
|  | 	 */ | ||||||
|  | 	createUploadsFolder(folderName) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  |  | ||||||
|  | 		folderName = _.kebabCase(_.trim(folderName)); | ||||||
|  |  | ||||||
|  | 		if(_.isEmpty(folderName) || !regFolderName.test(folderName)) { | ||||||
|  | 			return Promise.resolve(self.getUploadsFolders()); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => { | ||||||
|  | 			if(!_.includes(self._uploadsFolders, folderName)) { | ||||||
|  | 				self._uploadsFolders.push(folderName); | ||||||
|  | 				self._uploadsFolders = _.sortBy(self._uploadsFolders); | ||||||
|  | 			} | ||||||
|  | 			return self.getUploadsFolders(); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Check if folder is valid and exists | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}  folderName  The folder name | ||||||
|  | 	 * @return     {Boolean}   True if valid | ||||||
|  | 	 */ | ||||||
|  | 	validateUploadsFolder(folderName) { | ||||||
|  |  | ||||||
|  | 		if(_.includes(this._uploadsFolders, folderName)) { | ||||||
|  | 			return path.resolve(this._uploadsPath, folderName); | ||||||
|  | 		} else { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Sets the uploads files. | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Array<Object>}  arrFiles  The uploads files | ||||||
|  | 	 * @return     {Void}  Void | ||||||
|  | 	 */ | ||||||
|  | 	setUploadsFiles(arrFiles) { | ||||||
|  |  | ||||||
|  | 		let self = this; | ||||||
|  |  | ||||||
|  | 		/*if(_.isArray(arrFiles) && arrFiles.length > 0) { | ||||||
|  | 			self._uploadsDb.Files.clear(); | ||||||
|  | 			self._uploadsDb.Files.insert(arrFiles); | ||||||
|  | 			self._uploadsDb.Files.ensureIndex('category', true); | ||||||
|  | 			self._uploadsDb.Files.ensureIndex('folder', true); | ||||||
|  | 		}*/ | ||||||
|  |  | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Adds one or more uploads files. | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {Array<Object>}  arrFiles  The uploads files | ||||||
|  | 	 * @return     {Void}  Void | ||||||
|  | 	 */ | ||||||
|  | 	addUploadsFiles(arrFiles) { | ||||||
|  | 		if(_.isArray(arrFiles) || _.isPlainObject(arrFiles)) { | ||||||
|  | 			//this._uploadsDb.Files.insert(arrFiles); | ||||||
|  | 		} | ||||||
|  | 		return; | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Gets the uploads files. | ||||||
|  | 	 * | ||||||
|  | 	 * @param      {String}  cat     Category type | ||||||
|  | 	 * @param      {String}  fld     Folder | ||||||
|  | 	 * @return     {Array<Object>}  The files matching the query | ||||||
|  | 	 */ | ||||||
|  | 	getUploadsFiles(cat, fld) { | ||||||
|  |  | ||||||
|  | 		return /*this._uploadsDb.Files.chain().find({ | ||||||
|  | 			'$and': [{ 'category' : cat	},{ 'folder' : fld }] | ||||||
|  | 		}).simplesort('filename').data()*/; | ||||||
|  |  | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	deleteUploadsFile(fldName, f) { | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | }; | ||||||
							
								
								
									
										31
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								package.json
									
									
									
									
									
								
							| @@ -29,26 +29,24 @@ | |||||||
|   }, |   }, | ||||||
|   "homepage": "https://github.com/Requarks/wiki#readme", |   "homepage": "https://github.com/Requarks/wiki#readme", | ||||||
|   "engines": { |   "engines": { | ||||||
|     "node": ">=4.4.5" |     "node": ">=4.6" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "auto-load": "^2.1.0", |     "auto-load": "^2.1.0", | ||||||
|     "bcryptjs-then": "^1.0.1", |     "bcryptjs-then": "^1.0.1", | ||||||
|     "bluebird": "^3.4.6", |     "bluebird": "^3.4.6", | ||||||
|     "body-parser": "^1.15.2", |     "body-parser": "^1.15.2", | ||||||
|     "bson": "^0.5.5", |  | ||||||
|     "cheerio": "^0.22.0", |     "cheerio": "^0.22.0", | ||||||
|     "child-process-promise": "^2.1.3", |     "child-process-promise": "^2.1.3", | ||||||
|     "chokidar": "^1.6.0", |     "chokidar": "^1.6.0", | ||||||
|     "compression": "^1.6.2", |     "compression": "^1.6.2", | ||||||
|     "connect-flash": "^0.1.1", |     "connect-flash": "^0.1.1", | ||||||
|     "connect-loki": "^1.0.6", |     "connect-mongo": "^1.3.2", | ||||||
|     "connect-redis": "^3.1.0", |  | ||||||
|     "cookie-parser": "^1.4.3", |     "cookie-parser": "^1.4.3", | ||||||
|     "cron": "^1.1.0", |     "cron": "^1.1.1", | ||||||
|     "express": "^4.14.0", |     "express": "^4.14.0", | ||||||
|     "express-brute": "^1.0.0", |     "express-brute": "^1.0.0", | ||||||
|     "express-brute-loki": "^1.0.0", |     "express-brute-mongo": "^0.1.0", | ||||||
|     "express-session": "^1.14.1", |     "express-session": "^1.14.1", | ||||||
|     "express-validator": "^2.20.10", |     "express-validator": "^2.20.10", | ||||||
|     "farmhash": "^1.2.1", |     "farmhash": "^1.2.1", | ||||||
| @@ -61,31 +59,30 @@ | |||||||
|     "i18next-express-middleware": "^1.0.2", |     "i18next-express-middleware": "^1.0.2", | ||||||
|     "i18next-node-fs-backend": "^0.1.2", |     "i18next-node-fs-backend": "^0.1.2", | ||||||
|     "js-yaml": "^3.6.1", |     "js-yaml": "^3.6.1", | ||||||
|     "lodash": "^4.16.2", |     "lodash": "^4.16.4", | ||||||
|     "lokijs": "^1.4.1", |  | ||||||
|     "markdown-it": "^8.0.0", |     "markdown-it": "^8.0.0", | ||||||
|     "markdown-it-abbr": "^1.0.4", |     "markdown-it-abbr": "^1.0.4", | ||||||
|     "markdown-it-anchor": "^2.5.0", |     "markdown-it-anchor": "^2.5.0", | ||||||
|     "markdown-it-attrs": "^0.7.1", |     "markdown-it-attrs": "^0.7.1", | ||||||
|     "markdown-it-emoji": "^1.2.0", |     "markdown-it-emoji": "^1.3.0", | ||||||
|     "markdown-it-expand-tabs": "^1.0.11", |     "markdown-it-expand-tabs": "^1.0.11", | ||||||
|     "markdown-it-external-links": "0.0.5", |     "markdown-it-external-links": "0.0.6", | ||||||
|     "markdown-it-footnote": "^3.0.1", |     "markdown-it-footnote": "^3.0.1", | ||||||
|     "markdown-it-task-lists": "^1.4.1", |     "markdown-it-task-lists": "^1.4.1", | ||||||
|     "moment": "^2.15.1", |     "moment": "^2.15.1", | ||||||
|     "moment-timezone": "^0.5.5", |     "moment-timezone": "^0.5.6", | ||||||
|  |     "mongoose": "^4.6.3", | ||||||
|     "multer": "^1.2.0", |     "multer": "^1.2.0", | ||||||
|     "passport": "^0.3.2", |     "passport": "^0.3.2", | ||||||
|     "passport-local": "^1.0.0", |     "passport-local": "^1.0.0", | ||||||
|     "pug": "^2.0.0-beta6", |     "pug": "^2.0.0-beta6", | ||||||
|     "read-chunk": "^2.0.0", |     "read-chunk": "^2.0.0", | ||||||
|     "remove-markdown": "^0.1.0", |     "remove-markdown": "^0.1.0", | ||||||
|     "search-index": "^0.8.15", |  | ||||||
|     "serve-favicon": "^2.3.0", |     "serve-favicon": "^2.3.0", | ||||||
|     "sharp": "^0.16.0", |     "sharp": "^0.16.0", | ||||||
|     "simplemde": "^1.11.2", |     "simplemde": "^1.11.2", | ||||||
|     "snyk": "^1.19.1", |     "snyk": "^1.19.1", | ||||||
|     "socket.io": "^1.4.8", |     "socket.io": "^1.5.0", | ||||||
|     "sticky-js": "^1.0.7", |     "sticky-js": "^1.0.7", | ||||||
|     "validator": "^6.0.0", |     "validator": "^6.0.0", | ||||||
|     "validator-as-promised": "^1.0.2", |     "validator-as-promised": "^1.0.2", | ||||||
| @@ -96,7 +93,7 @@ | |||||||
|     "babel-preset-es2015": "^6.16.0", |     "babel-preset-es2015": "^6.16.0", | ||||||
|     "bulma": "^0.1.2", |     "bulma": "^0.1.2", | ||||||
|     "chai": "^3.5.0", |     "chai": "^3.5.0", | ||||||
|     "chai-as-promised": "^5.3.0", |     "chai-as-promised": "^6.0.0", | ||||||
|     "codacy-coverage": "^2.0.0", |     "codacy-coverage": "^2.0.0", | ||||||
|     "filesize.js": "^1.0.1", |     "filesize.js": "^1.0.1", | ||||||
|     "font-awesome": "^4.6.3", |     "font-awesome": "^4.6.3", | ||||||
| @@ -120,10 +117,10 @@ | |||||||
|     "merge-stream": "^1.0.0", |     "merge-stream": "^1.0.0", | ||||||
|     "mocha": "^3.1.0", |     "mocha": "^3.1.0", | ||||||
|     "mocha-lcov-reporter": "^1.2.0", |     "mocha-lcov-reporter": "^1.2.0", | ||||||
|     "nodemon": "^1.10.2", |     "nodemon": "^1.11.0", | ||||||
|     "sticky-js": "^1.1.0", |     "sticky-js": "^1.1.2", | ||||||
|     "twemoji-awesome": "^1.0.4", |     "twemoji-awesome": "^1.0.4", | ||||||
|     "vue": "^1.0.28" |     "vue": "^2.0.1" | ||||||
|   }, |   }, | ||||||
|   "snyk": true |   "snyk": true | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								server.js
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								server.js
									
									
									
									
									
								
							| @@ -20,8 +20,8 @@ winston.info('[SERVER] Requarks Wiki is initializing...'); | |||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
|  |  | ||||||
| var appconfig = require('./models/config')('./config.yml'); | var appconfig = require('./models/config')('./config.yml'); | ||||||
| global.lcdata = require('./models/localdata').init(appconfig, 'server'); | global.lcdata = require('./models/server/local').init(appconfig); | ||||||
| global.db = require('./models/db')(appconfig); | global.db = require('./models/mongo').init(appconfig); | ||||||
| global.git = require('./models/git').init(appconfig, false); | global.git = require('./models/git').init(appconfig, false); | ||||||
| global.entries = require('./models/entries').init(appconfig); | global.entries = require('./models/entries').init(appconfig); | ||||||
| global.mark = require('./models/markdown'); | global.mark = require('./models/markdown'); | ||||||
| @@ -35,7 +35,7 @@ var express = require('express'); | |||||||
| var path = require('path'); | var path = require('path'); | ||||||
| var favicon = require('serve-favicon'); | var favicon = require('serve-favicon'); | ||||||
| var session = require('express-session'); | var session = require('express-session'); | ||||||
| var lokiStore = require('connect-loki')(session); | const mongoStore = require('connect-mongo')(session); | ||||||
| var cookieParser = require('cookie-parser'); | var cookieParser = require('cookie-parser'); | ||||||
| var bodyParser = require('body-parser'); | var bodyParser = require('body-parser'); | ||||||
| var flash = require('connect-flash'); | var flash = require('connect-flash'); | ||||||
| @@ -73,7 +73,10 @@ var strategy = require('./models/auth')(passport, appconfig); | |||||||
| app.use(cookieParser()); | app.use(cookieParser()); | ||||||
| app.use(session({ | app.use(session({ | ||||||
|   name: 'requarkswiki.sid', |   name: 'requarkswiki.sid', | ||||||
|   store: new lokiStore({ path: path.join(appconfig.datadir.db, 'sessions.db') }), |   store: new mongoStore({ | ||||||
|  |     mongooseConnection: db.connection, | ||||||
|  |     touchAfter: 15 | ||||||
|  |   }), | ||||||
|   secret: appconfig.sessionSecret, |   secret: appconfig.sessionSecret, | ||||||
|   resave: false, |   resave: false, | ||||||
|   saveUninitialized: false |   saveUninitialized: false | ||||||
|   | |||||||
| @@ -20,7 +20,8 @@ | |||||||
| 			block rootNavRight | 			block rootNavRight | ||||||
| 				i.nav-item#notifload | 				i.nav-item#notifload | ||||||
|  |  | ||||||
| 	.box.searchresults.animated(v-show='searchactive', transition='slide', v-cloak, style={'display':'none'}) | 	transition(name="searchresults-anim", enter-active-class="slideInDown", leave-active-class="fadeOutUp") | ||||||
|  | 		.box.searchresults.animated(v-show='searchactive', v-cloak, style={'display':'none'}) | ||||||
| 			.menu | 			.menu | ||||||
| 				p.menu-label | 				p.menu-label | ||||||
| 					| Search Results | 					| Search Results | ||||||
| @@ -28,7 +29,7 @@ | |||||||
| 					li(v-if="searchres.length === 0") | 					li(v-if="searchres.length === 0") | ||||||
| 						a: em No results matching your query | 						a: em No results matching your query | ||||||
| 					li(v-for='sres in searchres') | 					li(v-for='sres in searchres') | ||||||
| 					a(href='/{{ sres.document.entryPath }}', v-bind:class="{ 'is-active': searchmovekey === 'res.' + sres.document.entryPath }") {{ sres.document.title }} | 						a(v-bind:href="'/' + sres._id", v-bind:class="{ 'is-active': searchmovekey === 'res.' + sres._id }") {{ sres.title }} | ||||||
| 				p.menu-label(v-if='searchsuggest.length > 0') | 				p.menu-label(v-if='searchsuggest.length > 0') | ||||||
| 					| Did you mean...? | 					| Did you mean...? | ||||||
| 				ul.menu-list(v-if='searchsuggest.length > 0') | 				ul.menu-list(v-if='searchsuggest.length > 0') | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								ws-server.js
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								ws-server.js
									
									
									
									
									
								
							| @@ -31,11 +31,11 @@ global.internalAuth = require('./lib/internalAuth').init(process.argv[2]);; | |||||||
| winston.info('[WS] WS Server is initializing...'); | winston.info('[WS] WS Server is initializing...'); | ||||||
|  |  | ||||||
| var appconfig = require('./models/config')('./config.yml'); | var appconfig = require('./models/config')('./config.yml'); | ||||||
| let lcdata = require('./models/localdata').init(appconfig, 'ws'); | global.db = require('./models/mongo').init(appconfig); | ||||||
|  | global.upl = require('./models/ws/uploads').init(appconfig); | ||||||
| global.entries = require('./models/entries').init(appconfig); | global.entries = require('./models/entries').init(appconfig); | ||||||
| global.mark = require('./models/markdown'); | global.mark = require('./models/markdown'); | ||||||
| global.search = require('./models/search').init(appconfig); | global.search = require('./models/ws/search').init(appconfig); | ||||||
|  |  | ||||||
| // ---------------------------------------- | // ---------------------------------------- | ||||||
| // Load local modules | // Load local modules | ||||||
| @@ -108,14 +108,14 @@ io.on('connection', (socket) => { | |||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('searchDel', (data, cb) => { | 	socket.on('searchDel', (data, cb) => { | ||||||
| 		cb = cb || _.noop | 		cb = cb || _.noop; | ||||||
| 		if(internalAuth.validateKey(data.auth)) { | 		if(internalAuth.validateKey(data.auth)) { | ||||||
| 			search.delete(data.entryPath); | 			search.delete(data.entryPath); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('search', (data, cb) => { | 	socket.on('search', (data, cb) => { | ||||||
| 		cb = cb || _.noop | 		cb = cb || _.noop; | ||||||
| 		search.find(data.terms).then((results) => { | 		search.find(data.terms).then((results) => { | ||||||
| 			cb(results); | 			cb(results); | ||||||
| 		}); | 		}); | ||||||
| @@ -125,44 +125,46 @@ io.on('connection', (socket) => { | |||||||
| 	// UPLOADS | 	// UPLOADS | ||||||
| 	//----------------------------------------- | 	//----------------------------------------- | ||||||
|  |  | ||||||
| 	socket.on('uploadsSetFolders', (data, cb) => { | 	socket.on('uploadsSetFolders', (data) => { | ||||||
| 		cb = cb || _.noop |  | ||||||
| 		if(internalAuth.validateKey(data.auth)) { | 		if(internalAuth.validateKey(data.auth)) { | ||||||
| 			lcdata.setUploadsFolders(data.content); | 			upl.setUploadsFolders(data.content); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('uploadsGetFolders', (data, cb) => { | 	socket.on('uploadsGetFolders', (data, cb) => { | ||||||
| 		cb = cb || _.noop | 		cb = cb || _.noop; | ||||||
| 		cb(lcdata.getUploadsFolders()); | 		cb(upl.getUploadsFolders()); | ||||||
|  | 	}); | ||||||
|  |  | ||||||
|  | 	socket.on('uploadsValidateFolder', (data, cb) => { | ||||||
|  | 		cb = cb || _.noop; | ||||||
|  | 		if(internalAuth.validateKey(data.auth)) { | ||||||
|  | 			cb(upl.validateUploadsFolder(data.content)); | ||||||
|  | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('uploadsCreateFolder', (data, cb) => { | 	socket.on('uploadsCreateFolder', (data, cb) => { | ||||||
| 		cb = cb || _.noop | 		cb = cb || _.noop; | ||||||
| 		lcdata.createUploadsFolder(data.foldername).then((fldList) => { | 		upl.createUploadsFolder(data.foldername).then((fldList) => { | ||||||
| 			cb(fldList); | 			cb(fldList); | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('uploadsSetFiles', (data, cb) => { | 	socket.on('uploadsSetFiles', (data) => { | ||||||
| 		cb = cb || _.noop; |  | ||||||
| 		if(internalAuth.validateKey(data.auth)) { | 		if(internalAuth.validateKey(data.auth)) { | ||||||
| 			lcdata.setUploadsFiles(data.content); | 			upl.setUploadsFiles(data.content); | ||||||
| 			cb(true); |  | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('uploadsAddFiles', (data, cb) => { | 	socket.on('uploadsAddFiles', (data) => { | ||||||
| 		cb = cb || _.noop |  | ||||||
| 		if(internalAuth.validateKey(data.auth)) { | 		if(internalAuth.validateKey(data.auth)) { | ||||||
| 			lcdata.addUploadsFiles(data.content); | 			upl.addUploadsFiles(data.content); | ||||||
| 			cb(true); |  | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	socket.on('uploadsGetImages', (data, cb) => { | 	socket.on('uploadsGetImages', (data, cb) => { | ||||||
| 		cb = cb || _.noop | 		cb = cb || _.noop; | ||||||
| 		cb(lcdata.getUploadsFiles('image', data.folder)); | 		cb(upl.getUploadsFiles('image', data.folder)); | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user