WebSocket server + Search index + indexable content parser
This commit is contained in:
parent
528fab6c87
commit
7945d024ad
62
agent.js
62
agent.js
@ -6,11 +6,36 @@
|
|||||||
|
|
||||||
global.ROOTPATH = __dirname;
|
global.ROOTPATH = __dirname;
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Load Winston
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
var _isDebug = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
global.winston = require('winston');
|
||||||
|
winston.remove(winston.transports.Console)
|
||||||
|
winston.add(winston.transports.Console, {
|
||||||
|
level: (_isDebug) ? 'info' : 'warn',
|
||||||
|
prettyPrint: true,
|
||||||
|
colorize: true,
|
||||||
|
silent: false,
|
||||||
|
timestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Fetch internal handshake key
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
if(!process.argv[2] || process.argv[2].length !== 40) {
|
||||||
|
winston.error('[WS] Illegal process start. Missing handshake key.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
global.WSInternalKey = process.argv[2];
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Load modules
|
// Load modules
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
global.winston = require('winston');
|
|
||||||
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');
|
||||||
@ -18,7 +43,6 @@ var appconfig = require('./models/config')('./config.yml');
|
|||||||
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.search = require('./models/search').init(appconfig);
|
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
@ -26,6 +50,8 @@ var Promise = require('bluebird');
|
|||||||
var fs = Promise.promisifyAll(require("fs-extra"));
|
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;
|
||||||
|
var wsClient = require('socket.io-client');
|
||||||
|
global.ws = wsClient('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Start Cron
|
// Start Cron
|
||||||
@ -90,8 +116,11 @@ var job = new cron({
|
|||||||
//-> Update search index
|
//-> Update search index
|
||||||
|
|
||||||
if(fileStatus !== 'active') {
|
if(fileStatus !== 'active') {
|
||||||
return entries.fetchTextVersion(entryPath).then((content) => {
|
return entries.fetchIndexableVersion(entryPath).then((content) => {
|
||||||
console.log(content);
|
ws.emit('searchAdd', {
|
||||||
|
auth: WSInternalKey,
|
||||||
|
content
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,25 +143,42 @@ var job = new cron({
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
Promise.all(jobs).then(() => {
|
Promise.all(jobs).then(() => {
|
||||||
winston.info('[AGENT] All jobs completed successfully! Going to sleep for now... [' + moment().toISOString() + ']');
|
winston.info('[AGENT] All jobs completed successfully! Going to sleep for now...');
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
winston.error('[AGENT] One or more jobs have failed [' + moment().toISOString() + ']: ', err);
|
winston.error('[AGENT] One or more jobs have failed: ', err);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
jobIsBusy = false;
|
jobIsBusy = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
start: true,
|
start: false,
|
||||||
timeZone: 'UTC',
|
timeZone: 'UTC',
|
||||||
runOnInit: true
|
runOnInit: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Connect to local WebSocket server
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
ws.on('connect', function () {
|
||||||
|
job.start();
|
||||||
|
winston.info('[AGENT] Background Agent started successfully! [RUNNING]');
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('connect_error', function () {
|
||||||
|
winston.warn('[AGENT] Unable to connect to WebSocket server! Retrying...');
|
||||||
|
});
|
||||||
|
ws.on('reconnect_failed', function () {
|
||||||
|
winston.error('[AGENT] Failed to reconnect to WebSocket server too many times! Stopping agent...');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Shutdown gracefully
|
// Shutdown gracefully
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
process.on('disconnect', () => {
|
process.on('disconnect', () => {
|
||||||
winston.warn('[AGENT] Lost connection to main server. Exiting... [' + moment().toISOString() + ']');
|
winston.warn('[AGENT] Lost connection to main server. Exiting...');
|
||||||
job.stop();
|
job.stop();
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function setInputSelection(e,t,a){if(e.focus(),"undefined"!=typeof e.selectionStart)e.selectionStart=t,e.selectionEnd=a;else if(document.selection&&document.selection.createRange){e.select();var n=document.selection.createRange();n.collapse(!0),n.moveEnd("character",a),n.moveStart("character",t),n.select()}}function makeSafePath(e){var t=_.split(_.trim(e),"/");return t=_.map(t,function(e){return _.kebabCase(_.deburr(_.trim(e)))}),_.join(_.filter(t,function(e){return!_.isEmpty(e)}),"/")}var _createClass=function(){function e(e,t){for(var a=0;a<t.length;a++){var n=t[a];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,a,n){return a&&e(t.prototype,a),n&&e(t,n),t}}(),Alerts=function(){function e(){_classCallCheck(this,e);var t=this;t.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){t.close(e)}}}),t.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var t=this,a=_.defaults(e,{_uid:t.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});t.mdl.children.push(a),a.sticky||_.delay(function(){t.close(a._uid)},5e3),t.uidNext++}},{key:"pushError",value:function(e,t){this.push({class:"is-danger",message:t,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,t){this.push({class:"is-success",message:t,sticky:!1,title:e})}},{key:"close",value:function(e){var t=this,a=_.findIndex(t.mdl.children,["_uid",e]),n=_.nth(t.mdl.children,a);a>=0&&n&&(n.class+=" exit",t.mdl.children.$set(a,n),_.delay(function(){t.mdl.children.$remove(n)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});new Sticky(".stickyscroll");e(window).bind("beforeunload",function(){e("#notifload").addClass("active")}),e(document).ajaxSend(function(){e("#notifload").addClass("active")}).ajaxComplete(function(){e("#notifload").removeClass("active")});var t=new Alerts;if(alertsData&&_.forEach(alertsData,function(e){t.push(e)}),1===e("#mk-editor").length)var a=new SimpleMDE({autofocus:!0,autoDownloadFontAwesome:!1,element:e("#mk-editor").get(0),hideIcons:["heading","quote"],placeholder:"Enter Markdown formatted content here...",showIcons:["strikethrough","heading-1","heading-2","heading-3","code","table","horizontal-rule"],spellChecker:!1,status:!1});if(e("#page-type-view").length&&!function(){var t="home"!==e("#page-type-view").data("entrypath")?e("#page-type-view").data("entrypath")+"/":"",a=t+"new-page";e(".btn-create-prompt").on("click",function(n){e("#txt-create-prompt").val(a),e("#modal-create-prompt").toggleClass("is-active"),setInputSelection(e("#txt-create-prompt").get(0),t.length,a.length),e("#txt-create-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-create-prompt").on("keypress",function(t){13===t.which&&e(".btn-create-go").trigger("click")}),e(".btn-create-go").on("click",function(t){var a=makeSafePath(e("#txt-create-prompt").val());_.isEmpty(a)?e("#txt-create-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-create-prompt").parent().addClass("is-loading"),window.location.assign("/create/"+a))})}(),e("#page-type-create").length&&(e(".btn-create-discard").on("click",function(t){e("#modal-create-discard").toggleClass("is-active")}),e(".btn-create-save").on("click",function(n){e.ajax(window.location.href,{data:{markdown:a.value()},dataType:"json",method:"PUT"}).then(function(a,n,o){a.ok?window.location.assign("/"+e("#page-type-create").data("entrypath")):t.pushError("Something went wrong",a.error)},function(e,a,n){t.pushError("Something went wrong","Save operation failed.")})})),e("#page-type-edit").length){e(".editor-toolbar").attr("data-margin-top",e("#header").height());new Sticky(".editor-toolbar");e(".btn-edit-discard").on("click",function(t){e("#modal-edit-discard").toggleClass("is-active")}),e(".btn-edit-save").on("click",function(n){e.ajax(window.location.href,{data:{markdown:a.value()},dataType:"json",method:"PUT"}).then(function(a,n,o){a.ok?window.location.assign("/"+e("#page-type-edit").data("entrypath")):t.pushError("Something went wrong",a.error)},function(e,a,n){t.pushError("Something went wrong","Save operation failed.")})})}if(e("#page-type-source").length){var n=ace.edit("source-display");n.setTheme("ace/theme/tomorrow_night"),n.getSession().setMode("ace/mode/markdown"),n.setReadOnly(!0),n.renderer.updateFull()}});
|
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function setInputSelection(e,t,a){if(e.focus(),"undefined"!=typeof e.selectionStart)e.selectionStart=t,e.selectionEnd=a;else if(document.selection&&document.selection.createRange){e.select();var n=document.selection.createRange();n.collapse(!0),n.moveEnd("character",a),n.moveStart("character",t),n.select()}}function makeSafePath(e){var t=_.split(_.trim(e),"/");return t=_.map(t,function(e){return _.kebabCase(_.deburr(_.trim(e)))}),_.join(_.filter(t,function(e){return!_.isEmpty(e)}),"/")}var _createClass=function(){function e(e,t){for(var a=0;a<t.length;a++){var n=t[a];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,a,n){return a&&e(t.prototype,a),n&&e(t,n),t}}(),Alerts=function(){function e(){_classCallCheck(this,e);var t=this;t.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){t.close(e)}}}),t.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var t=this,a=_.defaults(e,{_uid:t.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});t.mdl.children.push(a),a.sticky||_.delay(function(){t.close(a._uid)},5e3),t.uidNext++}},{key:"pushError",value:function(e,t){this.push({class:"is-danger",message:t,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,t){this.push({class:"is-success",message:t,sticky:!1,title:e})}},{key:"close",value:function(e){var t=this,a=_.findIndex(t.mdl.children,["_uid",e]),n=_.nth(t.mdl.children,a);a>=0&&n&&(n.class+=" exit",t.mdl.children.$set(a,n),_.delay(function(){t.mdl.children.$remove(n)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});new Sticky(".stickyscroll");e(window).bind("beforeunload",function(){e("#notifload").addClass("active")}),e(document).ajaxSend(function(){e("#notifload").addClass("active")}).ajaxComplete(function(){e("#notifload").removeClass("active")});var t=new Alerts;if(alertsData&&_.forEach(alertsData,function(e){t.push(e)}),1===e("#mk-editor").length)var a=new SimpleMDE({autofocus:!0,autoDownloadFontAwesome:!1,element:e("#mk-editor").get(0),hideIcons:["heading","quote"],placeholder:"Enter Markdown formatted content here...",showIcons:["strikethrough","heading-1","heading-2","heading-3","code","table","horizontal-rule"],spellChecker:!1,status:!1});var n=io(ioHost),o=new Vue({el:"#header-container",data:{searchq:"",searchres:[]},watch:{searchq:function(e,t){e.length>=3&&n.emit("search",{terms:e},function(e){o.$set("searchres",e)})}},methods:{}});if(e("#page-type-view").length&&!function(){var t="home"!==e("#page-type-view").data("entrypath")?e("#page-type-view").data("entrypath")+"/":"",a=t+"new-page";e(".btn-create-prompt").on("click",function(n){e("#txt-create-prompt").val(a),e("#modal-create-prompt").toggleClass("is-active"),setInputSelection(e("#txt-create-prompt").get(0),t.length,a.length),e("#txt-create-prompt").removeClass("is-danger").next().addClass("is-hidden")}),e("#txt-create-prompt").on("keypress",function(t){13===t.which&&e(".btn-create-go").trigger("click")}),e(".btn-create-go").on("click",function(t){var a=makeSafePath(e("#txt-create-prompt").val());_.isEmpty(a)?e("#txt-create-prompt").addClass("is-danger").next().removeClass("is-hidden"):(e("#txt-create-prompt").parent().addClass("is-loading"),window.location.assign("/create/"+a))})}(),e("#page-type-create").length&&(e(".btn-create-discard").on("click",function(t){e("#modal-create-discard").toggleClass("is-active")}),e(".btn-create-save").on("click",function(n){e.ajax(window.location.href,{data:{markdown:a.value()},dataType:"json",method:"PUT"}).then(function(a,n,o){a.ok?window.location.assign("/"+e("#page-type-create").data("entrypath")):t.pushError("Something went wrong",a.error)},function(e,a,n){t.pushError("Something went wrong","Save operation failed.")})})),e("#page-type-edit").length){e(".editor-toolbar").attr("data-margin-top",e("#header").height());new Sticky(".editor-toolbar");e(".btn-edit-discard").on("click",function(t){e("#modal-edit-discard").toggleClass("is-active")}),e(".btn-edit-save").on("click",function(n){e.ajax(window.location.href,{data:{markdown:a.value()},dataType:"json",method:"PUT"}).then(function(a,n,o){a.ok?window.location.assign("/"+e("#page-type-edit").data("entrypath")):t.pushError("Something went wrong",a.error)},function(e,a,n){t.pushError("Something went wrong","Save operation failed.")})})}if(e("#page-type-source").length){var i=ace.edit("source-display");i.setTheme("ace/theme/tomorrow_night"),i.getSession().setMode("ace/mode/markdown"),i.setReadOnly(!0),i.renderer.updateFull()}});
|
File diff suppressed because one or more lines are too long
@ -52,6 +52,32 @@ jQuery( document ).ready(function( $ ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================================
|
||||||
|
// Establish WebSocket connection
|
||||||
|
// ====================================
|
||||||
|
|
||||||
|
var socket = io(ioHost);
|
||||||
|
|
||||||
|
var vueHeader = new Vue({
|
||||||
|
el: '#header-container',
|
||||||
|
data: {
|
||||||
|
searchq: '',
|
||||||
|
searchres: []
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
searchq: (val, oldVal) => {
|
||||||
|
if(val.length >= 3) {
|
||||||
|
socket.emit('search', { terms: val }, (data) => {
|
||||||
|
vueHeader.$set('searchres', data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Pages logic
|
// Pages logic
|
||||||
// ====================================
|
// ====================================
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
|
|
||||||
|
#page-type-view > section {
|
||||||
|
transition: background-color .5s ease;
|
||||||
|
|
||||||
|
&.blurred {
|
||||||
|
background-color: $grey-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.sd-menus {
|
.sd-menus {
|
||||||
|
|
||||||
|
@ -25,4 +25,15 @@ h2.nav-item {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchresults {
|
||||||
|
position: fixed;
|
||||||
|
top: 45px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 500px;
|
||||||
|
z-index: 1;
|
||||||
|
//display: none;
|
||||||
}
|
}
|
@ -1,38 +1,46 @@
|
|||||||
###################################################
|
#######################################################################
|
||||||
# REQUARKS WIKI - CONFIGURATION #
|
# REQUARKS WIKI - CONFIGURATION #
|
||||||
###################################################
|
#######################################################################
|
||||||
# Full explanation + examples in the documentation (https://requarks-wiki.readme.io/)
|
# Full explanation + examples in the documentation:
|
||||||
|
# https://requarks-wiki.readme.io/
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Title of this site
|
# Title of this site
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
title: Wiki
|
title: Wiki
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Full path to the site, without the trailing slash
|
# Full public path to the site, without the trailing slash
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
host: http://localhost
|
host: http://localhost
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Port the server should listen to (80 by default)
|
# Port the main server should listen to (80 by default)
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# To use process.env.PORT, comment the line below:
|
# To use process.env.PORT, comment the line below:
|
||||||
|
|
||||||
port: 80
|
port: 80
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
# Port the websocket server should listen to (8080 by default)
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Make sure this port is opened in the firewall if applicable
|
||||||
|
|
||||||
|
wsPort: 8080
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
# Data Directories
|
# Data Directories
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
datadir:
|
datadir:
|
||||||
repo: ./repo
|
repo: ./repo
|
||||||
db: ./data
|
db: ./data
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Git Connection Info
|
# Git Connection Info
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Organization/Repo
|
url: https://github.com/Organization/Repo
|
||||||
@ -52,24 +60,25 @@ git:
|
|||||||
privateKey: /etc/requarkswiki/keys/git.key
|
privateKey: /etc/requarkswiki/keys/git.key
|
||||||
sslVerify: true
|
sslVerify: true
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Secret key to use when encrypting sessions
|
# Secret key to use when encrypting sessions
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Use a long and unique random string (256-bit keys are perfect!)
|
# Use a long and unique random string (256-bit keys are perfect!)
|
||||||
|
|
||||||
sessionSecret: 1234567890abcdefghijklmnopqrstuvxyz
|
sessionSecret: 1234567890abcdefghijklmnopqrstuvxyz
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Administrator email
|
# Administrator email
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# An account will be created using the email specified here.
|
# An admin account will be created using the email specified here.
|
||||||
# The password is set to "admin123" by default. Change it immediately upon login!!!
|
# The password is set to "admin123" by default. Change it immediately
|
||||||
|
# upon login!!!
|
||||||
|
|
||||||
admin: admin@company.com
|
admin: admin@company.com
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Site UI Language
|
# Site Language
|
||||||
# -------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Possible values: en, fr
|
# Possible values: en, fr
|
||||||
|
|
||||||
lang: en
|
lang: en
|
@ -19,6 +19,7 @@ var include = require("gulp-include");
|
|||||||
*/
|
*/
|
||||||
var paths = {
|
var paths = {
|
||||||
scriptlibs: [
|
scriptlibs: [
|
||||||
|
'./node_modules/socket.io-client/socket.io.js',
|
||||||
'./node_modules/jquery/dist/jquery.min.js',
|
'./node_modules/jquery/dist/jquery.min.js',
|
||||||
'./node_modules/vue/dist/vue.min.js',
|
'./node_modules/vue/dist/vue.min.js',
|
||||||
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
|
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
|
||||||
|
32
lib/internalAuth.js
Normal file
32
lib/internalAuth.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal Authentication
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
_curKey: false,
|
||||||
|
|
||||||
|
init(inKey) {
|
||||||
|
|
||||||
|
this._curKey = inKey;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
generateKey() {
|
||||||
|
|
||||||
|
return crypto.randomBytes(20).toString('hex')
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
validateKey(inKey) {
|
||||||
|
|
||||||
|
return inKey === this._curKey;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -183,7 +183,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {String} Text-only version
|
* @return {String} Text-only version
|
||||||
*/
|
*/
|
||||||
fetchTextVersion(entryPath) {
|
fetchIndexableVersion(entryPath) {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
@ -192,11 +192,13 @@ module.exports = {
|
|||||||
parseMeta: true,
|
parseMeta: true,
|
||||||
parseTree: false,
|
parseTree: false,
|
||||||
includeMarkdown: true,
|
includeMarkdown: true,
|
||||||
includeParentInfo: false,
|
includeParentInfo: true,
|
||||||
cache: false
|
cache: false
|
||||||
}).then((pageData) => {
|
}).then((pageData) => {
|
||||||
return {
|
return {
|
||||||
|
entryPath,
|
||||||
meta: pageData.meta,
|
meta: pageData.meta,
|
||||||
|
parent: pageData.parent || {},
|
||||||
text: mark.removeMarkdown(pageData.markdown)
|
text: mark.removeMarkdown(pageData.markdown)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -160,7 +160,7 @@ module.exports = {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
winston.info('[GIT] Repository is already in sync.');
|
winston.info('[GIT] Push skipped. Repository is already in sync.');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
var Promise = require('bluebird'),
|
var Promise = require('bluebird'),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
searchIndex = Promise.promisifyAll(require('search-index')),
|
searchIndex = require('search-index'),
|
||||||
stopWord = require('stopword');
|
stopWord = require('stopword');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,9 +21,10 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
init(appconfig) {
|
init(appconfig) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search-index');
|
let dbPath = path.resolve(ROOTPATH, appconfig.datadir.db, 'search-index');
|
||||||
|
|
||||||
this._si = searchIndex({
|
searchIndex({
|
||||||
deletable: true,
|
deletable: true,
|
||||||
fieldedSearch: true,
|
fieldedSearch: true,
|
||||||
indexPath: dbPath,
|
indexPath: dbPath,
|
||||||
@ -32,11 +33,86 @@ module.exports = {
|
|||||||
}, (err, si) => {
|
}, (err, si) => {
|
||||||
if(err) {
|
if(err) {
|
||||||
winston.error('Failed to initialize search-index.', 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, '')
|
||||||
|
.split(' ')
|
||||||
|
.filter((f) => { return !_.isEmpty(f); })
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return self._si.searchAsync({
|
||||||
|
query: {
|
||||||
|
AND: [{ '*': terms }]
|
||||||
|
},
|
||||||
|
pageSize: 10
|
||||||
|
}).get('hits');
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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._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: 'subtitle',
|
||||||
|
searchable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'content',
|
||||||
|
searchable: true,
|
||||||
|
weight: 0,
|
||||||
|
store: false
|
||||||
|
}]
|
||||||
|
}).then(() => {
|
||||||
|
winston.info('Entry ' + content.entryPath + ' added to index.');
|
||||||
|
}).catch((err) => {
|
||||||
|
winston.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
@ -76,6 +76,7 @@
|
|||||||
"search-index": "^0.8.15",
|
"search-index": "^0.8.15",
|
||||||
"serve-favicon": "^2.3.0",
|
"serve-favicon": "^2.3.0",
|
||||||
"simplemde": "^1.11.2",
|
"simplemde": "^1.11.2",
|
||||||
|
"socket.io": "^1.4.8",
|
||||||
"validator": "^5.5.0",
|
"validator": "^5.5.0",
|
||||||
"validator-as-promised": "^1.0.2",
|
"validator-as-promised": "^1.0.2",
|
||||||
"winston": "^2.2.0"
|
"winston": "^2.2.0"
|
||||||
|
24
server.js
24
server.js
@ -10,7 +10,18 @@ global.ROOTPATH = __dirname;
|
|||||||
// Load global modules
|
// Load global modules
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
|
var _isDebug = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
global.winston = require('winston');
|
global.winston = require('winston');
|
||||||
|
winston.remove(winston.transports.Console)
|
||||||
|
winston.add(winston.transports.Console, {
|
||||||
|
level: (_isDebug) ? 'info' : 'warn',
|
||||||
|
prettyPrint: true,
|
||||||
|
colorize: true,
|
||||||
|
silent: false,
|
||||||
|
timestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
winston.info('[SERVER] Requarks Wiki is initializing...');
|
winston.info('[SERVER] Requarks Wiki is initializing...');
|
||||||
|
|
||||||
var appconfig = require('./models/config')('./config.yml');
|
var appconfig = require('./models/config')('./config.yml');
|
||||||
@ -161,8 +172,6 @@ app.use(function(err, req, res, next) {
|
|||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
winston.info('[SERVER] Requarks Wiki has initialized successfully.');
|
|
||||||
|
|
||||||
winston.info('[SERVER] Starting HTTP server on port ' + appconfig.port + '...');
|
winston.info('[SERVER] Starting HTTP server on port ' + appconfig.port + '...');
|
||||||
|
|
||||||
app.set('port', appconfig.port);
|
app.set('port', appconfig.port);
|
||||||
@ -193,12 +202,17 @@ server.on('listening', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Start Agents
|
// Start child processes
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
var fork = require('child_process').fork;
|
var fork = require('child_process').fork,
|
||||||
var bgAgent = fork('agent.js');
|
libInternalAuth = require('./lib/internalAuth'),
|
||||||
|
internalAuthKey = libInternalAuth.generateKey();
|
||||||
|
|
||||||
|
var wsSrv = fork('ws-server.js', [internalAuthKey]),
|
||||||
|
bgAgent = fork('agent.js', [internalAuthKey]);
|
||||||
|
|
||||||
process.on('exit', (code) => {
|
process.on('exit', (code) => {
|
||||||
|
wsSrv.disconnect();
|
||||||
bgAgent.disconnect();
|
bgAgent.disconnect();
|
||||||
});
|
});
|
@ -1,34 +1,45 @@
|
|||||||
|
|
||||||
nav.nav.has-shadow.stickyscroll#header
|
#header-container
|
||||||
.nav-left
|
nav.nav.has-shadow.stickyscroll#header
|
||||||
block rootNavLeft
|
.nav-left
|
||||||
a.nav-item.is-brand(href='/')
|
block rootNavLeft
|
||||||
img(src='/favicons/android-icon-96x96.png', alt='Wiki')
|
a.nav-item.is-brand(href='/')
|
||||||
a.nav-item(href='/')
|
img(src='/favicons/android-icon-96x96.png', alt='Wiki')
|
||||||
h1.title Wiki
|
a.nav-item(href='/')
|
||||||
.nav-center
|
h1.title Wiki
|
||||||
block rootNavCenter
|
.nav-center
|
||||||
p.nav-item
|
block rootNavCenter
|
||||||
input.input(type='text', placeholder='Search...', style= { 'max-width': '300px', width: '33vw' })
|
p.nav-item
|
||||||
span.nav-toggle
|
input.input(type='text', v-model='searchq', debounce='500' placeholder='Search...', style= { 'max-width': '300px', width: '33vw' })
|
||||||
span
|
span.nav-toggle
|
||||||
span
|
span
|
||||||
span
|
span
|
||||||
.nav-right.nav-menu
|
span
|
||||||
block rootNavRight
|
.nav-right.nav-menu
|
||||||
i.nav-item#notifload
|
block rootNavRight
|
||||||
a.nav-item(href='/history/' + pageData.meta.path)
|
i.nav-item#notifload
|
||||||
| History
|
a.nav-item(href='/history/' + pageData.meta.path)
|
||||||
a.nav-item(href='/source/' + pageData.meta.path)
|
| History
|
||||||
| Source
|
a.nav-item(href='/source/' + pageData.meta.path)
|
||||||
span.nav-item
|
| Source
|
||||||
a.button(href='/edit/' + pageData.meta.path)
|
span.nav-item
|
||||||
span.icon
|
a.button(href='/edit/' + pageData.meta.path)
|
||||||
i.fa.fa-edit
|
span.icon
|
||||||
span Edit
|
i.fa.fa-edit
|
||||||
a.button.is-primary.btn-create-prompt
|
span Edit
|
||||||
span.icon
|
a.button.is-primary.btn-create-prompt
|
||||||
i.fa.fa-plus
|
span.icon
|
||||||
span Create
|
i.fa.fa-plus
|
||||||
|
span Create
|
||||||
|
|
||||||
|
.box.searchresults
|
||||||
|
.menu
|
||||||
|
p.menu-label
|
||||||
|
| Search Results
|
||||||
|
ul.menu-list
|
||||||
|
li(v-for='sres in searchres')
|
||||||
|
a(href='#') {{ sres.document.title }}
|
||||||
|
p.menu-label
|
||||||
|
| Do you mean...?
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ html
|
|||||||
// JS
|
// JS
|
||||||
script(type='text/javascript', src='/js/libs.js')
|
script(type='text/javascript', src='/js/libs.js')
|
||||||
script(type='text/javascript', src='/js/app.js')
|
script(type='text/javascript', src='/js/app.js')
|
||||||
|
script(type='text/javascript').
|
||||||
|
var ioHost = window.location.origin + ':!{appconfig.wsPort}/';
|
||||||
|
|
||||||
block head
|
block head
|
||||||
|
|
||||||
|
137
ws-server.js
Normal file
137
ws-server.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// ===========================================
|
||||||
|
// REQUARKS WIKI - WebSocket Server
|
||||||
|
// 1.0.0
|
||||||
|
// Licensed under AGPLv3
|
||||||
|
// ===========================================
|
||||||
|
|
||||||
|
global.ROOTPATH = __dirname;
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Load Winston
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
var _isDebug = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
global.winston = require('winston');
|
||||||
|
winston.remove(winston.transports.Console)
|
||||||
|
winston.add(winston.transports.Console, {
|
||||||
|
level: (_isDebug) ? 'info' : 'warn',
|
||||||
|
prettyPrint: true,
|
||||||
|
colorize: true,
|
||||||
|
silent: false,
|
||||||
|
timestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Fetch internal handshake key
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
if(!process.argv[2] || process.argv[2].length !== 40) {
|
||||||
|
winston.error('[WS] Illegal process start. Missing handshake key.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
global.internalAuth = require('./lib/internalAuth').init(process.argv[2]);;
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Load modules
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
winston.info('[WS] WS Server is initializing...');
|
||||||
|
|
||||||
|
var appconfig = require('./models/config')('./config.yml');
|
||||||
|
|
||||||
|
global.entries = require('./models/entries').init(appconfig);
|
||||||
|
global.mark = require('./models/markdown');
|
||||||
|
global.search = require('./models/search').init(appconfig);
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Load modules
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
var express = require('express');
|
||||||
|
var path = require('path');
|
||||||
|
var http = require('http');
|
||||||
|
var socketio = require('socket.io');
|
||||||
|
var moment = require('moment');
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Define Express App
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
global.app = express();
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Controllers
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
app.get('/', function(req, res){
|
||||||
|
res.send('Requarks Wiki WebSocket server');
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Start WebSocket server
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
winston.info('[SERVER] Starting WebSocket server on port ' + appconfig.wsPort + '...');
|
||||||
|
|
||||||
|
app.set('port', appconfig.wsPort);
|
||||||
|
var server = http.Server(app);
|
||||||
|
var io = socketio(server);
|
||||||
|
|
||||||
|
server.on('error', (error) => {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error('Listening on port ' + appconfig.port + ' requires elevated privileges!');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error('Port ' + appconfig.port + ' is already in use!');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(appconfig.wsPort, () => {
|
||||||
|
winston.info('[WS] WebSocket server started successfully! [RUNNING]');
|
||||||
|
});
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
|
||||||
|
socket.on('searchAdd', (data) => {
|
||||||
|
if(internalAuth.validateKey(data.auth)) {
|
||||||
|
search.add(data.content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('search', (data, cb) => {
|
||||||
|
search.find(data.terms).then((results) => {
|
||||||
|
cb(results);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/*setTimeout(() => {
|
||||||
|
search._si.searchAsync({ query: { AND: [{'*': ['unit']}] }}).then((stuff) => { console.log(stuff.hits); });
|
||||||
|
}, 8000);*/
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Shutdown gracefully
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
process.on('disconnect', () => {
|
||||||
|
winston.warn('[WS] Lost connection to main server. Exiting... [' + moment().toISOString() + ']');
|
||||||
|
server.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', () => {
|
||||||
|
server.stop();
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user