Merged main & websocket server, refactored libs, image uploads fixes

This commit is contained in:
NGPixel 2016-10-16 01:34:34 -04:00
parent 6ea243e8d4
commit 91d524eb06
29 changed files with 350 additions and 3985 deletions

View File

@ -12,7 +12,7 @@ global.PROCNAME = 'AGENT';
// ----------------------------------------
var _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./lib/winston')(_isDebug);
global.winston = require('./libs/winston')(_isDebug);
// ----------------------------------------
// Fetch internal handshake key
@ -30,13 +30,13 @@ global.WSInternalKey = process.argv[2];
winston.info('[AGENT] Background Agent is initializing...');
var appconfig = require('./models/config')('./config.yml');
global.db = require('./models/mongo').init(appconfig);
global.upl = require('./models/agent/uploads').init(appconfig);
global.git = require('./models/git').init(appconfig);
global.entries = require('./models/entries').init(appconfig);
global.mark = require('./models/markdown');
global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
var appconfig = require('./libs/config')('./config.yml');
global.db = require('./libs/mongo').init(appconfig);
global.upl = require('./libs/uploads-agent').init(appconfig);
global.git = require('./libs/git').init(appconfig);
global.entries = require('./libs/entries').init(appconfig);
global.mark = require('./libs/markdown');
global.ws = require('socket.io-client')('http://localhost:' + appconfig.port, { reconnectionAttempts: 10 });
// ----------------------------------------
// Load modules
@ -205,10 +205,10 @@ ws.on('connect', function () {
});
ws.on('connect_error', function () {
winston.warn('[AGENT] Unable to connect to WebSocket server! Retrying...');
winston.warn('[AGENT] Unable to connect to main server! Retrying...');
});
ws.on('reconnect_failed', function () {
winston.error('[AGENT] Failed to reconnect to WebSocket server too many times! Stopping agent...');
winston.error('[AGENT] Failed to reconnect to main server too many times! Stopping agent...');
process.exit(1);
});

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,10 @@ let vueImage = new Vue({
currentAlign: 'left',
images: [],
uploadSucceeded: false,
postUploadChecks: 0
postUploadChecks: 0,
deleteImageShow: false,
deleteImageId: 0,
deleteImageFilename: ''
},
methods: {
open: () => {
@ -33,8 +36,8 @@ let vueImage = new Vue({
mde.codemirror.execCommand('singleSelection');
}
let selImage = _.find(vueImage.images, ['uid', vueImage.currentImage]);
selImage.normalizedPath = (selImage.folder === '') ? selImage.filename : selImage.folder + '/' + selImage.filename;
let selImage = _.find(vueImage.images, ['_id', vueImage.currentImage]);
selImage.normalizedPath = (selImage.folder === 'f:') ? selImage.filename : selImage.folder.slice(2) + '/' + selImage.filename;
selImage.titleGuess = _.startCase(selImage.basename);
let imageText = '![' + selImage.titleGuess + '](/uploads/' + selImage.normalizedPath + ' "' + selImage.titleGuess + '")';
@ -93,6 +96,9 @@ let vueImage = new Vue({
fetchFromUrlDiscard: (ev) => {
vueImage.fetchFromUrlShow = false;
},
fetchFromUrlFetch: (ev) => {
},
/**
* Select a folder
@ -210,17 +216,36 @@ let vueImage = new Vue({
name: "Delete",
icon: "fa-trash",
callback: (key, opt) => {
alert("Clicked on " + key);
vueImage.deleteImageId = _.toString($(opt.$trigger).data('uid'));
vueImage.deleteImageWarn(true);
}
}
}
});
},
deleteImageWarn: (show) => {
if(show) {
vueImage.deleteImageFilename = _.find(vueImage.images, ['_id', vueImage.deleteImageId ]).filename;
}
vueImage.deleteImageShow = show;
},
deleteImageGo: () => {
vueImage.deleteImageWarn(false);
vueImage.isLoadingText = 'Deleting image...';
vueImage.isLoading = true;
Vue.nextTick(() => {
socket.emit('uploadsDeleteFile', { uid: vueImage.deleteImageId }, (data) => {
vueImage.loadImages();
});
});
},
waitUploadComplete: () => {
vueImage.postUploadChecks++;
vueImage.isLoadingText = 'Processing uploads...';
vueImage.isLoadingText = 'Processing...';
let currentUplAmount = vueImage.images.length;
vueImage.loadImages(true);
@ -233,7 +258,7 @@ let vueImage = new Vue({
} else if(vueImage.postUploadChecks > 5) {
vueImage.postUploadChecks = 0;
vueImage.isLoading = false;
alerts.pushError('Unable to fetch new uploads', 'Try again later');
alerts.pushError('Unable to fetch new listing', 'Try again later');
} else {
vueImage.waitUploadComplete();
}

View File

@ -1,88 +1,84 @@
"use strict";
jQuery( document ).ready(function( $ ) {
if($('#search-input').length) {
if($('#search-input').length) {
$('#search-input').focus();
$('#search-input').focus();
$('.searchresults').css('display', 'block');
$('.searchresults').css('display', 'block');
var vueHeader = new Vue({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0;
if(val.length >= 3) {
vueHeader.searchactive = true;
vueHeader.searchload++;
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match;
vueHeader.searchsuggest = data.suggest;
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest);
if(vueHeader.searchload > 0) { vueHeader.searchload--; }
});
} else {
vueHeader.searchactive = false;
vueHeader.searchres = [];
vueHeader.searchsuggest = [];
vueHeader.searchmovearr = [];
vueHeader.searchload = 0;
}
},
searchmoveidx: (val, oldVal) => {
if(val > 0) {
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ?
'res.' + vueHeader.searchmovearr[val - 1]._id :
'sug.' + vueHeader.searchmovearr[val - 1];
} else {
vueHeader.searchmovekey = '';
}
var vueHeader = new Vue({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0;
if(val.length >= 3) {
vueHeader.searchactive = true;
vueHeader.searchload++;
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match;
vueHeader.searchsuggest = data.suggest;
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest);
if(vueHeader.searchload > 0) { vueHeader.searchload--; }
});
} else {
vueHeader.searchactive = false;
vueHeader.searchres = [];
vueHeader.searchsuggest = [];
vueHeader.searchmovearr = [];
vueHeader.searchload = 0;
}
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug;
},
closeSearch: () => {
vueHeader.searchq = '';
},
moveSelectSearch: () => {
if(vueHeader.searchmoveidx < 1) { return; }
let i = vueHeader.searchmoveidx - 1;
if(vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id);
} else {
vueHeader.searchq = vueHeader.searchmovearr[i];
}
},
moveDownSearch: () => {
if(vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++;
}
},
moveUpSearch: () => {
if(vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--;
}
searchmoveidx: (val, oldVal) => {
if(val > 0) {
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ?
'res.' + vueHeader.searchmovearr[val - 1]._id :
'sug.' + vueHeader.searchmovearr[val - 1];
} else {
vueHeader.searchmovekey = '';
}
}
});
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug;
},
closeSearch: () => {
vueHeader.searchq = '';
},
moveSelectSearch: () => {
if(vueHeader.searchmoveidx < 1) { return; }
let i = vueHeader.searchmoveidx - 1;
$('main').on('click', vueHeader.closeSearch);
if(vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id);
} else {
vueHeader.searchq = vueHeader.searchmovearr[i];
}
}
},
moveDownSearch: () => {
if(vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++;
}
},
moveUpSearch: () => {
if(vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--;
}
}
}
});
});
$('main').on('click', vueHeader.closeSearch);
}

View File

@ -23,13 +23,6 @@ host: http://localhost
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
# ---------------------------------------------------------------------

View File

@ -24,10 +24,11 @@ router.get('/edit/*', (req, res, next) => {
cache: false
}).then((pageData) => {
if(pageData) {
return res.render('pages/edit', { pageData });
res.render('pages/edit', { pageData });
} else {
throw new Error('Invalid page path.');
}
return true;
}).catch((err) => {
res.render('error', {
message: err.message,
@ -158,12 +159,13 @@ router.get('/*', (req, res, next) => {
entries.fetch(safePath).then((pageData) => {
if(pageData) {
return res.render('pages/view', { pageData });
res.render('pages/view', { pageData });
} else {
res.render('error-notexist', {
newpath: safePath
});
}
return true;
}).error((err) => {
res.render('error-notexist', {
message: err.message,

View File

@ -41,10 +41,7 @@ router.post('/img', lcdata.uploadImgHandler, (req, res, next) => {
let destFolder = _.chain(req.body.folder).trim().toLower().value();
ws.emit('uploadsValidateFolder', {
auth: WSInternalKey,
content: destFolder
}, (destFolderPath) => {
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if(!destFolderPath) {
return res.json({ ok: false, msg: 'Invalid Folder' });

48
controllers/ws.js Normal file
View File

@ -0,0 +1,48 @@
"use strict";
module.exports = (socket) => {
//-----------------------------------------
// SEARCH
//-----------------------------------------
socket.on('search', (data, cb) => {
cb = cb || _.noop;
entries.search(data.terms).then((results) => {
cb(results);
});
});
//-----------------------------------------
// UPLOADS
//-----------------------------------------
socket.on('uploadsGetFolders', (data, cb) => {
cb = cb || _.noop;
upl.getUploadsFolders().then((f) => {
cb(f);
})
});
socket.on('uploadsCreateFolder', (data, cb) => {
cb = cb || _.noop;
upl.createUploadsFolder(data.foldername).then((f) => {
cb(f);
});
});
socket.on('uploadsGetImages', (data, cb) => {
cb = cb || _.noop;
upl.getUploadsFiles('image', data.folder).then((f) => {
cb(f);
});
});
socket.on('uploadsDeleteFile', (data, cb) => {
cb = cb || _.noop;
upl.deleteUploadsFile(data.uid).then((f) => {
cb(f);
});
});
};

File diff suppressed because it is too large Load Diff

View File

@ -321,12 +321,17 @@ module.exports = {
text: mark.removeMarkdown(pageData.markdown)
};
}).then((content) => {
return db.Entry.create({
return db.Entry.findOneAndUpdate({
_id: content.entryPath
}, {
_id: content.entryPath,
title: content.meta.title || content.entryPath,
subtitle: content.meta.subtitle || '',
parent: content.parent.title || '',
content: content.text || ''
}, {
new: true,
upsert: true
});
});
@ -430,6 +435,67 @@ module.exports = {
return _.replace(contents, new RegExp('{TITLE}', 'g'), formattedTitle);
});
},
/**
* Searches entries based on terms.
*
* @param {String} terms The terms to search for
* @return {Promise<Object>} Promise of the search results
*/
search(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) {
let regMatch = new RegExp('^' + _.split(terms, ' ')[0]);
return db.Entry.find({
_id: { $regex: regMatch }
}, '_id')
.sort('_id')
.limit(5)
.exec()
.then((matches) => {
return {
match: hits,
suggest: (matches) ? _.map(matches, '_id') : []
};
});
} else {
return {
match: _.filter(hits, (h) => { return h._doc.score >= 1; }),
suggest: []
};
}
}).catch((err) => {
winston.error(err);
return {
match: [],
suggest: []
};
});
}
};

View File

@ -23,7 +23,7 @@ module.exports = {
let self = this;
let dbModelsPath = path.resolve(ROOTPATH, 'models', 'db');
let dbModelsPath = path.resolve(ROOTPATH, 'models');
modb.Promise = require('bluebird');

View File

@ -57,10 +57,7 @@ module.exports = {
let pInfo = self.parseUploadsRelPath(p);
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
ws.emit('uploadsAddFiles', {
auth: WSInternalKey,
content: mData
});
return db.UplFile.create(mData);
}).then(() => {
return git.commitUploads('Uploaded ' + p);
});
@ -72,11 +69,9 @@ module.exports = {
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
});
return db.UplFile.findOneAndRemove({
folder: 'f:' + pInfo.folder,
filename: pInfo.filename
}).then(() => {
return git.commitUploads('Deleted ' + p);
});
@ -205,7 +200,7 @@ module.exports = {
category: 'image',
mime: mimeInfo.mime,
extra: _.pick(mImgData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']),
folder: null,
folder: 'f:' + fldName,
filename: f,
basename: fPathObj.name,
filesize: s.size

View File

@ -40,26 +40,15 @@ module.exports = {
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;
return db.UplFolder.find({}, 'name').sort('name').exec().then((results) => {
return (results) ? _.map(results, 'name') : [{ name: '' }];
});
},
/**
@ -79,10 +68,14 @@ module.exports = {
}
return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
if(!_.includes(self._uploadsFolders, folderName)) {
self._uploadsFolders.push(folderName);
self._uploadsFolders = _.sortBy(self._uploadsFolders);
}
return db.UplFolder.findOneAndUpdate({
_id: 'f:' + folderName
}, {
name: folderName
}, {
upsert: true
});
}).then(() => {
return self.getUploadsFolders();
});
@ -96,32 +89,9 @@ module.exports = {
*/
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;
return db.UplFolder.findOne({ name: folderName }).then((f) => {
return (f) ? path.resolve(this._uploadsPath, folderName) : false;
})
},
@ -147,14 +117,30 @@ module.exports = {
*/
getUploadsFiles(cat, fld) {
return /*this._uploadsDb.Files.chain().find({
'$and': [{ 'category' : cat },{ 'folder' : fld }]
}).simplesort('filename').data()*/;
return db.UplFile.find({
category: cat,
folder: 'f:' + fld
}).sort('filename').exec();
},
deleteUploadsFile(fldName, f) {
/**
* Deletes an uploads file.
*
* @param {string} uid The file unique ID
* @return {Promise} Promise of the operation
*/
deleteUploadsFile(uid) {
let self = this;
return db.UplFile.findOneAndRemove({ _id: uid }).then((f) => {
if(f) {
fs.remove(path.join(self._uploadsThumbsPath, uid + '.png'));
fs.remove(path.resolve(self._uploadsPath, f.folder.slice(2), f.filename));
}
return true;
})
}
};

View File

@ -11,8 +11,11 @@ const modb = require('mongoose'),
*/
var uplFolderSchema = modb.Schema({
_id: String,
name: {
type: String
type: String,
index: true
}
},

View File

@ -1,133 +0,0 @@
"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);
}
});
});*/
}
};

