From 12ea967a842910beb2feddd51ab844255891ffd4 Mon Sep 17 00:00:00 2001 From: NGPixel Date: Sun, 12 Feb 2017 15:40:43 -0500 Subject: [PATCH] Search-Index integration + cache flush on start --- .eslintrc.json | 1 + CHANGELOG.md | 4 + agent.js | 8 +- assets/js/app.js | 4 +- client/js/components/search.js | 4 +- controllers/ws.js | 2 +- libs/entries.js | 4 +- libs/git.js | 22 ++-- libs/local.js | 5 +- libs/search.js | 206 +++++++++++++++++++++++++++++++++ package.json | 4 +- server.js | 15 ++- views/common/header.pug | 4 +- 13 files changed, 259 insertions(+), 24 deletions(-) create mode 100644 libs/search.js diff --git a/.eslintrc.json b/.eslintrc.json index 2ebccbf6..9ef7c3bd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -32,6 +32,7 @@ "lcdata": true, "mark": true, "rights": true, + "search": true, "upl": true, "winston": true, "ws": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index 663ea02f..14c79d33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added - Offline mode (no remote git sync) can now be enabled by setting `git: false` in config.yml +- Improved search engine (Now using search-index engine instead of MongoDB text search) + +### Changed +- Cache is now flushed when starting / restarting the server ## [v1.0-beta.4] - 2017-02-11 ### Fixed diff --git a/agent.js b/agent.js index 254cea5c..7835739d 100644 --- a/agent.js +++ b/agent.js @@ -113,7 +113,13 @@ var job = new Cron({ // -> Update cache and search index if (fileStatus !== 'active') { - return entries.updateCache(entryPath) + return entries.updateCache(entryPath).then(entry => { + process.send({ + action: 'searchAdd', + content: entry + }) + return true + }) } return true diff --git a/assets/js/app.js b/assets/js/app.js index 2898b85e..db31f1ff 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,2 +1,2 @@ -"use strict";function _classCallCheck(e,o){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}function setInputSelection(e,o,t){if(e.focus(),"undefined"!=typeof e.selectionStart)e.selectionStart=o,e.selectionEnd=t;else if(document.selection&&document.selection.createRange){e.select();var n=document.selection.createRange();n.collapse(!0),n.moveEnd("character",t),n.moveStart("character",o),n.select()}}function makeSafePath(e){var o=_.split(_.trim(e),"/");return o=_.map(o,function(e){return _.kebabCase(_.deburr(_.trim(e)))}),_.join(_.filter(o,function(e){return!_.isEmpty(e)}),"/")}var _createClass=function(){function e(e,o){for(var t=0;t=3?(i.searchactive=!0,i.searchload++,n.emit("search",{terms:e},function(e){i.searchres=e.match,i.searchsuggest=e.suggest,i.searchmovearr=_.concat([],i.searchres,i.searchsuggest),i.searchload>0&&i.searchload--})):(i.searchactive=!1,i.searchres=[],i.searchsuggest=[],i.searchmovearr=[],i.searchload=0)},searchmoveidx:function(e,o){e>0?i.searchmovekey=i.searchmovearr[e-1]?"res."+i.searchmovearr[e-1]._id:"sug."+i.searchmovearr[e-1]:i.searchmovekey=""}},methods:{useSuggestion:function(e){i.searchq=e},closeSearch:function(){i.searchq=""},moveSelectSearch:function(){if(!(i.searchmoveidx<1)){var e=i.searchmoveidx-1;i.searchmovearr[e]?window.location.assign("/"+i.searchmovearr[e]._id):i.searchq=i.searchmovearr[e]}},moveDownSearch:function(){i.searchmoveidx0&&i.searchmoveidx--}}});e("main").on("click",i.closeSearch)}if(e("#page-type-view").length&&!function(){var o="home"!==e("#page-type-view").data("entrypath")?e("#page-type-view").data("entrypath"):"",n=o+"/new-page";e(".btn-create-prompt").on("click",function(t){e("#txt-create-prompt").val(n),e("#modal-create-prompt").toggleClass("is-active"),setInputSelection(e("#txt-create-prompt").get(0),o.length+1,n.length),e("#txt-create-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-create-prompt").on("keypress",function(o){13===o.which&&e(".btn-create-go").trigger("click")}),e(".btn-create-go").on("click",function(o){var t=makeSafePath(e("#txt-create-prompt").val());_.isEmpty(t)?e("#txt-create-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-create-prompt").parent().addClass("is-loading"),window.location.assign("/create/"+t))}),""!==o&&e(".btn-move-prompt").removeClass("is-hidden");var i=_.lastIndexOf(o,"/")+1;e(".btn-move-prompt").on("click",function(t){e("#txt-move-prompt").val(o),e("#modal-move-prompt").toggleClass("is-active"),setInputSelection(e("#txt-move-prompt").get(0),i,o.length),e("#txt-move-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-move-prompt").on("keypress",function(o){13===o.which&&e(".btn-move-go").trigger("click")}),e(".btn-move-go").on("click",function(n){var i=makeSafePath(e("#txt-move-prompt").val());_.isEmpty(i)||i===o||"home"===i?e("#txt-move-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-move-prompt").parent().addClass("is-loading"),e.ajax(window.location.href,{data:{move:i},dataType:"json",method:"PUT"}).then(function(e,o,n){e.ok?window.location.assign("/"+i):t.pushError("Something went wrong",e.error)},function(e,o,n){t.pushError("Something went wrong","Save operation failed.")}))})}(),e("#page-type-create").length){var a;!function(){var i=e("#page-type-create").data("entrypath");e(".btn-create-discard").on("click",function(o){e("#modal-create-discard").toggleClass("is-active")}),1===e("#mk-editor").length&&!function(){var r=!1;Vue.filter("filesize",function(e){return _.toUpper(filesize(e))});var l=new Vue({el:"#modal-editor-image",data:{isLoading:!1,isLoadingText:"",newFolderName:"",newFolderShow:!1,newFolderError:!1,fetchFromUrlURL:"",fetchFromUrlShow:!1,folders:[],currentFolder:"",currentImage:"",currentAlign:"left",images:[],uploadSucceeded:!1,postUploadChecks:0,renameImageShow:!1,renameImageId:"",renameImageFilename:"",deleteImageShow:!1,deleteImageId:"",deleteImageFilename:""},methods:{open:function(){r=!0,e("#modal-editor-image").addClass("is-active"),l.refreshFolders()},cancel:function(o){r=!1,e("#modal-editor-image").removeClass("is-active")},selectImage:function(e){l.currentImage=e},insertImage:function(e){a.codemirror.doc.somethingSelected()&&a.codemirror.execCommand("singleSelection");var o=_.find(l.images,["_id",l.currentImage]);o.normalizedPath="f:"===o.folder?o.filename:o.folder.slice(2)+"/"+o.filename,o.titleGuess=_.startCase(o.basename);var t="!["+o.titleGuess+"](/uploads/"+o.normalizedPath+' "'+o.titleGuess+'")';switch(l.currentAlign){case"center":t+="{.align-center}";break;case"right":t+="{.align-right}";break;case"logo":t+="{.pagelogo}"}a.codemirror.doc.replaceSelection(t),l.cancel()},newFolder:function(o){l.newFolderName="",l.newFolderError=!1,l.newFolderShow=!0,_.delay(function(){e("#txt-editor-image-newfoldername").focus()},400)},newFolderDiscard:function(e){l.newFolderShow=!1},newFolderCreate:function(e){var o=new RegExp("^[a-z0-9][a-z0-9-]*[a-z0-9]$");return l.newFolderName=_.kebabCase(_.trim(l.newFolderName)),_.isEmpty(l.newFolderName)||!o.test(l.newFolderName)?void(l.newFolderError=!0):(l.newFolderDiscard(),l.isLoadingText="Creating new folder...",l.isLoading=!0,void Vue.nextTick(function(){n.emit("uploadsCreateFolder",{foldername:l.newFolderName},function(e){l.folders=e,l.currentFolder=l.newFolderName,l.images=[],l.isLoading=!1})}))},fetchFromUrl:function(o){l.fetchFromUrlURL="",l.fetchFromUrlShow=!0,_.delay(function(){e("#txt-editor-image-fetchurl").focus()},400)},fetchFromUrlDiscard:function(e){l.fetchFromUrlShow=!1},fetchFromUrlGo:function(e){l.fetchFromUrlDiscard(),l.isLoadingText="Fetching image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsFetchFileFromURL",{folder:l.currentFolder,fetchUrl:l.fetchFromUrlURL},function(e){e.ok?l.waitChangeComplete(l.images.length,!0):(l.isLoading=!1,t.pushError("Upload error",e.msg))})})},renameImage:function(){var o=_.find(l.images,["_id",l.renameImageId]);l.renameImageFilename=o.basename||"",l.renameImageShow=!0,_.delay(function(){e("#txt-editor-image-rename").focus(),_.defer(function(){e("#txt-editor-image-rename").select()})},400)},renameImageDiscard:function(){l.renameImageShow=!1},renameImageGo:function(){l.renameImageDiscard(),l.isLoadingText="Renaming image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsRenameFile",{uid:l.renameImageId,folder:l.currentFolder,filename:l.renameImageFilename},function(e){e.ok?l.waitChangeComplete(l.images.length,!1):(l.isLoading=!1,t.pushError("Rename error",e.msg))})})},moveImage:function(e,o){l.isLoadingText="Moving image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsMoveFile",{uid:e,folder:o},function(e){e.ok?l.loadImages():(l.isLoading=!1,t.pushError("Rename error",e.msg))})})},deleteImageWarn:function(e){if(e){var o=_.find(l.images,["_id",l.deleteImageId]);l.deleteImageFilename=o.filename||"this image"}l.deleteImageShow=e},deleteImageGo:function(){l.deleteImageWarn(!1),l.isLoadingText="Deleting image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsDeleteFile",{uid:l.deleteImageId},function(e){l.loadImages()})})},selectFolder:function(e){l.currentFolder=e,l.loadImages()},refreshFolders:function(){l.isLoadingText="Fetching folders list...",l.isLoading=!0,l.currentFolder="",l.currentImage="",Vue.nextTick(function(){n.emit("uploadsGetFolders",{},function(e){l.folders=e,l.loadImages()})})},loadImages:function(e){return e||(l.isLoadingText="Fetching images...",l.isLoading=!0),new Promise(function(o,t){Vue.nextTick(function(){n.emit("uploadsGetImages",{folder:l.currentFolder},function(t){l.images=t,e||(l.isLoading=!1),l.attachContextMenus(),o(!0)})})})},waitChangeComplete:function(e,o){o=!_.isBoolean(o)||o,l.postUploadChecks++,l.isLoadingText="Processing...",Vue.nextTick(function(){l.loadImages(!0).then(function(){l.images.length!==e===o?(l.postUploadChecks=0,l.isLoading=!1):l.postUploadChecks>5?(l.postUploadChecks=0,l.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){l.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(l.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(l.folders,o);l.moveImage(n,i)}}});e.contextMenu("destroy",".editor-modal-image-choices > figure"),e.contextMenu({selector:".editor-modal-image-choices > figure",appendTo:".editor-modal-image-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/2,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){l.renameImageId=_.toString(o.$trigger[0].dataset.uid),l.renameImage()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){l.deleteImageId=_.toString(o.$trigger[0].dataset.uid),l.deleteImageWarn(!0)}}}})}}});e("#btn-editor-image-upload input").on("change",function(n){var i=l.images.length;e(n.currentTarget).simpleUpload("/uploads/img",{name:"imgfile",data:{folder:l.currentFolder},limit:20,expect:"json",allowedExts:["jpg","jpeg","gif","png","webp"],allowedTypes:["image/png","image/jpeg","image/gif","image/webp"],maxFileSize:3145728,init:function(e){l.uploadSucceeded=!1,l.isLoadingText="Preparing to upload...",l.isLoading=!0},progress:function(e){l.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(d.postUploadChecks=0,d.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){d.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(d.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(d.folders,o);d.moveFile(n,i)}}});e.contextMenu("destroy",".editor-modal-file-choices > figure"),e.contextMenu({selector:".editor-modal-file-choices > figure",appendTo:".editor-modal-file-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/5,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){d.renameFileId=_.toString(o.$trigger[0].dataset.uid),d.renameFile()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){d.deleteFileId=_.toString(o.$trigger[0].dataset.uid),d.deleteFileWarn(!0)}}}})}}});e("#btn-editor-file-upload input").on("change",function(n){var i=d.files.length;e(n.currentTarget).simpleUpload("/uploads/file",{name:"binfile",data:{folder:d.currentFolder},limit:20,expect:"json",maxFileSize:0,init:function(e){d.uploadSucceeded=!1,d.isLoadingText="Preparing to upload...",d.isLoading=!0},progress:function(e){d.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(l.postUploadChecks=0,l.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){l.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(l.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(l.folders,o);l.moveImage(n,i)}}});e.contextMenu("destroy",".editor-modal-image-choices > figure"),e.contextMenu({selector:".editor-modal-image-choices > figure",appendTo:".editor-modal-image-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/2,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){l.renameImageId=_.toString(o.$trigger[0].dataset.uid),l.renameImage()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){l.deleteImageId=_.toString(o.$trigger[0].dataset.uid),l.deleteImageWarn(!0)}}}})}}});e("#btn-editor-image-upload input").on("change",function(n){var i=l.images.length;e(n.currentTarget).simpleUpload("/uploads/img",{name:"imgfile",data:{folder:l.currentFolder},limit:20,expect:"json",allowedExts:["jpg","jpeg","gif","png","webp"],allowedTypes:["image/png","image/jpeg","image/gif","image/webp"],maxFileSize:3145728,init:function(e){l.uploadSucceeded=!1,l.isLoadingText="Preparing to upload...",l.isLoading=!0},progress:function(e){l.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(d.postUploadChecks=0,d.isLoading=!1, -t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){d.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(d.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(d.folders,o);d.moveFile(n,i)}}});e.contextMenu("destroy",".editor-modal-file-choices > figure"),e.contextMenu({selector:".editor-modal-file-choices > figure",appendTo:".editor-modal-file-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/5,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){d.renameFileId=_.toString(o.$trigger[0].dataset.uid),d.renameFile()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){d.deleteFileId=_.toString(o.$trigger[0].dataset.uid),d.deleteFileWarn(!0)}}}})}}});e("#btn-editor-file-upload input").on("change",function(n){var i=d.files.length;e(n.currentTarget).simpleUpload("/uploads/file",{name:"binfile",data:{folder:d.currentFolder},limit:20,expect:"json",maxFileSize:0,init:function(e){d.uploadSucceeded=!1,d.isLoadingText="Preparing to upload...",d.isLoading=!0},progress:function(e){d.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length=0&&n&&(n.class+=" exit",Vue.set(o.mdl.children,t,n),_.delay(function(){o.mdl.children.splice(t,1)},500))}}]),e}(); \ No newline at end of file +"use strict";function _classCallCheck(e,o){if(!(e instanceof o))throw new TypeError("Cannot call a class as a function")}function setInputSelection(e,o,t){if(e.focus(),"undefined"!=typeof e.selectionStart)e.selectionStart=o,e.selectionEnd=t;else if(document.selection&&document.selection.createRange){e.select();var n=document.selection.createRange();n.collapse(!0),n.moveEnd("character",t),n.moveStart("character",o),n.select()}}function makeSafePath(e){var o=_.split(_.trim(e),"/");return o=_.map(o,function(e){return _.kebabCase(_.deburr(_.trim(e)))}),_.join(_.filter(o,function(e){return!_.isEmpty(e)}),"/")}var _createClass=function(){function e(e,o){for(var t=0;t=3?(i.searchactive=!0,i.searchload++,n.emit("search",{terms:e},function(e){i.searchres=e.match,i.searchsuggest=e.suggest,i.searchmovearr=_.concat([],i.searchres,i.searchsuggest),i.searchload>0&&i.searchload--})):(i.searchactive=!1,i.searchres=[],i.searchsuggest=[],i.searchmovearr=[],i.searchload=0)},searchmoveidx:function(e,o){e>0?i.searchmovekey=i.searchmovearr[e-1]?"res."+i.searchmovearr[e-1].entryPath:"sug."+i.searchmovearr[e-1]:i.searchmovekey=""}},methods:{useSuggestion:function(e){i.searchq=e},closeSearch:function(){i.searchq=""},moveSelectSearch:function(){if(!(i.searchmoveidx<1)){var e=i.searchmoveidx-1;i.searchmovearr[e]?window.location.assign("/"+i.searchmovearr[e].entryPath):i.searchq=i.searchmovearr[e]}},moveDownSearch:function(){i.searchmoveidx0&&i.searchmoveidx--}}});e("main").on("click",i.closeSearch)}if(e("#page-type-view").length&&!function(){var o="home"!==e("#page-type-view").data("entrypath")?e("#page-type-view").data("entrypath"):"",n=o+"/new-page";e(".btn-create-prompt").on("click",function(t){e("#txt-create-prompt").val(n),e("#modal-create-prompt").toggleClass("is-active"),setInputSelection(e("#txt-create-prompt").get(0),o.length+1,n.length),e("#txt-create-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-create-prompt").on("keypress",function(o){13===o.which&&e(".btn-create-go").trigger("click")}),e(".btn-create-go").on("click",function(o){var t=makeSafePath(e("#txt-create-prompt").val());_.isEmpty(t)?e("#txt-create-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-create-prompt").parent().addClass("is-loading"),window.location.assign("/create/"+t))}),""!==o&&e(".btn-move-prompt").removeClass("is-hidden");var i=_.lastIndexOf(o,"/")+1;e(".btn-move-prompt").on("click",function(t){e("#txt-move-prompt").val(o),e("#modal-move-prompt").toggleClass("is-active"),setInputSelection(e("#txt-move-prompt").get(0),i,o.length),e("#txt-move-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-move-prompt").on("keypress",function(o){13===o.which&&e(".btn-move-go").trigger("click")}),e(".btn-move-go").on("click",function(n){var i=makeSafePath(e("#txt-move-prompt").val());_.isEmpty(i)||i===o||"home"===i?e("#txt-move-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-move-prompt").parent().addClass("is-loading"),e.ajax(window.location.href,{data:{move:i},dataType:"json",method:"PUT"}).then(function(e,o,n){e.ok?window.location.assign("/"+i):t.pushError("Something went wrong",e.error)},function(e,o,n){t.pushError("Something went wrong","Save operation failed.")}))})}(),e("#page-type-create").length){var a;!function(){var i=e("#page-type-create").data("entrypath");e(".btn-create-discard").on("click",function(o){e("#modal-create-discard").toggleClass("is-active")}),1===e("#mk-editor").length&&!function(){var r=!1;Vue.filter("filesize",function(e){return _.toUpper(filesize(e))});var l=new Vue({el:"#modal-editor-image",data:{isLoading:!1,isLoadingText:"",newFolderName:"",newFolderShow:!1,newFolderError:!1,fetchFromUrlURL:"",fetchFromUrlShow:!1,folders:[],currentFolder:"",currentImage:"",currentAlign:"left",images:[],uploadSucceeded:!1,postUploadChecks:0,renameImageShow:!1,renameImageId:"",renameImageFilename:"",deleteImageShow:!1,deleteImageId:"",deleteImageFilename:""},methods:{open:function(){r=!0,e("#modal-editor-image").addClass("is-active"),l.refreshFolders()},cancel:function(o){r=!1,e("#modal-editor-image").removeClass("is-active")},selectImage:function(e){l.currentImage=e},insertImage:function(e){a.codemirror.doc.somethingSelected()&&a.codemirror.execCommand("singleSelection");var o=_.find(l.images,["_id",l.currentImage]);o.normalizedPath="f:"===o.folder?o.filename:o.folder.slice(2)+"/"+o.filename,o.titleGuess=_.startCase(o.basename);var t="!["+o.titleGuess+"](/uploads/"+o.normalizedPath+' "'+o.titleGuess+'")';switch(l.currentAlign){case"center":t+="{.align-center}";break;case"right":t+="{.align-right}";break;case"logo":t+="{.pagelogo}"}a.codemirror.doc.replaceSelection(t),l.cancel()},newFolder:function(o){l.newFolderName="",l.newFolderError=!1,l.newFolderShow=!0,_.delay(function(){e("#txt-editor-image-newfoldername").focus()},400)},newFolderDiscard:function(e){l.newFolderShow=!1},newFolderCreate:function(e){var o=new RegExp("^[a-z0-9][a-z0-9-]*[a-z0-9]$");return l.newFolderName=_.kebabCase(_.trim(l.newFolderName)),_.isEmpty(l.newFolderName)||!o.test(l.newFolderName)?void(l.newFolderError=!0):(l.newFolderDiscard(),l.isLoadingText="Creating new folder...",l.isLoading=!0,void Vue.nextTick(function(){n.emit("uploadsCreateFolder",{foldername:l.newFolderName},function(e){l.folders=e,l.currentFolder=l.newFolderName,l.images=[],l.isLoading=!1})}))},fetchFromUrl:function(o){l.fetchFromUrlURL="",l.fetchFromUrlShow=!0,_.delay(function(){e("#txt-editor-image-fetchurl").focus()},400)},fetchFromUrlDiscard:function(e){l.fetchFromUrlShow=!1},fetchFromUrlGo:function(e){l.fetchFromUrlDiscard(),l.isLoadingText="Fetching image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsFetchFileFromURL",{folder:l.currentFolder,fetchUrl:l.fetchFromUrlURL},function(e){e.ok?l.waitChangeComplete(l.images.length,!0):(l.isLoading=!1,t.pushError("Upload error",e.msg))})})},renameImage:function(){var o=_.find(l.images,["_id",l.renameImageId]);l.renameImageFilename=o.basename||"",l.renameImageShow=!0,_.delay(function(){e("#txt-editor-image-rename").focus(),_.defer(function(){e("#txt-editor-image-rename").select()})},400)},renameImageDiscard:function(){l.renameImageShow=!1},renameImageGo:function(){l.renameImageDiscard(),l.isLoadingText="Renaming image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsRenameFile",{uid:l.renameImageId,folder:l.currentFolder,filename:l.renameImageFilename},function(e){e.ok?l.waitChangeComplete(l.images.length,!1):(l.isLoading=!1,t.pushError("Rename error",e.msg))})})},moveImage:function(e,o){l.isLoadingText="Moving image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsMoveFile",{uid:e,folder:o},function(e){e.ok?l.loadImages():(l.isLoading=!1,t.pushError("Rename error",e.msg))})})},deleteImageWarn:function(e){if(e){var o=_.find(l.images,["_id",l.deleteImageId]);l.deleteImageFilename=o.filename||"this image"}l.deleteImageShow=e},deleteImageGo:function(){l.deleteImageWarn(!1),l.isLoadingText="Deleting image...",l.isLoading=!0,Vue.nextTick(function(){n.emit("uploadsDeleteFile",{uid:l.deleteImageId},function(e){l.loadImages()})})},selectFolder:function(e){l.currentFolder=e,l.loadImages()},refreshFolders:function(){l.isLoadingText="Fetching folders list...",l.isLoading=!0,l.currentFolder="",l.currentImage="",Vue.nextTick(function(){n.emit("uploadsGetFolders",{},function(e){l.folders=e,l.loadImages()})})},loadImages:function(e){return e||(l.isLoadingText="Fetching images...",l.isLoading=!0),new Promise(function(o,t){Vue.nextTick(function(){n.emit("uploadsGetImages",{folder:l.currentFolder},function(t){l.images=t,e||(l.isLoading=!1),l.attachContextMenus(),o(!0)})})})},waitChangeComplete:function(e,o){o=!_.isBoolean(o)||o,l.postUploadChecks++,l.isLoadingText="Processing...",Vue.nextTick(function(){l.loadImages(!0).then(function(){l.images.length!==e===o?(l.postUploadChecks=0,l.isLoading=!1):l.postUploadChecks>5?(l.postUploadChecks=0,l.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){l.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(l.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(l.folders,o);l.moveImage(n,i)}}});e.contextMenu("destroy",".editor-modal-image-choices > figure"),e.contextMenu({selector:".editor-modal-image-choices > figure",appendTo:".editor-modal-image-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/2,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){l.renameImageId=_.toString(o.$trigger[0].dataset.uid),l.renameImage()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){l.deleteImageId=_.toString(o.$trigger[0].dataset.uid),l.deleteImageWarn(!0)}}}})}}});e("#btn-editor-image-upload input").on("change",function(n){var i=l.images.length;e(n.currentTarget).simpleUpload("/uploads/img",{name:"imgfile",data:{folder:l.currentFolder},limit:20,expect:"json",allowedExts:["jpg","jpeg","gif","png","webp"],allowedTypes:["image/png","image/jpeg","image/gif","image/webp"],maxFileSize:3145728,init:function(e){l.uploadSucceeded=!1,l.isLoadingText="Preparing to upload...",l.isLoading=!0},progress:function(e){l.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(d.postUploadChecks=0,d.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){d.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(d.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(d.folders,o);d.moveFile(n,i)}}});e.contextMenu("destroy",".editor-modal-file-choices > figure"),e.contextMenu({selector:".editor-modal-file-choices > figure",appendTo:".editor-modal-file-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/5,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){d.renameFileId=_.toString(o.$trigger[0].dataset.uid),d.renameFile()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){d.deleteFileId=_.toString(o.$trigger[0].dataset.uid),d.deleteFileWarn(!0)}}}})}}});e("#btn-editor-file-upload input").on("change",function(n){var i=d.files.length;e(n.currentTarget).simpleUpload("/uploads/file",{name:"binfile",data:{folder:d.currentFolder},limit:20,expect:"json",maxFileSize:0,init:function(e){d.uploadSucceeded=!1,d.isLoadingText="Preparing to upload...",d.isLoading=!0},progress:function(e){d.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(l.postUploadChecks=0,l.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){l.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(l.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(l.folders,o);l.moveImage(n,i)}}});e.contextMenu("destroy",".editor-modal-image-choices > figure"),e.contextMenu({selector:".editor-modal-image-choices > figure",appendTo:".editor-modal-image-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/2,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){l.renameImageId=_.toString(o.$trigger[0].dataset.uid),l.renameImage()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){l.deleteImageId=_.toString(o.$trigger[0].dataset.uid),l.deleteImageWarn(!0)}}}})}}});e("#btn-editor-image-upload input").on("change",function(n){var i=l.images.length;e(n.currentTarget).simpleUpload("/uploads/img",{name:"imgfile",data:{folder:l.currentFolder},limit:20,expect:"json",allowedExts:["jpg","jpeg","gif","png","webp"],allowedTypes:["image/png","image/jpeg","image/gif","image/webp"],maxFileSize:3145728,init:function(e){l.uploadSucceeded=!1,l.isLoadingText="Preparing to upload...",l.isLoading=!0},progress:function(e){l.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length5?(d.postUploadChecks=0, +d.isLoading=!1,t.pushError("Unable to fetch updated listing","Try again later")):_.delay(function(){d.waitChangeComplete(e,o)},1500)})})},attachContextMenus:function(){var o=_.map(d.folders,function(o){return{name:""!==o?o:"/ (root)",icon:"fa-folder",callback:function(o,t){var n=_.toString(e(t.$trigger).data("uid")),i=_.nth(d.folders,o);d.moveFile(n,i)}}});e.contextMenu("destroy",".editor-modal-file-choices > figure"),e.contextMenu({selector:".editor-modal-file-choices > figure",appendTo:".editor-modal-file-choices",position:function(o,t,n){e(o.$trigger).addClass("is-contextopen");var i=e(o.$trigger).position(),a={w:e(o.$trigger).width()/5,h:e(o.$trigger).height()/2};o.$menu.css({top:i.top+a.h,left:i.left+a.w})},events:{hide:function(o){e(o.$trigger).removeClass("is-contextopen")}},items:{rename:{name:"Rename",icon:"fa-edit",callback:function(e,o){d.renameFileId=_.toString(o.$trigger[0].dataset.uid),d.renameFile()}},move:{name:"Move to...",icon:"fa-folder-open-o",items:o},delete:{name:"Delete",icon:"fa-trash",callback:function(e,o){d.deleteFileId=_.toString(o.$trigger[0].dataset.uid),d.deleteFileWarn(!0)}}}})}}});e("#btn-editor-file-upload input").on("change",function(n){var i=d.files.length;e(n.currentTarget).simpleUpload("/uploads/file",{name:"binfile",data:{folder:d.currentFolder},limit:20,expect:"json",maxFileSize:0,init:function(e){d.uploadSucceeded=!1,d.isLoadingText="Preparing to upload...",d.isLoading=!0},progress:function(e){d.isLoadingText="Uploading..."+Math.round(e)+"%"},success:function(e){if(e.ok){var o=_.filter(e.results,["ok",!1]);o.length?(_.forEach(o,function(e){t.pushError("Upload error",e.msg)}),o.length=0&&n&&(n.class+=" exit",Vue.set(o.mdl.children,t,n),_.delay(function(){o.mdl.children.splice(t,1)},500))}}]),e}(); \ No newline at end of file diff --git a/client/js/components/search.js b/client/js/components/search.js index ab67f278..3de556fa 100644 --- a/client/js/components/search.js +++ b/client/js/components/search.js @@ -42,7 +42,7 @@ if ($('#search-input').length) { searchmoveidx: (val, oldVal) => { if (val > 0) { vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) - ? 'res.' + vueHeader.searchmovearr[val - 1]._id + ? 'res.' + vueHeader.searchmovearr[val - 1].entryPath : 'sug.' + vueHeader.searchmovearr[val - 1] } else { vueHeader.searchmovekey = '' @@ -61,7 +61,7 @@ if ($('#search-input').length) { let i = vueHeader.searchmoveidx - 1 if (vueHeader.searchmovearr[i]) { - window.location.assign('/' + vueHeader.searchmovearr[i]._id) + window.location.assign('/' + vueHeader.searchmovearr[i].entryPath) } else { vueHeader.searchq = vueHeader.searchmovearr[i] } diff --git a/controllers/ws.js b/controllers/ws.js index c899c604..efaae330 100644 --- a/controllers/ws.js +++ b/controllers/ws.js @@ -13,7 +13,7 @@ module.exports = (socket) => { socket.on('search', (data, cb) => { cb = cb || _.noop - entries.search(data.terms).then((results) => { + search.find(data.terms).then((results) => { return cb(results) || true }) }) diff --git a/libs/entries.js b/libs/entries.js index bd20b6e7..134da8d6 100644 --- a/libs/entries.js +++ b/libs/entries.js @@ -256,7 +256,9 @@ module.exports = { return fs.statAsync(fpath).then((st) => { if (st.isFile()) { return self.makePersistent(entryPath, contents).then(() => { - return self.updateCache(entryPath) + return self.updateCache(entryPath).then(entry => { + return search.add(entry) + }) }) } else { return Promise.reject(new Error('Entry does not exist!')) diff --git a/libs/git.js b/libs/git.js index e8bdce9c..ffb5b299 100644 --- a/libs/git.js +++ b/libs/git.js @@ -68,13 +68,13 @@ module.exports = { _initRepo (appconfig) { let self = this - winston.info('[' + PROCNAME + '][GIT] Checking Git repository...') + winston.info('[' + PROCNAME + '.Git] Checking Git repository...') // -> Check if path is accessible return fs.mkdirAsync(self._repo.path).catch((err) => { if (err.code !== 'EEXIST') { - winston.error('[' + PROCNAME + '][GIT] Invalid Git repository path or missing permissions.') + winston.error('[' + PROCNAME + '.Git] Invalid Git repository path or missing permissions.') } }).then(() => { self._git = new Git({ 'git-dir': self._repo.path }) @@ -89,7 +89,7 @@ module.exports = { }) }).then(() => { if (appconfig.git === false) { - winston.info('[' + PROCNAME + '][GIT] Remote syncing is disabled. Not recommended!') + winston.info('[' + PROCNAME + '.Git] Remote syncing is disabled. Not recommended!') return Promise.resolve(true) } @@ -126,10 +126,10 @@ module.exports = { } }) }).catch((err) => { - winston.error('[' + PROCNAME + '][GIT] Git remote error!') + winston.error('[' + PROCNAME + '.Git] Git remote error!') throw err }).then(() => { - winston.info('[' + PROCNAME + '][GIT] Git repository is OK.') + winston.info('[' + PROCNAME + '.Git] Git repository is OK.') return true }) }, @@ -159,12 +159,12 @@ module.exports = { // Fetch - winston.info('[' + PROCNAME + '][GIT] Performing pull from remote repository...') + winston.info('[' + PROCNAME + '.Git] Performing pull from remote repository...') return self._git.pull('origin', self._repo.branch).then((cProc) => { - winston.info('[' + PROCNAME + '][GIT] Pull completed.') + winston.info('[' + PROCNAME + '.Git] Pull completed.') }) .catch((err) => { - winston.error('[' + PROCNAME + '][GIT] Unable to fetch from git origin!') + winston.error('[' + PROCNAME + '.Git] Unable to fetch from git origin!') throw err }) .then(() => { @@ -174,12 +174,12 @@ module.exports = { let out = cProc.stdout.toString() if (_.includes(out, 'commit')) { - winston.info('[' + PROCNAME + '][GIT] Performing push to remote repository...') + winston.info('[' + PROCNAME + '.Git] Performing push to remote repository...') return self._git.push('origin', self._repo.branch).then(() => { - return winston.info('[' + PROCNAME + '][GIT] Push completed.') + return winston.info('[' + PROCNAME + '.Git] Push completed.') }) } else { - winston.info('[' + PROCNAME + '][GIT] Push skipped. Repository is already in sync.') + winston.info('[' + PROCNAME + '.Git] Push skipped. Repository is already in sync.') } return true diff --git a/libs/local.js b/libs/local.js index 7fd56d8a..f243e5f3 100644 --- a/libs/local.js +++ b/libs/local.js @@ -98,11 +98,12 @@ module.exports = { * @return {Void} Void */ createBaseDirectories (appconfig) { - winston.info('[SERVER] Checking data directories...') + winston.info('[SERVER.Local] Checking data directories...') try { fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data)) fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache')) + fs.emptyDirSync(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')) @@ -120,7 +121,7 @@ module.exports = { winston.error(err) } - winston.info('[SERVER] Data and Repository directories are OK.') + winston.info('[SERVER.Local] Data and Repository directories are OK.') return }, diff --git a/libs/search.js b/libs/search.js new file mode 100644 index 00000000..145011bc --- /dev/null +++ b/libs/search.js @@ -0,0 +1,206 @@ +'use strict' + +const Promise = require('bluebird') +const _ = require('lodash') +const path = require('path') +const searchIndex = require('search-index') +const stopWord = require('stopword') +const streamToPromise = require('stream-to-promise') + +module.exports = { + + _si: null, + _isReady: false, + + /** + * Initialize search index + * + * @return {undefined} Void + */ + init () { + let self = this + let dbPath = path.resolve(ROOTPATH, appconfig.paths.data, 'search') + self._isReady = new Promise((resolve, reject) => { + searchIndex({ + deletable: true, + fieldedSearch: true, + indexPath: dbPath, + logLevel: 'error', + stopwords: _.get(stopWord, appconfig.lang, []) + }, (err, si) => { + if (err) { + winston.error('[SERVER.Search] Failed to initialize search index.', err) + reject(err) + } else { + self._si = Promise.promisifyAll(si) + self._si.flushAsync().then(() => { + winston.info('[SERVER.Search] Search index flushed and ready.') + resolve(true) + }) + } + }) + }) + + return self + }, + + /** + * 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._isReady.then(() => { + return self.delete(content._id).then(() => { + return self._si.concurrentAddAsync({ + fieldOptions: [{ + fieldName: 'entryPath', + searchable: true, + weight: 2 + }, + { + fieldName: 'title', + nGramLength: [1, 2], + searchable: true, + weight: 3 + }, + { + fieldName: 'subtitle', + searchable: true, + weight: 1, + storeable: false + }, + { + fieldName: 'parent', + searchable: false + }, + { + fieldName: 'content', + searchable: true, + weight: 0, + storeable: false + }] + }, [{ + entryPath: content._id, + title: content.title, + subtitle: content.subtitle || '', + parent: content.parent || '', + content: content.content || '' + }]).then(() => { + winston.log('verbose', '[SERVER.Search] Entry ' + content._id + ' 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._isReady.then(() => { + return streamToPromise(self._si.search({ + 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) + } + }) + }) + }, + + /** + * Flush the index + * + * @returns {Promise} Promise of the flush operation + */ + flush () { + let self = this + return self._isReady.then(() => { + return self._si.flushAsync() + }) + }, + + /** + * Search the index + * + * @param {Array} terms + * @returns {Promise} Hits and suggestions + */ + 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 streamToPromise(self._si.search({ + query: [{ + AND: { '*': arrTerms } + }], + pageSize: 10 + })).then((hits) => { + if (hits.length > 0) { + hits = _.map(_.sortBy(hits, ['score']), h => { + return h.document + }) + } + if (hits.length < 5) { + return streamToPromise(self._si.match({ + 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) + } + }) + } +} diff --git a/package.json b/package.json index 2b2f7673..809b80a5 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,8 @@ "simplemde": "^1.11.2", "socket.io": "^1.7.2", "sticky-js": "^1.0.7", + "stopword": "^0.1.1", + "stream-to-promise": "^2.2.0", "validator": "^6.2.0", "validator-as-promised": "^1.0.2", "winston": "^2.3.0" @@ -139,7 +141,6 @@ "app", "appconfig", "appdata", - "bgAgent", "db", "entries", "git", @@ -147,6 +148,7 @@ "lang", "lcdata", "rights", + "search", "upl", "winston", "ws", diff --git a/server.js b/server.js index 8bd8817c..e56ed01b 100644 --- a/server.js +++ b/server.js @@ -37,6 +37,7 @@ global.entries = require('./libs/entries').init() global.git = require('./libs/git').init(false) global.lang = require('i18next') global.mark = require('./libs/markdown') +global.search = require('./libs/search').init() global.upl = require('./libs/uploads').init() // ---------------------------------------- @@ -239,7 +240,19 @@ io.on('connection', ctrl.ws) // Start child processes // ---------------------------------------- -global.bgAgent = fork('agent.js') +let bgAgent = fork('agent.js') + +bgAgent.on('message', m => { + if (!m.action) { + return + } + + switch (m.action) { + case 'searchAdd': + search.add(m.content) + break + } +}) process.on('exit', (code) => { bgAgent.disconnect() diff --git a/views/common/header.pug b/views/common/header.pug index 0047a696..aede12f2 100644 --- a/views/common/header.pug +++ b/views/common/header.pug @@ -26,8 +26,8 @@ ul.searchresults-list li(v-if='searchres.length === 0') a: em No results matching your query - li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres._id }') - a(v-bind:href='"/" + sres._id') {{ sres.title }} + li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }') + a(v-bind:href='"/" + sres.entryPath') {{ sres.title }} p.searchresults-label(v-if='searchsuggest.length > 0') Did you mean...? ul.searchresults-list(v-if='searchsuggest.length > 0') li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }')