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

pull/2/head
NGPixel 8 years ago
parent 6ea243e8d4
commit 91d524eb06

@ -12,7 +12,7 @@ global.PROCNAME = 'AGENT';
// ---------------------------------------- // ----------------------------------------
var _isDebug = process.env.NODE_ENV === 'development'; var _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./lib/winston')(_isDebug); global.winston = require('./libs/winston')(_isDebug);
// ---------------------------------------- // ----------------------------------------
// Fetch internal handshake key // Fetch internal handshake key
@ -30,13 +30,13 @@ global.WSInternalKey = process.argv[2];
winston.info('[AGENT] Background Agent is initializing...'); winston.info('[AGENT] Background Agent is initializing...');
var appconfig = require('./models/config')('./config.yml'); var appconfig = require('./libs/config')('./config.yml');
global.db = require('./models/mongo').init(appconfig); global.db = require('./libs/mongo').init(appconfig);
global.upl = require('./models/agent/uploads').init(appconfig); global.upl = require('./libs/uploads-agent').init(appconfig);
global.git = require('./models/git').init(appconfig); global.git = require('./libs/git').init(appconfig);
global.entries = require('./models/entries').init(appconfig); global.entries = require('./libs/entries').init(appconfig);
global.mark = require('./models/markdown'); global.mark = require('./libs/markdown');
global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 }); global.ws = require('socket.io-client')('http://localhost:' + appconfig.port, { reconnectionAttempts: 10 });
// ---------------------------------------- // ----------------------------------------
// Load modules // Load modules
@ -205,10 +205,10 @@ ws.on('connect', function () {
}); });
ws.on('connect_error', 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 () { 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); process.exit(1);
}); });

File diff suppressed because one or more lines are too long

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

@ -1,7 +1,5 @@
"use strict"; "use strict";
jQuery( document ).ready(function( $ ) {
if($('#search-input').length) { if($('#search-input').length) {
$('#search-input').focus(); $('#search-input').focus();
@ -84,5 +82,3 @@ jQuery( document ).ready(function( $ ) {
$('main').on('click', vueHeader.closeSearch); $('main').on('click', vueHeader.closeSearch);
} }
});

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

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

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

@ -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

@ -321,12 +321,17 @@ module.exports = {
text: mark.removeMarkdown(pageData.markdown) text: mark.removeMarkdown(pageData.markdown)
}; };
}).then((content) => { }).then((content) => {
return db.Entry.create({ return db.Entry.findOneAndUpdate({
_id: content.entryPath
}, {
_id: content.entryPath, _id: content.entryPath,
title: content.meta.title || content.entryPath, title: content.meta.title || content.entryPath,
subtitle: content.meta.subtitle || '', subtitle: content.meta.subtitle || '',
parent: content.parent.title || '', parent: content.parent.title || '',
content: content.text || '' content: content.text || ''
}, {
new: true,
upsert: true
}); });
}); });
@ -430,6 +435,67 @@ module.exports = {
return _.replace(contents, new RegExp('{TITLE}', 'g'), formattedTitle); 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: []
};
});
} }
}; };

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

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

@ -40,26 +40,15 @@ module.exports = {
return this._uploadsThumbsPath; 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. * Gets the uploads folders.
* *
* @return {Array<String>} The uploads folders. * @return {Array<String>} The uploads folders.
*/ */
getUploadsFolders() { 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(() => { return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
if(!_.includes(self._uploadsFolders, folderName)) { return db.UplFolder.findOneAndUpdate({
self._uploadsFolders.push(folderName); _id: 'f:' + folderName
self._uploadsFolders = _.sortBy(self._uploadsFolders); }, {
} name: folderName
}, {
upsert: true
});
}).then(() => {
return self.getUploadsFolders(); return self.getUploadsFolders();
}); });
@ -96,32 +89,9 @@ module.exports = {
*/ */
validateUploadsFolder(folderName) { validateUploadsFolder(folderName) {
if(_.includes(this._uploadsFolders, folderName)) { return db.UplFolder.findOne({ name: folderName }).then((f) => {
return path.resolve(this._uploadsPath, folderName); return (f) ? path.resolve(this._uploadsPath, folderName) : false;
} 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;
}, },
@ -147,14 +117,30 @@ module.exports = {
*/ */
getUploadsFiles(cat, fld) { getUploadsFiles(cat, fld) {
return /*this._uploadsDb.Files.chain().find({ return db.UplFile.find({
'$and': [{ 'category' : cat },{ 'folder' : fld }] category: cat,
}).simplesort('filename').data()*/; 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;
})
} }
}; };

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

@ -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);
}
});
});*/
}
};

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

@ -25,7 +25,7 @@ html
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'). script(type='text/javascript').
var ioHost = window.location.origin + ':!{appconfig.wsPort}/'; var ioHost = window.location.origin + ':!{appconfig.port}/';
block head block head

@ -17,7 +17,7 @@
span.icon.is-small: i.fa.fa-folder span.icon.is-small: i.fa.fa-folder
span New Folder span New Folder
.control.has-addons .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.icon.is-small: i.fa.fa-upload
span Upload Image span Upload Image
label label
@ -58,8 +58,8 @@
a.button.is-primary(title="Page Logo", v-on:click="selectAlignment('logo')", v-bind:class="{ 'is-outlined': currentAlign !== 'logo' }") 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 span.icon.is-small: i.fa.fa-external-link-square
.column.editor-modal-imagechoices .column.editor-modal-imagechoices
figure(v-for="img in images", v-bind:class="{ 'is-active': currentImage === img.uid }", v-on:click="selectImage(img.uid)") 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.uid + '.png'") img(v-bind:src="'/uploads/t/' + img._id + '.png'")
span: strong {{ img.basename }} span: strong {{ img.basename }}
span {{ img.filesize | filesize }} span {{ img.filesize | filesize }}
em(v-show="images.length < 1") This folder is empty. em(v-show="images.length < 1") This folder is empty.
@ -97,3 +97,17 @@
footer.card-footer footer.card-footer
a.card-footer-item(v-on:click="fetchFromUrlDiscard") Discard 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

@ -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();
});
Loading…
Cancel
Save