122
server.js
View File

@ -1,3 +1,4 @@
"use strict";
// ===========================================
// REQUARKS WIKI
// 1.0.0
@ -11,52 +12,57 @@ global.PROCNAME = 'SERVER';
// Load Winston
// ----------------------------------------
var _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./lib/winston')(_isDebug);
const _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./libs/winston')(_isDebug);
winston.info('[SERVER] Requarks Wiki is initializing...');
// ----------------------------------------
// Load global modules
// ----------------------------------------
var appconfig = require('./models/config')('./config.yml');
global.lcdata = require('./models/server/local').init(appconfig);
global.db = require('./models/mongo').init(appconfig);
global.git = require('./models/git').init(appconfig, false);
global.entries = require('./models/entries').init(appconfig);
global.mark = require('./models/markdown');
var appconfig = require('./libs/config')('./config.yml');
global.lcdata = require('./libs/local').init(appconfig);
global.db = require('./libs/mongo').init(appconfig);
global.entries = require('./libs/entries').init(appconfig);
global.git = require('./libs/git').init(appconfig, false);
global.lang = require('i18next');
global.mark = require('./libs/markdown');
global.upl = require('./libs/uploads').init(appconfig);
// ----------------------------------------
// Load modules
// ----------------------------------------
var _ = require('lodash');
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var session = require('express-session');
const mongoStore = require('connect-mongo')(session);
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var flash = require('connect-flash');
var compression = require('compression');
var passport = require('passport');
var autoload = require('auto-load');
var expressValidator = require('express-validator');
var http = require('http');
global.lang = require('i18next');
var i18next_backend = require('i18next-node-fs-backend');
var i18next_mw = require('i18next-express-middleware');
const _ = require('lodash');
const autoload = require('auto-load');
const bodyParser = require('body-parser');
const compression = require('compression');
const cookieParser = require('cookie-parser');
const express = require('express');
const favicon = require('serve-favicon');
const flash = require('connect-flash');
const fork = require('child_process').fork;
const http = require('http');
const i18next_backend = require('i18next-node-fs-backend');
const i18next_mw = require('i18next-express-middleware');
const passport = require('passport');
const path = require('path');
const session = require('express-session');
const sessionMongoStore = require('connect-mongo')(session);
const socketio = require('socket.io');
var mw = autoload(path.join(ROOTPATH, '/middlewares'));
var ctrl = autoload(path.join(ROOTPATH, '/controllers'));
var libInternalAuth = require('./libs/internalAuth');
global.WSInternalKey = libInternalAuth.generateKey();
// ----------------------------------------
// Define Express App
// ----------------------------------------
global.app = express();
app.use(compression());
// ----------------------------------------
// Security
@ -65,15 +71,22 @@ global.app = express();
app.use(mw.security);
// ----------------------------------------
// Passport Authentication
// Public Assets
// ----------------------------------------
var strategy = require('./models/auth')(passport, appconfig);
app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
app.use(express.static(path.join(ROOTPATH, 'assets')));
// ----------------------------------------
// Session
// ----------------------------------------
var strategy = require('./libs/auth')(passport, appconfig);
app.use(cookieParser());
app.use(session({
name: 'requarkswiki.sid',
store: new mongoStore({
store: new sessionMongoStore({
mongooseConnection: db.connection,
touchAfter: 15
}),
@ -109,22 +122,12 @@ lang
// View Engine Setup
// ----------------------------------------
app.use(compression());
app.use(i18next_mw.handle(lang));
app.set('views', path.join(ROOTPATH, 'views'));
app.set('view engine', 'pug');
app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressValidator());
// ----------------------------------------
// Public Assets
// ----------------------------------------
app.use(express.static(path.join(ROOTPATH, 'assets')));
// ----------------------------------------
// View accessible data
@ -149,14 +152,12 @@ app.use('/', ctrl.pages);
// Error handling
// ----------------------------------------
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
@ -169,10 +170,12 @@ app.use(function(err, req, res, next) {
// Start HTTP server
// ----------------------------------------
winston.info('[SERVER] Starting HTTP server on port ' + appconfig.port + '...');
winston.info('[SERVER] Starting HTTP/WS server on port ' + appconfig.port + '...');
app.set('port', appconfig.port);
var server = http.createServer(app);
var io = socketio(server);
server.listen(appconfig.port);
server.on('error', (error) => {
if (error.syscall !== 'listen') {
@ -195,40 +198,21 @@ server.on('error', (error) => {
});
server.on('listening', () => {
winston.info('[SERVER] HTTP server started successfully! [RUNNING]');
winston.info('[SERVER] HTTP/WS server started successfully! [RUNNING]');
});
// ----------------------------------------
// WebSocket handlers
// ----------------------------------------
io.on('connection', ctrl.ws);
// ----------------------------------------
// Start child processes
// ----------------------------------------
var fork = require('child_process').fork,
libInternalAuth = require('./lib/internalAuth');
global.WSInternalKey = libInternalAuth.generateKey();
var wsSrv = fork('ws-server.js', [WSInternalKey]),
bgAgent = fork('agent.js', [WSInternalKey]);
var bgAgent = fork('agent.js', [WSInternalKey]);
process.on('exit', (code) => {
wsSrv.disconnect();
bgAgent.disconnect();
});
// ----------------------------------------
// Connect to local WebSocket server
// ----------------------------------------
var wsClient = require('socket.io-client');
global.ws = wsClient('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
ws.on('connect', function () {
winston.info('[SERVER] Connected to WebSocket server successfully!');
});
ws.on('connect_error', function () {
winston.warn('[SERVER] Unable to connect to WebSocket server! Retrying...');
});
ws.on('reconnect_failed', function () {
winston.error('[SERVER] Failed to reconnect to WebSocket server too many times! Stopping...');
process.exit(1);
});

View File

@ -25,7 +25,7 @@ html
script(type='text/javascript', src='/js/libs.js')
script(type='text/javascript', src='/js/app.js')
script(type='text/javascript').
var ioHost = window.location.origin + ':!{appconfig.wsPort}/';
var ioHost = window.location.origin + ':!{appconfig.port}/';
block head

View File

@ -17,7 +17,7 @@
span.icon.is-small: i.fa.fa-folder
span New Folder
.control.has-addons
a.button.is-info.is-outlined#btn-editor-uploadimage(v-on:click="uploadImage")
a.button.is-info.is-outlined#btn-editor-uploadimage
span.icon.is-small: i.fa.fa-upload
span Upload Image
label
@ -58,8 +58,8 @@
a.button.is-primary(title="Page Logo", v-on:click="selectAlignment('logo')", v-bind:class="{ 'is-outlined': currentAlign !== 'logo' }")
span.icon.is-small: i.fa.fa-external-link-square
.column.editor-modal-imagechoices
figure(v-for="img in images", v-bind:class="{ 'is-active': currentImage === img.uid }", v-on:click="selectImage(img.uid)")
img(v-bind:src="'/uploads/t/' + img.uid + '.png'")
figure(v-for="img in images", v-bind:class="{ 'is-active': currentImage === img._id }", v-on:click="selectImage(img._id)", v-bind:data-uid="img._id")
img(v-bind:src="'/uploads/t/' + img._id + '.png'")
span: strong {{ img.basename }}
span {{ img.filesize | filesize }}
em(v-show="images.length < 1") This folder is empty.
@ -96,4 +96,18 @@
span.help.is-danger.is-hidden This URL path is invalid!
footer.card-footer
a.card-footer-item(v-on:click="fetchFromUrlDiscard") Discard
a.card-footer-item(v-on:click="fetchFromUrlFetch") Fetch
a.card-footer-item(v-on:click="fetchFromUrlFetch") Fetch
.modal(v-bind:class="{ 'is-active': deleteImageShow }")
.modal-background
.modal-container
.modal-content
.card.is-fullwidth
header.card-header.is-danger
p.card-header-title Delete image?
.card-content
.content
| Are you sure you want to delete #[strong {{deleteImageFilename}}]?
footer.card-footer
a.card-footer-item(v-on:click="deleteImageWarn(false)") Discard
a.card-footer-item(v-on:click="deleteImageGo") Delete

View File

@ -1,184 +0,0 @@
// ===========================================
// REQUARKS WIKI - WebSocket Server
// 1.0.0
// Licensed under AGPLv3
// ===========================================
global.ROOTPATH = __dirname;
global.PROCNAME = 'WS';
// ----------------------------------------
// Load Winston
// ----------------------------------------
var _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./lib/winston')(_isDebug);
// ----------------------------------------
// 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 global modules
// ----------------------------------------
winston.info('[WS] WS Server is initializing...');
var appconfig = require('./models/config')('./config.yml');
global.db = require('./models/mongo').init(appconfig);
global.upl = require('./models/ws/uploads').init(appconfig);
global.entries = require('./models/entries').init(appconfig);
global.mark = require('./models/markdown');
global.search = require('./models/ws/search').init(appconfig);
// ----------------------------------------
// Load local 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) => {
//-----------------------------------------
// SEARCH
//-----------------------------------------
socket.on('searchAdd', (data) => {
if(internalAuth.validateKey(data.auth)) {
search.add(data.content);
}
});
socket.on('searchDel', (data, cb) => {
cb = cb || _.noop;
if(internalAuth.validateKey(data.auth)) {
search.delete(data.entryPath);
}
});
socket.on('search', (data, cb) => {
cb = cb || _.noop;
search.find(data.terms).then((results) => {
cb(results);
});
});
//-----------------------------------------
// UPLOADS
//-----------------------------------------
socket.on('uploadsSetFolders', (data) => {
if(internalAuth.validateKey(data.auth)) {
upl.setUploadsFolders(data.content);
}
});
socket.on('uploadsGetFolders', (data, cb) => {
cb = cb || _.noop;
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) => {
cb = cb || _.noop;
upl.createUploadsFolder(data.foldername).then((fldList) => {
cb(fldList);
});
});
socket.on('uploadsSetFiles', (data) => {
if(internalAuth.validateKey(data.auth)) {
upl.setUploadsFiles(data.content);
}
});
socket.on('uploadsAddFiles', (data) => {
if(internalAuth.validateKey(data.auth)) {
upl.addUploadsFiles(data.content);
}
});
socket.on('uploadsGetImages', (data, cb) => {
cb = cb || _.noop;
cb(upl.getUploadsFiles('image', data.folder));
});
});
// ----------------------------------------
// 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();
});