Standard JS code conversion + fixes

This commit is contained in:
NGPixel
2017-02-08 20:52:37 -05:00
parent a508b2a7f4
commit 414dc386d6
54 changed files with 4022 additions and 4288 deletions
+24
View File
@@ -0,0 +1,24 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- Change log
### Fixed
- Fixed issue with social accounts with empty name
### Changed
- Updated dependencies + snyk policy
- Conversion to Standard JS compliant code
## [v1.0-beta.2] - 2017-01-30
### Added
- Save own profile under My Account
### Changed
- Updated dependencies + snyk policy
[Unreleased]: https://github.com/Requarks/wiki/compare/v1.0-beta.2...HEAD
[v1.0-beta.2]: https://github.com/Requarks/wiki/releases/tag/v1.0-beta.2
+74 -93
View File
@@ -4,75 +4,72 @@
// Licensed under AGPLv3
// ===========================================
global.PROCNAME = 'AGENT';
global.ROOTPATH = __dirname;
global.IS_DEBUG = process.env.NODE_ENV === 'development';
global.PROCNAME = 'AGENT'
global.ROOTPATH = __dirname
global.IS_DEBUG = process.env.NODE_ENV === 'development'
if (IS_DEBUG) {
global.CORE_PATH = ROOTPATH + '/../core/';
global.CORE_PATH = ROOTPATH + '/../core/'
} else {
global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/';
global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/'
}
// ----------------------------------------
// Load Winston
// ----------------------------------------
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG)
// ----------------------------------------
// Load global modules
// ----------------------------------------
winston.info('[AGENT] Background Agent is initializing...');
winston.info('[AGENT] Background Agent is initializing...')
let appconf = require(CORE_PATH + 'core-libs/config')();
global.appconfig = appconf.config;
global.appdata = appconf.data;
global.db = require(CORE_PATH + 'core-libs/mongodb').init();
global.upl = require('./libs/uploads-agent').init();
global.git = require('./libs/git').init();
global.entries = require('./libs/entries').init();
global.mark = require('./libs/markdown');
let appconf = require(CORE_PATH + 'core-libs/config')()
global.appconfig = appconf.config
global.appdata = appconf.data
global.db = require(CORE_PATH + 'core-libs/mongodb').init()
global.upl = require('./libs/uploads-agent').init()
global.git = require('./libs/git').init()
global.entries = require('./libs/entries').init()
global.mark = require('./libs/markdown')
// ----------------------------------------
// Load modules
// ----------------------------------------
var _ = require('lodash');
var moment = require('moment');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs-extra"));
var klaw = require('klaw');
var path = require('path');
var cron = require('cron').CronJob;
var moment = require('moment')
var Promise = require('bluebird')
var fs = Promise.promisifyAll(require('fs-extra'))
var klaw = require('klaw')
var path = require('path')
var Cron = require('cron').CronJob
// ----------------------------------------
// Start Cron
// ----------------------------------------
var jobIsBusy = false;
var jobUplWatchStarted = false;
var jobIsBusy = false
var jobUplWatchStarted = false
var job = new cron({
var job = new Cron({
cronTime: '0 */5 * * * *',
onTick: () => {
// Make sure we don't start two concurrent jobs
if (jobIsBusy) {
winston.warn('[AGENT] Previous job has not completed gracefully or is still running! Skipping for now. (This is not normal, you should investigate)');
return;
winston.warn('[AGENT] Previous job has not completed gracefully or is still running! Skipping for now. (This is not normal, you should investigate)')
return
}
winston.info('[AGENT] Running all jobs...');
jobIsBusy = true;
winston.info('[AGENT] Running all jobs...')
jobIsBusy = true
// Prepare async job collector
let jobs = [];
let repoPath = path.resolve(ROOTPATH, appconfig.paths.repo);
let dataPath = path.resolve(ROOTPATH, appconfig.paths.data);
let uploadsPath = path.join(repoPath, 'uploads');
let uploadsTempPath = path.join(dataPath, 'temp-upload');
let jobs = []
let repoPath = path.resolve(ROOTPATH, appconfig.paths.repo)
let dataPath = path.resolve(ROOTPATH, appconfig.paths.data)
let uploadsTempPath = path.join(dataPath, 'temp-upload')
// ----------------------------------------
// REGULAR JOBS
@@ -84,61 +81,52 @@ var job = new cron({
jobs.push(git.onReady.then(() => {
return git.resync().then(() => {
// -> Stream all documents
let cacheJobs = [];
let jobCbStreamDocs_resolve = null,
jobCbStreamDocs = new Promise((resolve, reject) => {
jobCbStreamDocs_resolve = resolve;
});
let cacheJobs = []
let jobCbStreamDocsResolve = null
let jobCbStreamDocs = new Promise((resolve, reject) => {
jobCbStreamDocsResolve = resolve
})
klaw(repoPath).on('data', function (item) {
if (path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') {
let entryPath = entries.parsePath(entries.getEntryPathFromFullPath(item.path));
let cachePath = entries.getCachePath(entryPath);
let entryPath = entries.parsePath(entries.getEntryPathFromFullPath(item.path))
let cachePath = entries.getCachePath(entryPath)
// -> Purge outdated cache
cacheJobs.push(
fs.statAsync(cachePath).then((st) => {
return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active';
return moment(st.mtime).isBefore(item.stats.mtime) ? 'expired' : 'active'
}).catch((err) => {
return (err.code !== 'EEXIST') ? err : 'new';
return (err.code !== 'EEXIST') ? err : 'new'
}).then((fileStatus) => {
// -> Delete expired cache file
if (fileStatus === 'expired') {
return fs.unlinkAsync(cachePath).return(fileStatus);
return fs.unlinkAsync(cachePath).return(fileStatus)
}
return fileStatus;
return fileStatus
}).then((fileStatus) => {
// -> Update cache and search index
if (fileStatus !== 'active') {
return entries.updateCache(entryPath);
return entries.updateCache(entryPath)
}
return true;
return true
})
);
)
}
}).on('end', () => {
jobCbStreamDocs_resolve(Promise.all(cacheJobs));
});
jobCbStreamDocsResolve(Promise.all(cacheJobs))
})
return jobCbStreamDocs;
});
}));
return jobCbStreamDocs
})
}))
//* ****************************************
// -> Clear failed temporary upload files
@@ -146,65 +134,58 @@ var job = new cron({
jobs.push(
fs.readdirAsync(uploadsTempPath).then((ls) => {
let fifteenAgo = moment().subtract(15, 'minutes');
let fifteenAgo = moment().subtract(15, 'minutes')
return Promise.map(ls, (f) => {
return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s }; });
}).filter((s) => { return s.stat.isFile(); }).then((arrFiles) => {
return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s } })
}).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
return Promise.map(arrFiles, (f) => {
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
return fs.unlinkAsync(path.join(uploadsTempPath, f.filename));
return fs.unlinkAsync(path.join(uploadsTempPath, f.filename))
} else {
return true;
return true
}
});
});
})
);
})
})
)
// ----------------------------------------
// Run
// ----------------------------------------
Promise.all(jobs).then(() => {
winston.info('[AGENT] All jobs completed successfully! Going to sleep for now.');
winston.info('[AGENT] All jobs completed successfully! Going to sleep for now.')
if (!jobUplWatchStarted) {
jobUplWatchStarted = true;
jobUplWatchStarted = true
upl.initialScan().then(() => {
job.start();
});
job.start()
})
}
return true;
return true
}).catch((err) => {
winston.error('[AGENT] One or more jobs have failed: ', err);
winston.error('[AGENT] One or more jobs have failed: ', err)
}).finally(() => {
jobIsBusy = false;
});
jobIsBusy = false
})
},
start: false,
timeZone: 'UTC',
runOnInit: true
});
})
// ----------------------------------------
// Shutdown gracefully
// ----------------------------------------
process.on('disconnect', () => {
winston.warn('[AGENT] Lost connection to main server. Exiting...');
job.stop();
process.exit();
});
winston.warn('[AGENT] Lost connection to main server. Exiting...')
job.stop()
process.exit()
})
process.on('exit', () => {
job.stop();
});
job.stop()
})
+21 -1
View File
File diff suppressed because one or more lines are too long
+7 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+7 -1
View File
File diff suppressed because one or more lines are too long
+25 -25
View File
File diff suppressed because one or more lines are too long
+13 -15
View File
@@ -1,7 +1,6 @@
"use strict";
'use strict'
jQuery(document).ready(function ($) {
// ====================================
// Scroll
// ====================================
@@ -9,35 +8,35 @@ jQuery( document ).ready(function( $ ) {
$('a').smoothScroll({
speed: 400,
offset: -70
});
})
var sticky = new Sticky('.stickyscroll');
var sticky = new Sticky('.stickyscroll')
// ====================================
// Notifications
// ====================================
$(window).bind('beforeunload', () => {
$('#notifload').addClass('active');
});
$('#notifload').addClass('active')
})
$(document).ajaxSend(() => {
$('#notifload').addClass('active');
$('#notifload').addClass('active')
}).ajaxComplete(() => {
$('#notifload').removeClass('active');
});
$('#notifload').removeClass('active')
})
var alerts = new Alerts();
var alerts = new Alerts()
if (alertsData) {
_.forEach(alertsData, (alertRow) => {
alerts.push(alertRow);
});
alerts.push(alertRow)
})
}
// ====================================
// Establish WebSocket connection
// ====================================
var socket = io(window.location.origin);
var socket = io(window.location.origin)
// =include components/search.js
@@ -50,8 +49,7 @@ jQuery( document ).ready(function( $ ) {
// =include pages/edit.js
// =include pages/source.js
// =include pages/admin.js
});
})
// =include helpers/form.js
// =include helpers/pages.js
+21 -27
View File
@@ -1,4 +1,4 @@
"use strict";
'use strict'
/**
* Alerts
@@ -11,8 +11,7 @@ class Alerts {
* @class
*/
constructor () {
let self = this;
let self = this
self.mdl = new Vue({
el: '#alerts',
@@ -21,13 +20,12 @@ class Alerts {
},
methods: {
acknowledge: (uid) => {
self.close(uid);
self.close(uid)
}
}
});
self.uidNext = 1;
})
self.uidNext = 1
}
/**
@@ -37,8 +35,7 @@ class Alerts {
* @return {null} Void
*/
push (options) {
let self = this;
let self = this
let nAlert = _.defaults(options, {
_uid: self.uidNext,
@@ -46,18 +43,17 @@ class Alerts {
message: '---',
sticky: false,
title: '---'
});
})
self.mdl.children.push(nAlert);
self.mdl.children.push(nAlert)
if (!nAlert.sticky) {
_.delay(() => {
self.close(nAlert._uid);
}, 5000);
self.close(nAlert._uid)
}, 5000)
}
self.uidNext++;
self.uidNext++
}
/**
@@ -72,7 +68,7 @@ class Alerts {
message,
sticky: false,
title
});
})
}
/**
@@ -87,7 +83,7 @@ class Alerts {
message,
sticky: false,
title
});
})
}
/**
@@ -96,20 +92,18 @@ class Alerts {
* @param {Integer} uid The unique ID of the alert
*/
close (uid) {
let self = this
let self = this;
let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid]);
let nAlert = _.nth(self.mdl.children, nAlertIdx);
let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid])
let nAlert = _.nth(self.mdl.children, nAlertIdx)
if (nAlertIdx >= 0 && nAlert) {
nAlert.class += ' exit';
Vue.set(self.mdl.children, nAlertIdx, nAlert);
nAlert.class += ' exit'
Vue.set(self.mdl.children, nAlertIdx, nAlert)
_.delay(() => {
self.mdl.children.splice(nAlertIdx, 1);
}, 500);
}
self.mdl.children.splice(nAlertIdx, 1)
}, 500)
}
}
}
+30 -34
View File
@@ -1,25 +1,25 @@
let modelist = ace.require("ace/ext/modelist");
let codeEditor = null;
let modelist = ace.require('ace/ext/modelist')
let codeEditor = null
// ACE - Mode Loader
let modelistLoaded = [];
let modelistLoaded = []
let loadAceMode = (m) => {
return $.ajax({
url: '/js/ace/mode-' + m + '.js',
dataType: "script",
dataType: 'script',
cache: true,
beforeSend: () => {
if (_.includes(modelistLoaded, m)) {
return false;
return false
}
},
success: () => {
modelistLoaded.push(m);
modelistLoaded.push(m)
}
})
}
});
};
// Vue Code Block instance
@@ -33,46 +33,42 @@ let vueCodeBlock = new Vue({
watch: {
modeSelected: (val, oldVal) => {
loadAceMode(val).done(() => {
ace.require("ace/mode/" + val);
codeEditor.getSession().setMode("ace/mode/" + val);
});
ace.require('ace/mode/' + val)
codeEditor.getSession().setMode('ace/mode/' + val)
})
}
},
methods: {
open: (ev) => {
$('#modal-editor-codeblock').addClass('is-active');
$('#modal-editor-codeblock').addClass('is-active')
_.delay(() => {
codeEditor = ace.edit("codeblock-editor");
codeEditor.setTheme("ace/theme/tomorrow_night");
codeEditor.getSession().setMode("ace/mode/" + vueCodeBlock.modeSelected);
codeEditor.setOption('fontSize', '14px');
codeEditor.setOption('hScrollBarAlwaysVisible', false);
codeEditor.setOption('wrap', true);
codeEditor = ace.edit('codeblock-editor')
codeEditor.setTheme('ace/theme/tomorrow_night')
codeEditor.getSession().setMode('ace/mode/' + vueCodeBlock.modeSelected)
codeEditor.setOption('fontSize', '14px')
codeEditor.setOption('hScrollBarAlwaysVisible', false)
codeEditor.setOption('wrap', true)
codeEditor.setValue(vueCodeBlock.initContent);
codeEditor.focus();
codeEditor.renderer.updateFull();
}, 300);
codeEditor.setValue(vueCodeBlock.initContent)
codeEditor.focus()
codeEditor.renderer.updateFull()
}, 300)
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-codeblock').removeClass('is-active');
vueCodeBlock.initContent = '';
mdeModalOpenState = false
$('#modal-editor-codeblock').removeClass('is-active')
vueCodeBlock.initContent = ''
},
insertCode: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
mde.codemirror.execCommand('singleSelection')
}
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n';
mde.codemirror.doc.replaceSelection(codeBlockText);
vueCodeBlock.cancel();
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
mde.codemirror.doc.replaceSelection(codeBlockText)
vueCodeBlock.cancel()
}
}
});
})
+134 -148
View File
@@ -23,13 +23,13 @@ let vueFile = new Vue({
methods: {
open: () => {
mdeModalOpenState = true;
$('#modal-editor-file').addClass('is-active');
vueFile.refreshFolders();
mdeModalOpenState = true
$('#modal-editor-file').addClass('is-active')
vueFile.refreshFolders()
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-file').removeClass('is-active');
mdeModalOpenState = false
$('#modal-editor-file').removeClass('is-active')
},
// -------------------------------------------
@@ -37,23 +37,21 @@ let vueFile = new Vue({
// -------------------------------------------
selectFile: (fileId) => {
vueFile.currentFile = fileId;
vueFile.currentFile = fileId
},
insertFileLink: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
mde.codemirror.execCommand('singleSelection')
}
let selFile = _.find(vueFile.files, ['_id', vueFile.currentFile]);
selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename;
selFile.titleGuess = _.startCase(selFile.basename);
let selFile = _.find(vueFile.files, ['_id', vueFile.currentFile])
selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename
selFile.titleGuess = _.startCase(selFile.basename)
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")';
mde.codemirror.doc.replaceSelection(fileText);
vueFile.cancel();
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")'
mde.codemirror.doc.replaceSelection(fileText)
vueFile.cancel()
},
// -------------------------------------------
@@ -61,37 +59,35 @@ let vueFile = new Vue({
// -------------------------------------------
newFolder: (ev) => {
vueFile.newFolderName = '';
vueFile.newFolderError = false;
vueFile.newFolderShow = true;
_.delay(() => { $('#txt-editor-file-newfoldername').focus(); }, 400);
vueFile.newFolderName = ''
vueFile.newFolderError = false
vueFile.newFolderShow = true
_.delay(() => { $('#txt-editor-file-newfoldername').focus() }, 400)
},
newFolderDiscard: (ev) => {
vueFile.newFolderShow = false;
vueFile.newFolderShow = false
},
newFolderCreate: (ev) => {
let regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
vueFile.newFolderName = _.kebabCase(_.trim(vueFile.newFolderName));
let regFolderName = new RegExp('^[a-z0-9][a-z0-9\-]*[a-z0-9]$')
vueFile.newFolderName = _.kebabCase(_.trim(vueFile.newFolderName))
if (_.isEmpty(vueFile.newFolderName) || !regFolderName.test(vueFile.newFolderName)) {
vueFile.newFolderError = true;
return;
vueFile.newFolderError = true
return
}
vueFile.newFolderDiscard();
vueFile.isLoadingText = 'Creating new folder...';
vueFile.isLoading = true;
vueFile.newFolderDiscard()
vueFile.isLoadingText = 'Creating new folder...'
vueFile.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsCreateFolder', { foldername: vueFile.newFolderName }, (data) => {
vueFile.folders = data;
vueFile.currentFolder = vueFile.newFolderName;
vueFile.files = [];
vueFile.isLoading = false;
});
});
vueFile.folders = data
vueFile.currentFolder = vueFile.newFolderName
vueFile.files = []
vueFile.isLoading = false
})
})
},
// -------------------------------------------
@@ -99,35 +95,32 @@ let vueFile = new Vue({
// -------------------------------------------
renameFile: () => {
let c = _.find(vueFile.files, ['_id', vueFile.renameFileId ]);
vueFile.renameFileFilename = c.basename || '';
vueFile.renameFileShow = true;
let c = _.find(vueFile.files, ['_id', vueFile.renameFileId ])
vueFile.renameFileFilename = c.basename || ''
vueFile.renameFileShow = true
_.delay(() => {
$('#txt-editor-renamefile').focus();
_.defer(() => { $('#txt-editor-file-rename').select(); });
}, 400);
$('#txt-editor-renamefile').focus()
_.defer(() => { $('#txt-editor-file-rename').select() })
}, 400)
},
renameFileDiscard: () => {
vueFile.renameFileShow = false;
vueFile.renameFileShow = false
},
renameFileGo: () => {
vueFile.renameFileDiscard();
vueFile.isLoadingText = 'Renaming file...';
vueFile.isLoading = true;
vueFile.renameFileDiscard()
vueFile.isLoadingText = 'Renaming file...'
vueFile.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsRenameFile', { uid: vueFile.renameFileId, folder: vueFile.currentFolder, filename: vueFile.renameFileFilename }, (data) => {
if (data.ok) {
vueFile.waitChangeComplete(vueFile.files.length, false);
vueFile.waitChangeComplete(vueFile.files.length, false)
} else {
vueFile.isLoading = false;
alerts.pushError('Rename error', data.msg);
vueFile.isLoading = false
alerts.pushError('Rename error', data.msg)
}
});
});
})
})
},
// -------------------------------------------
@@ -135,18 +128,18 @@ let vueFile = new Vue({
// -------------------------------------------
moveFile: (uid, fld) => {
vueFile.isLoadingText = 'Moving file...';
vueFile.isLoading = true;
vueFile.isLoadingText = 'Moving file...'
vueFile.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
if (data.ok) {
vueFile.loadFiles();
vueFile.loadFiles()
} else {
vueFile.isLoading = false;
alerts.pushError('Rename error', data.msg);
vueFile.isLoading = false
alerts.pushError('Rename error', data.msg)
}
});
});
})
})
},
// -------------------------------------------
@@ -155,20 +148,20 @@ let vueFile = new Vue({
deleteFileWarn: (show) => {
if (show) {
let c = _.find(vueFile.files, ['_id', vueFile.deleteFileId ]);
vueFile.deleteFileFilename = c.filename || 'this file';
let c = _.find(vueFile.files, ['_id', vueFile.deleteFileId ])
vueFile.deleteFileFilename = c.filename || 'this file'
}
vueFile.deleteFileShow = show;
vueFile.deleteFileShow = show
},
deleteFileGo: () => {
vueFile.deleteFileWarn(false);
vueFile.isLoadingText = 'Deleting file...';
vueFile.isLoading = true;
vueFile.deleteFileWarn(false)
vueFile.isLoadingText = 'Deleting file...'
vueFile.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsDeleteFile', { uid: vueFile.deleteFileId }, (data) => {
vueFile.loadFiles();
});
});
vueFile.loadFiles()
})
})
},
// -------------------------------------------
@@ -176,66 +169,64 @@ let vueFile = new Vue({
// -------------------------------------------
selectFolder: (fldName) => {
vueFile.currentFolder = fldName;
vueFile.loadFiles();
vueFile.currentFolder = fldName
vueFile.loadFiles()
},
refreshFolders: () => {
vueFile.isLoadingText = 'Fetching folders list...';
vueFile.isLoading = true;
vueFile.currentFolder = '';
vueFile.currentImage = '';
vueFile.isLoadingText = 'Fetching folders list...'
vueFile.isLoading = true
vueFile.currentFolder = ''
vueFile.currentImage = ''
Vue.nextTick(() => {
socket.emit('uploadsGetFolders', { }, (data) => {
vueFile.folders = data;
vueFile.loadFiles();
});
});
vueFile.folders = data
vueFile.loadFiles()
})
})
},
loadFiles: (silent) => {
if (!silent) {
vueFile.isLoadingText = 'Fetching files...';
vueFile.isLoading = true;
vueFile.isLoadingText = 'Fetching files...'
vueFile.isLoading = true
}
return new Promise((resolve, reject) => {
Vue.nextTick(() => {
socket.emit('uploadsGetFiles', { folder: vueFile.currentFolder }, (data) => {
vueFile.files = data;
vueFile.files = data
if (!silent) {
vueFile.isLoading = false;
vueFile.isLoading = false
}
vueFile.attachContextMenus();
resolve(true);
});
});
});
vueFile.attachContextMenus()
resolve(true)
})
})
})
},
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
expectChange = (_.isBoolean(expectChange)) ? expectChange : true;
vueFile.postUploadChecks++;
vueFile.isLoadingText = 'Processing...';
vueFile.postUploadChecks++
vueFile.isLoadingText = 'Processing...'
Vue.nextTick(() => {
vueFile.loadFiles(true).then(() => {
if ((vueFile.files.length !== oldAmount) === expectChange) {
vueFile.postUploadChecks = 0;
vueFile.isLoading = false;
vueFile.postUploadChecks = 0
vueFile.isLoading = false
} else if (vueFile.postUploadChecks > 5) {
vueFile.postUploadChecks = 0;
vueFile.isLoading = false;
alerts.pushError('Unable to fetch updated listing', 'Try again later');
vueFile.postUploadChecks = 0
vueFile.isLoading = false
alerts.pushError('Unable to fetch updated listing', 'Try again later')
} else {
_.delay(() => {
vueFile.waitChangeComplete(oldAmount, expectChange);
}, 1500);
vueFile.waitChangeComplete(oldAmount, expectChange)
}, 1500)
}
});
});
})
})
},
// -------------------------------------------
@@ -243,68 +234,66 @@ let vueFile = new Vue({
// -------------------------------------------
attachContextMenus: () => {
let moveFolders = _.map(vueFile.folders, (f) => {
return {
name: (f !== '') ? f : '/ (root)',
icon: 'fa-folder',
callback: (key, opt) => {
let moveFileId = _.toString($(opt.$trigger).data('uid'));
let moveFileDestFolder = _.nth(vueFile.folders, key);
vueFile.moveFile(moveFileId, moveFileDestFolder);
let moveFileId = _.toString($(opt.$trigger).data('uid'))
let moveFileDestFolder = _.nth(vueFile.folders, key)
vueFile.moveFile(moveFileId, moveFileDestFolder)
}
};
});
}
})
$.contextMenu('destroy', '.editor-modal-file-choices > figure');
$.contextMenu('destroy', '.editor-modal-file-choices > figure')
$.contextMenu({
selector: '.editor-modal-file-choices > figure',
appendTo: '.editor-modal-file-choices',
position: (opt, x, y) => {
$(opt.$trigger).addClass('is-contextopen');
let trigPos = $(opt.$trigger).position();
let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 };
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w });
$(opt.$trigger).addClass('is-contextopen')
let trigPos = $(opt.$trigger).position()
let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 }
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
},
events: {
hide: (opt) => {
$(opt.$trigger).removeClass('is-contextopen');
$(opt.$trigger).removeClass('is-contextopen')
}
},
items: {
rename: {
name: "Rename",
icon: "fa-edit",
name: 'Rename',
icon: 'fa-edit',
callback: (key, opt) => {
vueFile.renameFileId = _.toString(opt.$trigger[0].dataset.uid);
vueFile.renameFile();
vueFile.renameFileId = _.toString(opt.$trigger[0].dataset.uid)
vueFile.renameFile()
}
},
move: {
name: "Move to...",
icon: "fa-folder-open-o",
name: 'Move to...',
icon: 'fa-folder-open-o',
items: moveFolders
},
delete: {
name: "Delete",
icon: "fa-trash",
name: 'Delete',
icon: 'fa-trash',
callback: (key, opt) => {
vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid);
vueFile.deleteFileWarn(true);
vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid)
vueFile.deleteFileWarn(true)
}
}
}
});
})
}
}
});
})
$('#btn-editor-file-upload input').on('change', (ev) => {
let curFileAmount = vueFile.files.length
let curFileAmount = vueFile.files.length;
$(ev.currentTarget).simpleUpload("/uploads/file", {
$(ev.currentTarget).simpleUpload('/uploads/file', {
name: 'binfile',
data: {
@@ -315,51 +304,48 @@ $('#btn-editor-file-upload input').on('change', (ev) => {
maxFileSize: 0,
init: (totalUploads) => {
vueFile.uploadSucceeded = false;
vueFile.isLoadingText = 'Preparing to upload...';
vueFile.isLoading = true;
vueFile.uploadSucceeded = false
vueFile.isLoadingText = 'Preparing to upload...'
vueFile.isLoading = true
},
progress: (progress) => {
vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%';
vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
},
success: (data) => {
if (data.ok) {
let failedUpls = _.filter(data.results, ['ok', false]);
let failedUpls = _.filter(data.results, ['ok', false])
if (failedUpls.length) {
_.forEach(failedUpls, (u) => {
alerts.pushError('Upload error', u.msg);
});
alerts.pushError('Upload error', u.msg)
})
if (failedUpls.length < data.results.length) {
alerts.push({
title: 'Some uploads succeeded',
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
});
vueFile.uploadSucceeded = true;
})
vueFile.uploadSucceeded = true
}
} else {
vueFile.uploadSucceeded = true;
vueFile.uploadSucceeded = true
}
} else {
alerts.pushError('Upload error', data.msg);
alerts.pushError('Upload error', data.msg)
}
},
error: (error) => {
alerts.pushError(error.message, this.upload.file.name);
alerts.pushError(error.message, this.upload.file.name)
},
finish: () => {
if (vueFile.uploadSucceeded) {
vueFile.waitChangeComplete(curFileAmount, true);
vueFile.waitChangeComplete(curFileAmount, true)
} else {
vueFile.isLoading = false;
vueFile.isLoading = false
}
}
});
});
})
})
+153 -169
View File
@@ -26,13 +26,13 @@ let vueImage = new Vue({
methods: {
open: () => {
mdeModalOpenState = true;
$('#modal-editor-image').addClass('is-active');
vueImage.refreshFolders();
mdeModalOpenState = true
$('#modal-editor-image').addClass('is-active')
vueImage.refreshFolders()
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-image').removeClass('is-active');
mdeModalOpenState = false
$('#modal-editor-image').removeClass('is-active')
},
// -------------------------------------------
@@ -40,34 +40,32 @@ let vueImage = new Vue({
// -------------------------------------------
selectImage: (imageId) => {
vueImage.currentImage = imageId;
vueImage.currentImage = imageId
},
insertImage: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
mde.codemirror.execCommand('singleSelection')
}
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 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 + '")';
let imageText = '![' + selImage.titleGuess + '](/uploads/' + selImage.normalizedPath + ' "' + selImage.titleGuess + '")'
switch (vueImage.currentAlign) {
case 'center':
imageText += '{.align-center}';
break;
imageText += '{.align-center}'
break
case 'right':
imageText += '{.align-right}';
break;
imageText += '{.align-right}'
break
case 'logo':
imageText += '{.pagelogo}';
break;
imageText += '{.pagelogo}'
break
}
mde.codemirror.doc.replaceSelection(imageText);
vueImage.cancel();
mde.codemirror.doc.replaceSelection(imageText)
vueImage.cancel()
},
// -------------------------------------------
@@ -75,37 +73,35 @@ let vueImage = new Vue({
// -------------------------------------------
newFolder: (ev) => {
vueImage.newFolderName = '';
vueImage.newFolderError = false;
vueImage.newFolderShow = true;
_.delay(() => { $('#txt-editor-image-newfoldername').focus(); }, 400);
vueImage.newFolderName = ''
vueImage.newFolderError = false
vueImage.newFolderShow = true
_.delay(() => { $('#txt-editor-image-newfoldername').focus() }, 400)
},
newFolderDiscard: (ev) => {
vueImage.newFolderShow = false;
vueImage.newFolderShow = false
},
newFolderCreate: (ev) => {
let regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
vueImage.newFolderName = _.kebabCase(_.trim(vueImage.newFolderName));
let regFolderName = new RegExp('^[a-z0-9][a-z0-9\-]*[a-z0-9]$')
vueImage.newFolderName = _.kebabCase(_.trim(vueImage.newFolderName))
if (_.isEmpty(vueImage.newFolderName) || !regFolderName.test(vueImage.newFolderName)) {
vueImage.newFolderError = true;
return;
vueImage.newFolderError = true
return
}
vueImage.newFolderDiscard();
vueImage.isLoadingText = 'Creating new folder...';
vueImage.isLoading = true;
vueImage.newFolderDiscard()
vueImage.isLoadingText = 'Creating new folder...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsCreateFolder', { foldername: vueImage.newFolderName }, (data) => {
vueImage.folders = data;
vueImage.currentFolder = vueImage.newFolderName;
vueImage.images = [];
vueImage.isLoading = false;
});
});
vueImage.folders = data
vueImage.currentFolder = vueImage.newFolderName
vueImage.images = []
vueImage.isLoading = false
})
})
},
// -------------------------------------------
@@ -113,30 +109,28 @@ let vueImage = new Vue({
// -------------------------------------------
fetchFromUrl: (ev) => {
vueImage.fetchFromUrlURL = '';
vueImage.fetchFromUrlShow = true;
_.delay(() => { $('#txt-editor-image-fetchurl').focus(); }, 400);
vueImage.fetchFromUrlURL = ''
vueImage.fetchFromUrlShow = true
_.delay(() => { $('#txt-editor-image-fetchurl').focus() }, 400)
},
fetchFromUrlDiscard: (ev) => {
vueImage.fetchFromUrlShow = false;
vueImage.fetchFromUrlShow = false
},
fetchFromUrlGo: (ev) => {
vueImage.fetchFromUrlDiscard();
vueImage.isLoadingText = 'Fetching image...';
vueImage.isLoading = true;
vueImage.fetchFromUrlDiscard()
vueImage.isLoadingText = 'Fetching image...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsFetchFileFromURL', { folder: vueImage.currentFolder, fetchUrl: vueImage.fetchFromUrlURL }, (data) => {
if (data.ok) {
vueImage.waitChangeComplete(vueImage.images.length, true);
vueImage.waitChangeComplete(vueImage.images.length, true)
} else {
vueImage.isLoading = false;
alerts.pushError('Upload error', data.msg);
vueImage.isLoading = false
alerts.pushError('Upload error', data.msg)
}
});
});
})
})
},
// -------------------------------------------
@@ -144,35 +138,32 @@ let vueImage = new Vue({
// -------------------------------------------
renameImage: () => {
let c = _.find(vueImage.images, ['_id', vueImage.renameImageId ]);
vueImage.renameImageFilename = c.basename || '';
vueImage.renameImageShow = true;
let c = _.find(vueImage.images, ['_id', vueImage.renameImageId ])
vueImage.renameImageFilename = c.basename || ''
vueImage.renameImageShow = true
_.delay(() => {
$('#txt-editor-image-rename').focus();
_.defer(() => { $('#txt-editor-image-rename').select(); });
}, 400);
$('#txt-editor-image-rename').focus()
_.defer(() => { $('#txt-editor-image-rename').select() })
}, 400)
},
renameImageDiscard: () => {
vueImage.renameImageShow = false;
vueImage.renameImageShow = false
},
renameImageGo: () => {
vueImage.renameImageDiscard();
vueImage.isLoadingText = 'Renaming image...';
vueImage.isLoading = true;
vueImage.renameImageDiscard()
vueImage.isLoadingText = 'Renaming image...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsRenameFile', { uid: vueImage.renameImageId, folder: vueImage.currentFolder, filename: vueImage.renameImageFilename }, (data) => {
if (data.ok) {
vueImage.waitChangeComplete(vueImage.images.length, false);
vueImage.waitChangeComplete(vueImage.images.length, false)
} else {
vueImage.isLoading = false;
alerts.pushError('Rename error', data.msg);
vueImage.isLoading = false
alerts.pushError('Rename error', data.msg)
}
});
});
})
})
},
// -------------------------------------------
@@ -180,18 +171,18 @@ let vueImage = new Vue({
// -------------------------------------------
moveImage: (uid, fld) => {
vueImage.isLoadingText = 'Moving image...';
vueImage.isLoading = true;
vueImage.isLoadingText = 'Moving image...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
if (data.ok) {
vueImage.loadImages();
vueImage.loadImages()
} else {
vueImage.isLoading = false;
alerts.pushError('Rename error', data.msg);
vueImage.isLoading = false
alerts.pushError('Rename error', data.msg)
}
});
});
})
})
},
// -------------------------------------------
@@ -200,20 +191,20 @@ let vueImage = new Vue({
deleteImageWarn: (show) => {
if (show) {
let c = _.find(vueImage.images, ['_id', vueImage.deleteImageId ]);
vueImage.deleteImageFilename = c.filename || 'this image';
let c = _.find(vueImage.images, ['_id', vueImage.deleteImageId ])
vueImage.deleteImageFilename = c.filename || 'this image'
}
vueImage.deleteImageShow = show;
vueImage.deleteImageShow = show
},
deleteImageGo: () => {
vueImage.deleteImageWarn(false);
vueImage.isLoadingText = 'Deleting image...';
vueImage.isLoading = true;
vueImage.deleteImageWarn(false)
vueImage.isLoadingText = 'Deleting image...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsDeleteFile', { uid: vueImage.deleteImageId }, (data) => {
vueImage.loadImages();
});
});
vueImage.loadImages()
})
})
},
// -------------------------------------------
@@ -221,66 +212,64 @@ let vueImage = new Vue({
// -------------------------------------------
selectFolder: (fldName) => {
vueImage.currentFolder = fldName;
vueImage.loadImages();
vueImage.currentFolder = fldName
vueImage.loadImages()
},
refreshFolders: () => {
vueImage.isLoadingText = 'Fetching folders list...';
vueImage.isLoading = true;
vueImage.currentFolder = '';
vueImage.currentImage = '';
vueImage.isLoadingText = 'Fetching folders list...'
vueImage.isLoading = true
vueImage.currentFolder = ''
vueImage.currentImage = ''
Vue.nextTick(() => {
socket.emit('uploadsGetFolders', { }, (data) => {
vueImage.folders = data;
vueImage.loadImages();
});
});
vueImage.folders = data
vueImage.loadImages()
})
})
},
loadImages: (silent) => {
if (!silent) {
vueImage.isLoadingText = 'Fetching images...';
vueImage.isLoading = true;
vueImage.isLoadingText = 'Fetching images...'
vueImage.isLoading = true
}
return new Promise((resolve, reject) => {
Vue.nextTick(() => {
socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
vueImage.images = data;
vueImage.images = data
if (!silent) {
vueImage.isLoading = false;
vueImage.isLoading = false
}
vueImage.attachContextMenus();
resolve(true);
});
});
});
vueImage.attachContextMenus()
resolve(true)
})
})
})
},
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
expectChange = (_.isBoolean(expectChange)) ? expectChange : true;
vueImage.postUploadChecks++;
vueImage.isLoadingText = 'Processing...';
vueImage.postUploadChecks++
vueImage.isLoadingText = 'Processing...'
Vue.nextTick(() => {
vueImage.loadImages(true).then(() => {
if ((vueImage.images.length !== oldAmount) === expectChange) {
vueImage.postUploadChecks = 0;
vueImage.isLoading = false;
vueImage.postUploadChecks = 0
vueImage.isLoading = false
} else if (vueImage.postUploadChecks > 5) {
vueImage.postUploadChecks = 0;
vueImage.isLoading = false;
alerts.pushError('Unable to fetch updated listing', 'Try again later');
vueImage.postUploadChecks = 0
vueImage.isLoading = false
alerts.pushError('Unable to fetch updated listing', 'Try again later')
} else {
_.delay(() => {
vueImage.waitChangeComplete(oldAmount, expectChange);
}, 1500);
vueImage.waitChangeComplete(oldAmount, expectChange)
}, 1500)
}
});
});
})
})
},
// -------------------------------------------
@@ -288,68 +277,66 @@ let vueImage = new Vue({
// -------------------------------------------
attachContextMenus: () => {
let moveFolders = _.map(vueImage.folders, (f) => {
return {
name: (f !== '') ? f : '/ (root)',
icon: 'fa-folder',
callback: (key, opt) => {
let moveImageId = _.toString($(opt.$trigger).data('uid'));
let moveImageDestFolder = _.nth(vueImage.folders, key);
vueImage.moveImage(moveImageId, moveImageDestFolder);
let moveImageId = _.toString($(opt.$trigger).data('uid'))
let moveImageDestFolder = _.nth(vueImage.folders, key)
vueImage.moveImage(moveImageId, moveImageDestFolder)
}
};
});
}
})
$.contextMenu('destroy', '.editor-modal-image-choices > figure');
$.contextMenu('destroy', '.editor-modal-image-choices > figure')
$.contextMenu({
selector: '.editor-modal-image-choices > figure',
appendTo: '.editor-modal-image-choices',
position: (opt, x, y) => {
$(opt.$trigger).addClass('is-contextopen');
let trigPos = $(opt.$trigger).position();
let trigDim = { w: $(opt.$trigger).width() / 2, h: $(opt.$trigger).height() / 2 };
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w });
$(opt.$trigger).addClass('is-contextopen')
let trigPos = $(opt.$trigger).position()
let trigDim = { w: $(opt.$trigger).width() / 2, h: $(opt.$trigger).height() / 2 }
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
},
events: {
hide: (opt) => {
$(opt.$trigger).removeClass('is-contextopen');
$(opt.$trigger).removeClass('is-contextopen')
}
},
items: {
rename: {
name: "Rename",
icon: "fa-edit",
name: 'Rename',
icon: 'fa-edit',
callback: (key, opt) => {
vueImage.renameImageId = _.toString(opt.$trigger[0].dataset.uid);
vueImage.renameImage();
vueImage.renameImageId = _.toString(opt.$trigger[0].dataset.uid)
vueImage.renameImage()
}
},
move: {
name: "Move to...",
icon: "fa-folder-open-o",
name: 'Move to...',
icon: 'fa-folder-open-o',
items: moveFolders
},
delete: {
name: "Delete",
icon: "fa-trash",
name: 'Delete',
icon: 'fa-trash',
callback: (key, opt) => {
vueImage.deleteImageId = _.toString(opt.$trigger[0].dataset.uid);
vueImage.deleteImageWarn(true);
vueImage.deleteImageId = _.toString(opt.$trigger[0].dataset.uid)
vueImage.deleteImageWarn(true)
}
}
}
});
})
}
}
});
})
$('#btn-editor-image-upload input').on('change', (ev) => {
let curImageAmount = vueImage.images.length
let curImageAmount = vueImage.images.length;
$(ev.currentTarget).simpleUpload("/uploads/img", {
$(ev.currentTarget).simpleUpload('/uploads/img', {
name: 'imgfile',
data: {
@@ -357,56 +344,53 @@ $('#btn-editor-image-upload input').on('change', (ev) => {
},
limit: 20,
expect: 'json',
allowedExts: ["jpg", "jpeg", "gif", "png", "webp"],
allowedExts: ['jpg', 'jpeg', 'gif', 'png', 'webp'],
allowedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
maxFileSize: 3145728, // max 3 MB
init: (totalUploads) => {
vueImage.uploadSucceeded = false;
vueImage.isLoadingText = 'Preparing to upload...';
vueImage.isLoading = true;
vueImage.uploadSucceeded = false
vueImage.isLoadingText = 'Preparing to upload...'
vueImage.isLoading = true
},
progress: (progress) => {
vueImage.isLoadingText = 'Uploading...' + Math.round(progress) + '%';
vueImage.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
},
success: (data) => {
if (data.ok) {
let failedUpls = _.filter(data.results, ['ok', false]);
let failedUpls = _.filter(data.results, ['ok', false])
if (failedUpls.length) {
_.forEach(failedUpls, (u) => {
alerts.pushError('Upload error', u.msg);
});
alerts.pushError('Upload error', u.msg)
})
if (failedUpls.length < data.results.length) {
alerts.push({
title: 'Some uploads succeeded',
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
});
vueImage.uploadSucceeded = true;
})
vueImage.uploadSucceeded = true
}
} else {
vueImage.uploadSucceeded = true;
vueImage.uploadSucceeded = true
}
} else {
alerts.pushError('Upload error', data.msg);
alerts.pushError('Upload error', data.msg)
}
},
error: (error) => {
alerts.pushError(error.message, this.upload.file.name);
alerts.pushError(error.message, this.upload.file.name)
},
finish: () => {
if (vueImage.uploadSucceeded) {
vueImage.waitChangeComplete(curImageAmount, true);
vueImage.waitChangeComplete(curImageAmount, true)
} else {
vueImage.isLoading = false;
vueImage.isLoading = false
}
}
});
});
})
})
+14 -16
View File
@@ -3,7 +3,7 @@ const videoRules = {
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
'vimeo': new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
};
}
// Vue Video instance
@@ -14,36 +14,34 @@ let vueVideo = new Vue({
},
methods: {
open: (ev) => {
$('#modal-editor-video').addClass('is-active');
$('#modal-editor-video input').focus();
$('#modal-editor-video').addClass('is-active')
$('#modal-editor-video input').focus()
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-video').removeClass('is-active');
vueVideo.link = '';
mdeModalOpenState = false
$('#modal-editor-video').removeClass('is-active')
vueVideo.link = ''
},
insertVideo: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
mde.codemirror.execCommand('singleSelection')
}
// Guess video type
let videoType = _.findKey(videoRules, (vr) => {
return vr.test(vueVideo.link);
});
return vr.test(vueVideo.link)
})
if (_.isNil(videoType)) {
videoType = 'video';
videoType = 'video'
}
// Insert video tag
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n';
mde.codemirror.doc.replaceSelection(videoText);
vueVideo.cancel();
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
mde.codemirror.doc.replaceSelection(videoText)
vueVideo.cancel()
}
}
});
})
+81 -86
View File
@@ -4,13 +4,12 @@
// ====================================
if ($('#mk-editor').length === 1) {
let mdeModalOpenState = false;
let mdeCurrentEditor = null;
let mdeModalOpenState = false
let mdeCurrentEditor = null
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v));
});
return _.toUpper(filesize(v))
})
// =include editor-image.js
// =include editor-file.js
@@ -20,164 +19,161 @@ if($('#mk-editor').length === 1) {
var mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $("#mk-editor").get(0),
element: $('#mk-editor').get(0),
placeholder: 'Enter Markdown formatted content here...',
spellChecker: false,
status: false,
toolbar: [{
name: "bold",
name: 'bold',
action: SimpleMDE.toggleBold,
className: "icon-bold",
title: "Bold",
className: 'icon-bold',
title: 'Bold'
},
{
name: "italic",
name: 'italic',
action: SimpleMDE.toggleItalic,
className: "icon-italic",
title: "Italic",
className: 'icon-italic',
title: 'Italic'
},
{
name: "strikethrough",
name: 'strikethrough',
action: SimpleMDE.toggleStrikethrough,
className: "icon-strikethrough",
title: "Strikethrough",
className: 'icon-strikethrough',
title: 'Strikethrough'
},
'|',
{
name: "heading-1",
name: 'heading-1',
action: SimpleMDE.toggleHeading1,
className: "icon-header fa-header-x fa-header-1",
title: "Big Heading",
className: 'icon-header fa-header-x fa-header-1',
title: 'Big Heading'
},
{
name: "heading-2",
name: 'heading-2',
action: SimpleMDE.toggleHeading2,
className: "icon-header fa-header-x fa-header-2",
title: "Medium Heading",
className: 'icon-header fa-header-x fa-header-2',
title: 'Medium Heading'
},
{
name: "heading-3",
name: 'heading-3',
action: SimpleMDE.toggleHeading3,
className: "icon-header fa-header-x fa-header-3",
title: "Small Heading",
className: 'icon-header fa-header-x fa-header-3',
title: 'Small Heading'
},
{
name: "quote",
name: 'quote',
action: SimpleMDE.toggleBlockquote,
className: "icon-quote-left",
title: "Quote",
className: 'icon-quote-left',
title: 'Quote'
},
'|',
{
name: "unordered-list",
name: 'unordered-list',
action: SimpleMDE.toggleUnorderedList,
className: "icon-th-list",
title: "Bullet List",
className: 'icon-th-list',
title: 'Bullet List'
},
{
name: "ordered-list",
name: 'ordered-list',
action: SimpleMDE.toggleOrderedList,
className: "icon-list-ol",
title: "Numbered List",
className: 'icon-list-ol',
title: 'Numbered List'
},
'|',
{
name: "link",
name: 'link',
action: (editor) => {
/* if(!mdeModalOpenState) {
mdeModalOpenState = true;
$('#modal-editor-link').slideToggle();
} */
},
className: "icon-link2",
title: "Insert Link",
className: 'icon-link2',
title: 'Insert Link'
},
{
name: "image",
name: 'image',
action: (editor) => {
if (!mdeModalOpenState) {
vueImage.open();
vueImage.open()
}
},
className: "icon-image",
title: "Insert Image",
className: 'icon-image',
title: 'Insert Image'
},
{
name: "file",
name: 'file',
action: (editor) => {
if (!mdeModalOpenState) {
vueFile.open();
vueFile.open()
}
},
className: "icon-paper",
title: "Insert File",
className: 'icon-paper',
title: 'Insert File'
},
{
name: "video",
name: 'video',
action: (editor) => {
if (!mdeModalOpenState) {
vueVideo.open();
vueVideo.open()
}
},
className: "icon-video-camera2",
title: "Insert Video Player",
className: 'icon-video-camera2',
title: 'Insert Video Player'
},
'|',
{
name: "inline-code",
name: 'inline-code',
action: (editor) => {
if (!editor.codemirror.doc.somethingSelected()) {
return alerts.pushError('Invalid selection','You must select at least 1 character first.');
return alerts.pushError('Invalid selection', 'You must select at least 1 character first.')
}
let curSel = editor.codemirror.doc.getSelections();
let curSel = editor.codemirror.doc.getSelections()
curSel = _.map(curSel, (s) => {
return '`' + s + '`';
});
editor.codemirror.doc.replaceSelections(curSel);
return '`' + s + '`'
})
editor.codemirror.doc.replaceSelections(curSel)
},
className: "icon-terminal",
title: "Inline Code",
className: 'icon-terminal',
title: 'Inline Code'
},
{
name: "code-block",
name: 'code-block',
action: (editor) => {
if (!mdeModalOpenState) {
mdeModalOpenState = true;
mdeModalOpenState = true
if (mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection();
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
}
vueCodeBlock.open();
vueCodeBlock.open()
}
},
className: "icon-code",
title: "Code Block",
className: 'icon-code',
title: 'Code Block'
},
'|',
{
name: "table",
name: 'table',
action: (editor) => {
// todo
},
className: "icon-table",
title: "Insert Table",
className: 'icon-table',
title: 'Insert Table'
},
{
name: "horizontal-rule",
name: 'horizontal-rule',
action: SimpleMDE.drawHorizontalRule,
className: "icon-minus2",
title: "Horizontal Rule",
className: 'icon-minus2',
title: 'Horizontal Rule'
}
],
shortcuts: {
"toggleBlockquote": null,
"toggleFullScreen": null
'toggleBlockquote': null,
'toggleFullScreen': null
}
});
})
// -> Save
@@ -190,28 +186,27 @@ if($('#mk-editor').length === 1) {
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if (rData.ok) {
window.location.assign('/' + pageEntryPath);
window.location.assign('/' + pageEntryPath)
} else {
alerts.pushError('Something went wrong', rData.error);
alerts.pushError('Something went wrong', rData.error)
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.');
});
};
alerts.pushError('Something went wrong', 'Save operation failed.')
})
}
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
saveCurrentDocument(ev);
});
saveCurrentDocument(ev)
})
$(window).bind('keydown', (ev) => {
if (ev.ctrlKey || ev.metaKey) {
switch (String.fromCharCode(ev.which).toLowerCase()) {
case 's':
ev.preventDefault();
saveCurrentDocument(ev);
break;
ev.preventDefault()
saveCurrentDocument(ev)
break
}
}
});
})
}
+28 -31
View File
@@ -1,10 +1,9 @@
"use strict";
'use strict'
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',
@@ -20,65 +19,63 @@ if($('#search-input').length) {
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0;
vueHeader.searchmoveidx = 0
if (val.length >= 3) {
vueHeader.searchactive = true;
vueHeader.searchload++;
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--; }
});
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;
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];
'sug.' + vueHeader.searchmovearr[val - 1]
} else {
vueHeader.searchmovekey = '';
vueHeader.searchmovekey = ''
}
}
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug;
vueHeader.searchq = sug
},
closeSearch: () => {
vueHeader.searchq = '';
vueHeader.searchq = ''
},
moveSelectSearch: () => {
if(vueHeader.searchmoveidx < 1) { return; }
let i = vueHeader.searchmoveidx - 1;
if (vueHeader.searchmoveidx < 1) { return }
let i = vueHeader.searchmoveidx - 1
if (vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id);
window.location.assign('/' + vueHeader.searchmovearr[i]._id)
} else {
vueHeader.searchq = vueHeader.searchmovearr[i];
vueHeader.searchq = vueHeader.searchmovearr[i]
}
},
moveDownSearch: () => {
if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++;
vueHeader.searchmoveidx++
}
},
moveUpSearch: () => {
if (vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--;
vueHeader.searchmoveidx--
}
}
}
});
$('main').on('click', vueHeader.closeSearch);
})
$('main').on('click', vueHeader.closeSearch)
}
+10 -10
View File
@@ -1,16 +1,16 @@
function setInputSelection (input, startPos, endPos) {
input.focus();
if (typeof input.selectionStart != "undefined") {
input.selectionStart = startPos;
input.selectionEnd = endPos;
input.focus()
if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos
input.selectionEnd = endPos
} else if (document.selection && document.selection.createRange) {
// IE branch
input.select();
var range = document.selection.createRange();
range.collapse(true);
range.moveEnd("character", endPos);
range.moveStart("character", startPos);
range.select();
input.select()
var range = document.selection.createRange()
range.collapse(true)
range.moveEnd('character', endPos)
range.moveStart('character', startPos)
range.select()
}
}
+4 -6
View File
@@ -1,11 +1,9 @@
function makeSafePath (rawPath) {
let rawParts = _.split(_.trim(rawPath), '/');
let rawParts = _.split(_.trim(rawPath), '/')
rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r)));
});
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r); }), '/');
return _.kebabCase(_.deburr(_.trim(r)))
})
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
}
+3 -5
View File
@@ -1,7 +1,5 @@
"use strict";
'use strict'
jQuery(document).ready(function ($) {
$('#login-user').focus();
});
$('#login-user').focus()
})
+8 -10
View File
@@ -11,20 +11,18 @@ let vueCreateUser = new Vue({
},
methods: {
open: (ev) => {
$('#modal-admin-users-create').addClass('is-active');
$('#modal-admin-users-create input').first().focus();
$('#modal-admin-users-create').addClass('is-active')
$('#modal-admin-users-create input').first().focus()
},
cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active');
vueCreateUser.email = '';
vueCreateUser.provider = 'local';
$('#modal-admin-users-create').removeClass('is-active')
vueCreateUser.email = ''
vueCreateUser.provider = 'local'
},
create: (ev) => {
vueCreateUser.cancel();
vueCreateUser.cancel()
}
}
});
})
$('.btn-create-prompt').on('click', vueCreateUser.open);
$('.btn-create-prompt').on('click', vueCreateUser.open)
+5 -5
View File
@@ -8,15 +8,15 @@ let vueDeleteUser = new Vue({
},
methods: {
open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active');
$('#modal-admin-users-delete').addClass('is-active')
},
cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active');
$('#modal-admin-users-delete').removeClass('is-active')
},
deleteUser: (ev) => {
vueDeleteUser.cancel();
vueDeleteUser.cancel()
}
}
});
})
$('.btn-deluser-prompt').on('click', vueDeleteUser.open);
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
+13 -15
View File
@@ -1,29 +1,27 @@
// -> Create New Document
let suggestedCreatePath = currentBasePath + '/new-page';
let suggestedCreatePath = currentBasePath + '/new-page'
$('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath);
$('#modal-create-prompt').toggleClass('is-active');
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length);
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden');
});
$('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('#txt-create-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-create-go').trigger('click');
$('.btn-create-go').trigger('click')
}
});
})
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val());
let newDocPath = makeSafePath($('#txt-create-prompt').val())
if (_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden');
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-create-prompt').parent().addClass('is-loading');
window.location.assign('/create/' + newDocPath);
$('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath)
}
});
})
+17 -20
View File
@@ -2,31 +2,30 @@
// -> Move Existing Document
if (currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden');
$('.btn-move-prompt').removeClass('is-hidden')
}
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1;
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
$('.btn-move-prompt').on('click', (ev) => {
$('#txt-move-prompt').val(currentBasePath);
$('#modal-move-prompt').toggleClass('is-active');
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length);
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden');
});
$('#txt-move-prompt').val(currentBasePath)
$('#modal-move-prompt').toggleClass('is-active')
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length)
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('#txt-move-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-move-go').trigger('click');
$('.btn-move-go').trigger('click')
}
});
})
$('.btn-move-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-move-prompt').val());
let newDocPath = makeSafePath($('#txt-move-prompt').val())
if (_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden');
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-move-prompt').parent().addClass('is-loading');
$('#txt-move-prompt').parent().addClass('is-loading')
$.ajax(window.location.href, {
data: {
@@ -36,14 +35,12 @@ $('.btn-move-go').on('click', (ev) => {
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if (rData.ok) {
window.location.assign('/' + newDocPath);
window.location.assign('/' + newDocPath)
} else {
alerts.pushError('Something went wrong', rData.error);
alerts.pushError('Something went wrong', rData.error)
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.');
});
alerts.pushError('Something went wrong', 'Save operation failed.')
})
}
});
})
+17 -23
View File
@@ -1,6 +1,5 @@
if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
@@ -11,30 +10,28 @@ if($('#page-type-admin-profile').length) {
methods: {
saveUser: (ev) => {
if (vueProfile.password !== vueProfile.passwordVerify) {
alerts.pushError('Error', "Passwords don't match!");
return;
alerts.pushError('Error', "Passwords don't match!")
return
}
$.post(window.location.href, {
password: vueProfile.password,
name: vueProfile.name
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.');
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp);
alerts.pushError('Error', resp)
})
}
},
created: function () {
this.name = usrDataName;
this.name = usrDataName
}
});
})
} else if ($('#page-type-admin-users').length) {
// =include ../modals/admin-users-create.js
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
el: '#page-type-admin-users-edit',
data: {
@@ -52,7 +49,7 @@ if($('#page-type-admin-profile').length) {
path: '/',
exact: false,
deny: false
});
})
},
removeRightsRow: (idx) => {
_.pullAt(vueEditUser.rights, idx)
@@ -68,35 +65,32 @@ if($('#page-type-admin-profile').length) {
exact: false,
deny: false
})
break;
break
}
$.post(window.location.href, {
password: vueEditUser.password,
name: vueEditUser.name,
rights: JSON.stringify(formattedRights)
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.');
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp);
alerts.pushError('Error', resp)
})
}
},
created: function () {
this.id = usrData._id;
this.email = usrData.email;
this.name = usrData.name;
this.id = usrData._id
this.email = usrData.email
this.name = usrData.name
if (_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin']);
this.roleoverride = 'admin';
this.rights = _.reject(usrData.rights, ['role', 'admin'])
this.roleoverride = 'admin'
} else {
this.rights = usrData.rights;
this.rights = usrData.rights
}
}
});
})
// =include ../modals/admin-users-delete.js
}
+3 -5
View File
@@ -1,14 +1,12 @@
if ($('#page-type-create').length) {
let pageEntryPath = $('#page-type-create').data('entrypath');
let pageEntryPath = $('#page-type-create').data('entrypath')
// -> Discard
$('.btn-create-discard').on('click', (ev) => {
$('#modal-create-discard').toggleClass('is-active');
});
$('#modal-create-discard').toggleClass('is-active')
})
// =include ../components/editor.js
}
+3 -5
View File
@@ -1,14 +1,12 @@
if ($('#page-type-edit').length) {
let pageEntryPath = $('#page-type-edit').data('entrypath');
let pageEntryPath = $('#page-type-edit').data('entrypath')
// -> Discard
$('.btn-edit-discard').on('click', (ev) => {
$('#modal-edit-discard').toggleClass('is-active');
});
$('#modal-edit-discard').toggleClass('is-active')
})
// =include ../components/editor.js
}
+9 -11
View File
@@ -1,18 +1,16 @@
if ($('#page-type-source').length) {
var scEditor = ace.edit('source-display')
scEditor.setTheme('ace/theme/tomorrow_night')
scEditor.getSession().setMode('ace/mode/markdown')
scEditor.setOption('fontSize', '14px')
scEditor.setOption('hScrollBarAlwaysVisible', false)
scEditor.setOption('wrap', true)
scEditor.setReadOnly(true)
scEditor.renderer.updateFull()
var scEditor = ace.edit("source-display");
scEditor.setTheme("ace/theme/tomorrow_night");
scEditor.getSession().setMode("ace/mode/markdown");
scEditor.setOption('fontSize', '14px');
scEditor.setOption('hScrollBarAlwaysVisible', false);
scEditor.setOption('wrap', true);
scEditor.setReadOnly(true);
scEditor.renderer.updateFull();
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : '';
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
// =include ../modals/create.js
// =include ../modals/move.js
}
+1 -3
View File
@@ -1,9 +1,7 @@
if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : '';
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : ''
// =include ../modals/create.js
// =include ../modals/move.js
}
+53 -69
View File
@@ -1,61 +1,56 @@
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
const Promise = require('bluebird');
const validator = require('validator');
const _ = require('lodash');
var express = require('express')
var router = express.Router()
const Promise = require('bluebird')
const validator = require('validator')
const _ = require('lodash')
/**
* Admin
*/
router.get('/', (req, res) => {
res.redirect('/admin/profile');
});
res.redirect('/admin/profile')
})
router.get('/profile', (req, res) => {
if (res.locals.isGuest) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
res.render('pages/admin/profile', { adminTab: 'profile' });
});
res.render('pages/admin/profile', { adminTab: 'profile' })
})
router.post('/profile', (req, res) => {
if (res.locals.isGuest) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
return db.User.findById(req.user.id).then((usr) => {
usr.name = _.trim(req.body.name);
usr.name = _.trim(req.body.name)
if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password);
let nPwd = _.trim(req.body.password)
if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd;
return usr.save();
});
usr.password = pwd
return usr.save()
})
}
} else {
return usr.save();
return usr.save()
}
}).then(() => {
return res.json({ msg: 'OK' });
return res.json({ msg: 'OK' })
}).catch((err) => {
res.status(400).json({ msg: err.message });
res.status(400).json({ msg: err.message })
})
})
});
router.get('/stats', (req, res) => {
if (res.locals.isGuest) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
Promise.all([
@@ -64,99 +59,88 @@ router.get('/stats', (req, res) => {
db.User.count()
]).spread((totalEntries, totalUploads, totalUsers) => {
return res.render('pages/admin/stats', {
totalEntries, totalUploads, totalUsers,
adminTab: 'stats'
}) || true;
totalEntries, totalUploads, totalUsers, adminTab: 'stats'
}) || true
}).catch((err) => {
throw err;
});
});
throw err
})
})
router.get('/users', (req, res) => {
if (!res.locals.rights.manage) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
db.User.find({})
.select('-password -rights')
.sort('name email')
.exec().then((usrs) => {
res.render('pages/admin/users', { adminTab: 'users', usrs });
});
});
res.render('pages/admin/users', { adminTab: 'users', usrs })
})
})
router.get('/users/:id', (req, res) => {
if (!res.locals.rights.manage) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
if (!validator.isMongoId(req.params.id)) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
db.User.findById(req.params.id)
.select('-password -providerId')
.exec().then((usr) => {
let usrOpts = {
canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin),
canChangeName: (usr.email !== 'guest'),
canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'),
canChangeRole: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin)),
canBeDeleted: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin))
};
}
res.render('pages/admin/users-edit', { adminTab: 'users', usr, usrOpts });
});
});
res.render('pages/admin/users-edit', { adminTab: 'users', usr, usrOpts })
})
})
router.post('/users/:id', (req, res) => {
if (!res.locals.rights.manage) {
return res.status(401).json({ msg: 'Unauthorized' });
return res.status(401).json({ msg: 'Unauthorized' })
}
if (!validator.isMongoId(req.params.id)) {
return res.status(400).json({ msg: 'Invalid User ID' });
return res.status(400).json({ msg: 'Invalid User ID' })
}
return db.User.findById(req.params.id).then((usr) => {
usr.name = _.trim(req.body.name);
usr.rights = JSON.parse(req.body.rights);
usr.name = _.trim(req.body.name)
usr.rights = JSON.parse(req.body.rights)
if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password);
let nPwd = _.trim(req.body.password)
if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd;
return usr.save();
});
usr.password = pwd
return usr.save()
})
}
} else {
return usr.save();
return usr.save()
}
}).then(() => {
return res.json({ msg: 'OK' });
return res.json({ msg: 'OK' })
}).catch((err) => {
res.status(400).json({ msg: err.message });
res.status(400).json({ msg: err.message })
})
})
});
router.get('/settings', (req, res) => {
if (!res.locals.rights.manage) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
res.render('pages/admin/settings', { adminTab: 'settings' });
res.render('pages/admin/settings', { adminTab: 'settings' })
})
});
module.exports = router;
module.exports = router
+35 -35
View File
@@ -1,15 +1,17 @@
var express = require('express');
var router = express.Router();
var passport = require('passport');
var ExpressBrute = require('express-brute');
var ExpressBruteMongooseStore = require('express-brute-mongoose');
var moment = require('moment');
'use strict'
const express = require('express')
const router = express.Router()
const passport = require('passport')
const ExpressBrute = require('express-brute')
const ExpressBruteMongooseStore = require('express-brute-mongoose')
const moment = require('moment')
/**
* Setup Express-Brute
*/
var EBstore = new ExpressBruteMongooseStore(db.Bruteforce);
var bruteforce = new ExpressBrute(EBstore, {
const EBstore = new ExpressBruteMongooseStore(db.Bruteforce)
const bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5,
minWait: 60 * 1000,
maxWait: 5 * 60 * 1000,
@@ -20,10 +22,10 @@ var bruteforce = new ExpressBrute(EBstore, {
title: 'Too many attempts!',
message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.',
iconClass: 'fa-times'
});
res.redirect('/login');
})
res.redirect('/login')
}
});
})
/**
* Login form
@@ -31,50 +33,48 @@ var bruteforce = new ExpressBrute(EBstore, {
router.get('/login', function (req, res, next) {
res.render('auth/login', {
usr: res.locals.usr
});
});
})
})
router.post('/login', bruteforce.prevent, function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
if (err) { return next(err); }
if (err) { return next(err) }
if (!user) {
req.flash('alert', {
title: 'Invalid login',
message: "The email or password is invalid."
});
return res.redirect('/login');
message: 'The email or password is invalid.'
})
return res.redirect('/login')
}
req.logIn(user, function (err) {
if (err) { return next(err); }
if (err) { return next(err) }
req.brute.reset(function () {
return res.redirect('/');
});
});
})(req, res, next);
});
return res.redirect('/')
})
})
})(req, res, next)
})
/**
* Social Login
*/
router.get('/login/ms', passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] }));
router.get('/login/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/login/facebook', passport.authenticate('facebook', { scope: ['public_profile', 'email'] }));
router.get('/login/ms', passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] }))
router.get('/login/google', passport.authenticate('google', { scope: ['profile', 'email'] }))
router.get('/login/facebook', passport.authenticate('facebook', { scope: ['public_profile', 'email'] }))
router.get('/login/ms/callback', passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/google/callback', passport.authenticate('google', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/ms/callback', passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/google/callback', passport.authenticate('google', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }))
/**
* Logout
*/
router.get('/logout', function (req, res) {
req.logout();
res.redirect('/');
});
req.logout()
res.redirect('/')
})
module.exports = router;
module.exports = router
+63 -80
View File
@@ -1,8 +1,8 @@
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
var _ = require('lodash');
const express = require('express')
const router = express.Router()
const _ = require('lodash')
// ==========================================
// EDIT MODE
@@ -12,12 +12,11 @@ var _ = require('lodash');
* Edit document in Markdown
*/
router.get('/edit/*', (req, res, next) => {
if (!res.locals.rights.write) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''))
entries.fetchOriginal(safePath, {
parseMarkdown: false,
@@ -28,116 +27,108 @@ router.get('/edit/*', (req, res, next) => {
cache: false
}).then((pageData) => {
if (pageData) {
res.render('pages/edit', { pageData });
res.render('pages/edit', { pageData })
} else {
throw new Error('Invalid page path.');
throw new Error('Invalid page path.')
}
return true;
return true
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
})
})
})
router.put('/edit/*', (req, res, next) => {
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
})
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''))
entries.update(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true;
}) || true
}).catch((err) => {
res.json({
ok: false,
error: err.message
});
});
});
})
})
})
// ==========================================
// CREATE MODE
// ==========================================
router.get('/create/*', (req, res, next) => {
if (!res.locals.rights.write) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
if(_.some(['create','edit','account','source','history','mk'], (e) => { return _.startsWith(req.path, '/create/' + e); })) {
if (_.some(['create', 'edit', 'account', 'source', 'history', 'mk'], (e) => { return _.startsWith(req.path, '/create/' + e) })) {
return res.render('error', {
message: 'You cannot create a document with this name as it is reserved by the system.',
error: {}
});
})
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
let safePath = entries.parsePath(_.replace(req.path, '/create', ''))
entries.exists(safePath).then((docExists) => {
if (!docExists) {
return entries.getStarter(safePath).then((contents) => {
let pageData = {
markdown: contents,
meta: {
title: _.startCase(safePath),
path: safePath
}
};
res.render('pages/create', { pageData });
return true;
}
res.render('pages/create', { pageData })
return true
}).catch((err) => {
throw new Error('Could not load starter content!');
});
winston.warn(err)
throw new Error('Could not load starter content!')
})
} else {
throw new Error('This entry already exists!');
throw new Error('This entry already exists!')
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
})
})
})
router.put('/create/*', (req, res, next) => {
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
})
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
let safePath = entries.parsePath(_.replace(req.path, '/create', ''))
entries.create(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true;
}) || true
}).catch((err) => {
return res.json({
ok: false,
error: err.message
});
});
});
})
})
})
// ==========================================
// VIEW MODE
@@ -147,8 +138,7 @@ router.put('/create/*', (req, res, next) => {
* View source of a document
*/
router.get('/source/*', (req, res, next) => {
let safePath = entries.parsePath(_.replace(req.path, '/source', ''));
let safePath = entries.parsePath(_.replace(req.path, '/source', ''))
entries.fetchOriginal(safePath, {
parseMarkdown: false,
@@ -159,90 +149,83 @@ router.get('/source/*', (req, res, next) => {
cache: false
}).then((pageData) => {
if (pageData) {
res.render('pages/source', { pageData });
res.render('pages/source', { pageData })
} else {
throw new Error('Invalid page path.');
throw new Error('Invalid page path.')
}
return true;
return true
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
})
})
})
/**
* View document
*/
router.get('/*', (req, res, next) => {
let safePath = entries.parsePath(req.path);
let safePath = entries.parsePath(req.path)
entries.fetch(safePath).then((pageData) => {
if (pageData) {
res.render('pages/view', { pageData });
res.render('pages/view', { pageData })
} else {
res.render('error-notexist', {
newpath: safePath
});
})
}
return true;
return true
}).error((err) => {
if (safePath === 'home') {
res.render('pages/welcome');
res.render('pages/welcome')
} else {
res.render('error-notexist', {
message: err.message,
newpath: safePath
});
})
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
})
})
})
/**
* Move document
*/
router.put('/*', (req, res, next) => {
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
})
}
let safePath = entries.parsePath(req.path);
let safePath = entries.parsePath(req.path)
if (_.isEmpty(req.body.move)) {
return res.json({
ok: false,
error: 'Invalid document action call.'
});
})
}
let safeNewPath = entries.parsePath(req.body.move);
let safeNewPath = entries.parsePath(req.body.move)
entries.move(safePath, safeNewPath).then(() => {
res.json({
ok: true
});
})
}).catch((err) => {
res.json({
ok: false,
error: err.message
});
});
})
})
})
});
module.exports = router;
module.exports = router
+66 -90
View File
@@ -1,27 +1,26 @@
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
const express = require('express')
const router = express.Router()
var readChunk = require('read-chunk'),
fileType = require('file-type'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
path = require('path'),
_ = require('lodash');
const readChunk = require('read-chunk')
const fileType = require('file-type')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const path = require('path')
const _ = require('lodash')
var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$");
var validPathThumbsRe = new RegExp("^([0-9]+\\.png)$");
const validPathRe = new RegExp('^([a-z0-9\\/-]+\\.[a-z0-9]+)$')
const validPathThumbsRe = new RegExp('^([0-9]+\\.png)$')
// ==========================================
// SERVE UPLOADS FILES
// ==========================================
router.get('/t/*', (req, res, next) => {
let fileName = req.params[0];
let fileName = req.params[0]
if (!validPathThumbsRe.test(fileName)) {
return res.sendStatus(404).end();
return res.sendStatus(404).end()
}
// todo: Authentication-based access
@@ -31,141 +30,119 @@ router.get('/t/*', (req, res, next) => {
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end();
res.status(err.status).end()
}
});
});
})
})
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()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' });
return true;
res.json({ ok: false, msg: 'Invalid Folder' })
return true
}
Promise.map(req.files, (f) => {
let destFilename = '';
let destFilePath = '';
let destFilename = ''
let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
destFilename = fname;
destFilePath = path.resolve(destFolderPath, destFilename);
return readChunk(f.path, 0, 262);
return readChunk(f.path, 0, 262)
}).then((buf) => {
// -> Check MIME type by magic number
let mimeInfo = fileType(buf);
let mimeInfo = fileType(buf)
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return Promise.reject(new Error('Invalid file type.'));
return Promise.reject(new Error('Invalid file type.'))
}
return true;
return true
}).then(() => {
// -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false });
return fs.moveAsync(f.path, destFilePath, { clobber: false })
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
};
}).reflect();
}
}).reflect()
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if (r.isFulfilled()) {
return r.value();
return r.value()
} else {
return {
ok: false,
msg: r.reason().message
};
}
});
res.json({ ok: true, results: uplResults });
return true;
}
})
res.json({ ok: true, results: uplResults })
return true
}).catch((err) => {
res.json({ ok: false, msg: err.message });
return true;
});
});
});
res.json({ ok: false, msg: err.message })
return true
})
})
})
router.post('/file', lcdata.uploadFileHandler, (req, res, next) => {
let destFolder = _.chain(req.body.folder).trim().toLower().value();
let destFolder = _.chain(req.body.folder).trim().toLower().value()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' });
return true;
res.json({ ok: false, msg: 'Invalid Folder' })
return true
}
Promise.map(req.files, (f) => {
let destFilename = '';
let destFilePath = '';
let destFilename = ''
let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
destFilename = fname;
destFilePath = path.resolve(destFolderPath, destFilename);
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
// -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false });
return fs.moveAsync(f.path, destFilePath, { clobber: false })
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
};
}).reflect();
}
}).reflect()
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if (r.isFulfilled()) {
return r.value();
return r.value()
} else {
return {
ok: false,
msg: r.reason().message
};
}
});
res.json({ ok: true, results: uplResults });
return true;
}
})
res.json({ ok: true, results: uplResults })
return true
}).catch((err) => {
res.json({ ok: false, msg: err.message });
return true;
});
});
});
res.json({ ok: false, msg: err.message })
return true
})
})
})
router.get('/*', (req, res, next) => {
let fileName = req.params[0];
let fileName = req.params[0]
if (!validPathRe.test(fileName)) {
return res.sendStatus(404).end();
return res.sendStatus(404).end()
}
// todo: Authentication-based access
@@ -175,10 +152,9 @@ router.get('/*', (req, res, next) => {
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end();
res.status(err.status).end()
}
});
})
})
});
module.exports = router;
module.exports = router
+44 -44
View File
@@ -1,9 +1,10 @@
"use strict";
'use strict'
const _ = require('lodash')
module.exports = (socket) => {
if (!socket.request.user.logged_in) {
return;
return
}
// -----------------------------------------
@@ -11,85 +12,84 @@ module.exports = (socket) => {
// -----------------------------------------
socket.on('search', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
entries.search(data.terms).then((results) => {
return cb(results) || true;
});
});
return cb(results) || true
})
})
// -----------------------------------------
// UPLOADS
// -----------------------------------------
socket.on('uploadsGetFolders', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFolders().then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsCreateFolder', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.createUploadsFolder(data.foldername).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsGetImages', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFiles('image', data.folder).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsGetFiles', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFiles('binary', data.folder).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsDeleteFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.deleteUploadsFile(data.uid).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsFetchFileFromURL', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.downloadFromUrl(data.folder, data.fetchUrl).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
}) || true
})
})
socket.on('uploadsRenameFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder, data.filename).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
}) || true
})
})
socket.on('uploadsMoveFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
};
}) || true
})
})
}
+56 -61
View File
@@ -1,26 +1,27 @@
var gulp = require("gulp");
var watch = require('gulp-watch');
var merge = require('merge-stream');
var babel = require("gulp-babel");
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var nodemon = require('gulp-nodemon');
var plumber = require('gulp-plumber');
var zip = require('gulp-zip');
var tar = require('gulp-tar');
var gzip = require('gulp-gzip');
var sass = require('gulp-sass');
var cleanCSS = require('gulp-clean-css');
var include = require("gulp-include");
var run = require('run-sequence');
var _ = require('lodash');
'use strict'
const gulp = require('gulp')
const watch = require('gulp-watch')
const merge = require('merge-stream')
const babel = require('gulp-babel')
const uglify = require('gulp-uglify')
const concat = require('gulp-concat')
const nodemon = require('gulp-nodemon')
const plumber = require('gulp-plumber')
const zip = require('gulp-zip')
const tar = require('gulp-tar')
const gzip = require('gulp-gzip')
const sass = require('gulp-sass')
const cleanCSS = require('gulp-clean-css')
const include = require('gulp-include')
const run = require('run-sequence')
/**
* Paths
*
* @type {Object}
*/
var paths = {
const paths = {
scripts: {
combine: [
'./node_modules/socket.io-client/dist/socket.io.min.js',
@@ -81,7 +82,7 @@ var paths = {
'!.babelrc', '!.gitattributes', '!.gitignore', '!.snyk', '!.travis.yml',
'!gulpfile.js', '!inch.json', '!config.yml', '!wiki.sublime-project'
]
};
}
/**
* TASK - Starts server in development mode
@@ -92,111 +93,105 @@ gulp.task('server', ['scripts', 'css', 'fonts'], function() {
ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'],
ext: 'js json',
env: { 'NODE_ENV': 'development' }
});
});
})
})
/**
* TASK - Process all scripts processes
*/
gulp.task("scripts", ['scripts-libs', 'scripts-app']);
gulp.task('scripts', ['scripts-libs', 'scripts-app'])
/**
* TASK - Combine js libraries
*/
gulp.task("scripts-libs", function () {
gulp.task('scripts-libs', function () {
return merge(
gulp.src(paths.scripts.combine)
.pipe(concat('libs.js', {newLine: ';\n'}))
.pipe(uglify({ mangle: false }))
.pipe(gulp.dest("./assets/js")),
.pipe(gulp.dest('./assets/js')),
gulp.src(paths.scripts.ace)
.pipe(gulp.dest("./assets/js/ace"))
.pipe(gulp.dest('./assets/js/ace'))
);
});
)
})
/**
* TASK - Combine, make compatible and compress js app scripts
*/
gulp.task("scripts-app", function () {
gulp.task('scripts-app', function () {
return gulp.src(paths.scripts.compile)
.pipe(plumber())
.pipe(include({ extensions: "js" }))
.pipe(include({ extensions: 'js' }))
.pipe(babel())
.pipe(uglify())
.pipe(plumber.stop())
.pipe(gulp.dest("./assets/js"));
});
.pipe(gulp.dest('./assets/js'))
})
/**
* TASK - Process all css processes
*/
gulp.task("css", ['css-libs', 'css-app']);
gulp.task('css', ['css-libs', 'css-app'])
/**
* TASK - Combine css libraries
*/
gulp.task("css-libs", function () {
gulp.task('css-libs', function () {
return gulp.src(paths.css.combine)
.pipe(plumber())
.pipe(concat('libs.css'))
.pipe(cleanCSS({ keepSpecialComments: 0 }))
.pipe(plumber.stop())
.pipe(gulp.dest("./assets/css"));
});
.pipe(gulp.dest('./assets/css'))
})
/**
* TASK - Combine app css
*/
gulp.task("css-app", function () {
gulp.task('css-app', function () {
return gulp.src(paths.css.compile)
.pipe(plumber())
.pipe(sass.sync({ includePaths: paths.css.includes }))
.pipe(cleanCSS({ keepSpecialComments: 0 }))
.pipe(plumber.stop())
.pipe(gulp.dest("./assets/css"));
});
.pipe(gulp.dest('./assets/css'))
})
/**
* TASK - Copy web fonts
*/
gulp.task("fonts", function () {
gulp.task('fonts', function () {
return gulp.src(paths.fonts)
.pipe(gulp.dest("./assets/fonts"));
});
.pipe(gulp.dest('./assets/fonts'))
})
/**
* TASK - Start dev watchers
*/
gulp.task('watch', function () {
return merge(
watch(paths.scripts.watch, {base: './'}, function() { return gulp.start('scripts-app'); }),
watch(paths.css.watch, {base: './'}, function() { return gulp.start('css-app'); })
);
});
watch(paths.scripts.watch, {base: './'}, function () { return gulp.start('scripts-app') }),
watch(paths.css.watch, {base: './'}, function () { return gulp.start('css-app') })
)
})
/**
* TASK - Starts development server with watchers
*/
gulp.task('default', ['watch', 'server']);
gulp.task('default', ['watch', 'server'])
gulp.task('dev', function () {
paths.css.includes.pop()
paths.css.includes.push('../core')
paths.css.includes.pop();
paths.css.includes.push('../core');
paths.fonts.pop()
paths.fonts.push('../core/core-client/fonts/**/*')
paths.fonts.pop();
paths.fonts.push('../core/core-client/fonts/**/*');
return run('default');
});
return run('default')
})
/**
* TASK - Creates deployment packages
@@ -204,12 +199,12 @@ gulp.task('dev', function() {
gulp.task('deploy', ['scripts', 'css', 'fonts'], function () {
var zipStream = gulp.src(paths.deploy)
.pipe(zip('wiki-js.zip'))
.pipe(gulp.dest('dist'));
.pipe(gulp.dest('dist'))
var targzStream = gulp.src(paths.deploy)
.pipe(tar('wiki-js.tar'))
.pipe(gzip())
.pipe(gulp.dest('dist'));
.pipe(gulp.dest('dist'))
return merge(zipStream, targzStream);
});
return merge(zipStream, targzStream)
})
+121 -169
View File
@@ -1,11 +1,10 @@
"use strict";
'use strict'
var Promise = require('bluebird'),
path = require('path'),
fs = Promise.promisifyAll(require("fs-extra")),
_ = require('lodash'),
farmhash = require('farmhash'),
moment = require('moment');
const Promise = require('bluebird')
const path = require('path')
const fs = Promise.promisifyAll(require('fs-extra'))
const _ = require('lodash')
const farmhash = require('farmhash')
/**
* Entries Model
@@ -21,14 +20,12 @@ module.exports = {
* @return {Object} Entries model instance
*/
init () {
let self = this
let self = this;
self._repoPath = path.resolve(ROOTPATH, appconfig.paths.repo);
self._cachePath = path.resolve(ROOTPATH, appconfig.paths.data, 'cache');
return self;
self._repoPath = path.resolve(ROOTPATH, appconfig.paths.repo)
self._cachePath = path.resolve(ROOTPATH, appconfig.paths.data, 'cache')
return self
},
/**
@@ -38,8 +35,7 @@ module.exports = {
* @return {Promise<Boolean>} True if exists, false otherwise
*/
exists (entryPath) {
let self = this;
let self = this
return self.fetchOriginal(entryPath, {
parseMarkdown: false,
@@ -49,11 +45,10 @@ module.exports = {
includeParentInfo: false,
cache: false
}).then(() => {
return true;
}).catch((err) => {
return false;
});
return true
}).catch((err) => { // eslint-disable-line handle-callback-err
return false
})
},
/**
@@ -63,39 +58,31 @@ module.exports = {
* @return {Promise<Object>} Page Data
*/
fetch (entryPath) {
let self = this
let self = this;
let cpath = self.getCachePath(entryPath);
let cpath = self.getCachePath(entryPath)
return fs.statAsync(cpath).then((st) => {
return st.isFile();
}).catch((err) => {
return false;
return st.isFile()
}).catch((err) => { // eslint-disable-line handle-callback-err
return false
}).then((isCache) => {
if (isCache) {
// Load from cache
return fs.readFileAsync(cpath).then((contents) => {
return JSON.parse(contents);
}).catch((err) => {
winston.error('Corrupted cache file. Deleting it...');
fs.unlinkSync(cpath);
return false;
});
return JSON.parse(contents)
}).catch((err) => { // eslint-disable-line handle-callback-err
winston.error('Corrupted cache file. Deleting it...')
fs.unlinkSync(cpath)
return false
})
} else {
// Load original
return self.fetchOriginal(entryPath);
return self.fetchOriginal(entryPath)
}
});
})
},
/**
@@ -106,11 +93,10 @@ module.exports = {
* @return {Promise<Object>} Page data
*/
fetchOriginal (entryPath, options) {
let self = this
let self = this;
let fpath = self.getFullPath(entryPath);
let cpath = self.getCachePath(entryPath);
let fpath = self.getFullPath(entryPath)
let cpath = self.getCachePath(entryPath)
options = _.defaults(options, {
parseMarkdown: true,
@@ -119,12 +105,11 @@ module.exports = {
includeMarkdown: false,
includeParentInfo: true,
cache: true
});
})
return fs.statAsync(fpath).then((st) => {
if (st.isFile()) {
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
// Parse contents
let pageData = {
@@ -132,46 +117,43 @@ module.exports = {
html: (options.parseMarkdown) ? mark.parseContent(contents) : '',
meta: (options.parseMeta) ? mark.parseMeta(contents) : {},
tree: (options.parseTree) ? mark.parseTree(contents) : []
};
if(!pageData.meta.title) {
pageData.meta.title = _.startCase(entryPath);
}
pageData.meta.path = entryPath;
if (!pageData.meta.title) {
pageData.meta.title = _.startCase(entryPath)
}
pageData.meta.path = entryPath
// Get parent
let parentPromise = (options.includeParentInfo) ? self.getParentInfo(entryPath).then((parentData) => {
return (pageData.parent = parentData);
}).catch((err) => {
return (pageData.parent = false);
}) : Promise.resolve(true);
return (pageData.parent = parentData)
}).catch((err) => { // eslint-disable-line handle-callback-err
return (pageData.parent = false)
}) : Promise.resolve(true)
return parentPromise.then(() => {
// Cache to disk
if (options.cache) {
let cacheData = JSON.stringify(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false);
let cacheData = JSON.stringify(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false)
return fs.writeFileAsync(cpath, cacheData).catch((err) => {
winston.error('Unable to write to cache! Performance may be affected.');
return true;
});
winston.error('Unable to write to cache! Performance may be affected.')
winston.error(err)
return true
})
} else {
return true;
return true
}
}).return(pageData);
});
}).return(pageData)
})
} else {
return false;
return false
}
}).catch((err) => {
return Promise.reject(new Promise.OperationalError('Entry ' + entryPath + ' does not exist!'));
});
}).catch((err) => { // eslint-disable-line handle-callback-err
return Promise.reject(new Promise.OperationalError('Entry ' + entryPath + ' does not exist!'))
})
},
/**
@@ -181,19 +163,17 @@ module.exports = {
* @return {String} Safe entry path
*/
parsePath (urlPath) {
let wlist = new RegExp('[^a-z0-9/-]', 'g')
let wlist = new RegExp('[^a-z0-9/\-]','g');
urlPath = _.toLower(urlPath).replace(wlist, '');
urlPath = _.toLower(urlPath).replace(wlist, '')
if (urlPath === '/') {
urlPath = 'home';
urlPath = 'home'
}
let urlParts = _.filter(_.split(urlPath, '/'), (p) => { return !_.isEmpty(p); });
return _.join(urlParts, '/');
let urlParts = _.filter(_.split(urlPath, '/'), (p) => { return !_.isEmpty(p) })
return _.join(urlParts, '/')
},
/**
@@ -203,38 +183,32 @@ module.exports = {
* @return {Promise<Object|False>} The parent information.
*/
getParentInfo (entryPath) {
let self = this;
let self = this
if (_.includes(entryPath, '/')) {
let parentParts = _.initial(_.split(entryPath, '/'));
let parentPath = _.join(parentParts,'/');
let parentFile = _.last(parentParts);
let fpath = self.getFullPath(parentPath);
let parentParts = _.initial(_.split(entryPath, '/'))
let parentPath = _.join(parentParts, '/')
let parentFile = _.last(parentParts)
let fpath = self.getFullPath(parentPath)
return fs.statAsync(fpath).then((st) => {
if (st.isFile()) {
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
let pageMeta = mark.parseMeta(contents);
let pageMeta = mark.parseMeta(contents)
return {
path: parentPath,
title: (pageMeta.title) ? pageMeta.title : _.startCase(parentFile),
subtitle: (pageMeta.subtitle) ? pageMeta.subtitle : false
};
});
} else {
return Promise.reject(new Error('Parent entry is not a valid file.'));
}
});
})
} else {
return Promise.reject(new Error('Parent entry is root.'));
return Promise.reject(new Error('Parent entry is not a valid file.'))
}
})
} else {
return Promise.reject(new Error('Parent entry is root.'))
}
},
/**
@@ -244,7 +218,7 @@ module.exports = {
* @return {String} The full path.
*/
getFullPath (entryPath) {
return path.join(this._repoPath, entryPath + '.md');
return path.join(this._repoPath, entryPath + '.md')
},
/**
@@ -254,7 +228,7 @@ module.exports = {
* @return {String} The full cache path.
*/
getCachePath (entryPath) {
return path.join(this._cachePath, farmhash.fingerprint32(entryPath) + '.json');
return path.join(this._cachePath, farmhash.fingerprint32(entryPath) + '.json')
},
/**
@@ -264,8 +238,8 @@ module.exports = {
* @return {String} The entry path
*/
getEntryPathFromFullPath (fullPath) {
let absRepoPath = path.resolve(ROOTPATH, this._repoPath);
return _.chain(fullPath).replace(absRepoPath, '').replace('.md', '').replace(new RegExp('\\\\', 'g'),'/').value();
let absRepoPath = path.resolve(ROOTPATH, this._repoPath)
return _.chain(fullPath).replace(absRepoPath, '').replace('.md', '').replace(new RegExp('\\\\', 'g'), '/').value()
},
/**
@@ -276,23 +250,21 @@ module.exports = {
* @return {Promise<Boolean>} True on success, false on failure
*/
update (entryPath, contents) {
let self = this;
let fpath = self.getFullPath(entryPath);
let self = this
let fpath = self.getFullPath(entryPath)
return fs.statAsync(fpath).then((st) => {
if (st.isFile()) {
return self.makePersistent(entryPath, contents).then(() => {
return self.updateCache(entryPath);
});
return self.updateCache(entryPath)
})
} else {
return Promise.reject(new Error('Entry does not exist!'));
return Promise.reject(new Error('Entry does not exist!'))
}
}).catch((err) => {
winston.error(err);
return Promise.reject(new Error('Failed to save document.'));
});
winston.error(err)
return Promise.reject(new Error('Failed to save document.'))
})
},
/**
@@ -302,8 +274,7 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
updateCache (entryPath) {
let self = this;
let self = this
return self.fetchOriginal(entryPath, {
parseMarkdown: true,
@@ -318,7 +289,7 @@ module.exports = {
meta: pageData.meta,
parent: pageData.parent || {},
text: mark.removeMarkdown(pageData.markdown)
};
}
}).then((content) => {
return db.Entry.findOneAndUpdate({
_id: content.entryPath
@@ -331,9 +302,8 @@ module.exports = {
}, {
new: true,
upsert: true
});
});
})
})
},
/**
@@ -344,22 +314,20 @@ module.exports = {
* @return {Promise<Boolean>} True on success, false on failure
*/
create (entryPath, contents) {
let self = this;
let self = this
return self.exists(entryPath).then((docExists) => {
if (!docExists) {
return self.makePersistent(entryPath, contents).then(() => {
return self.updateCache(entryPath);
});
return self.updateCache(entryPath)
})
} else {
return Promise.reject(new Error('Entry already exists!'));
return Promise.reject(new Error('Entry already exists!'))
}
}).catch((err) => {
winston.error(err);
return Promise.reject(new Error('Something went wrong.'));
});
winston.error(err)
return Promise.reject(new Error('Something went wrong.'))
})
},
/**
@@ -370,14 +338,12 @@ module.exports = {
* @return {Promise<Boolean>} True on success, false on failure
*/
makePersistent (entryPath, contents) {
let self = this;
let fpath = self.getFullPath(entryPath);
let self = this
let fpath = self.getFullPath(entryPath)
return fs.outputFileAsync(fpath, contents).then(() => {
return git.commitDocument(entryPath);
});
return git.commitDocument(entryPath)
})
},
/**
@@ -388,35 +354,31 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
move (entryPath, newEntryPath) {
let self = this;
let self = this
if (_.isEmpty(entryPath) || entryPath === 'home') {
return Promise.reject(new Error('Invalid path!'));
return Promise.reject(new Error('Invalid path!'))
}
return git.moveDocument(entryPath, newEntryPath).then(() => {
return git.commitDocument(newEntryPath).then(() => {
// Delete old cache version
let oldEntryCachePath = self.getCachePath(entryPath);
fs.unlinkAsync(oldEntryCachePath).catch((err) => { return true; });
let oldEntryCachePath = self.getCachePath(entryPath)
fs.unlinkAsync(oldEntryCachePath).catch((err) => { return true }) // eslint-disable-line handle-callback-err
// Delete old index entry
ws.emit('searchDel', {
auth: WSInternalKey,
entryPath
});
})
// Create cache for new entry
return self.updateCache(newEntryPath);
});
});
return self.updateCache(newEntryPath)
})
})
},
/**
@@ -426,14 +388,11 @@ module.exports = {
* @return {Promise<String>} Starter content
*/
getStarter (entryPath) {
let self = this;
let formattedTitle = _.startCase(_.last(_.split(entryPath, '/')));
let formattedTitle = _.startCase(_.last(_.split(entryPath, '/')))
return fs.readFileAsync(path.join(ROOTPATH, 'client/content/create.md'), 'utf8').then((contents) => {
return _.replace(contents, new RegExp('{TITLE}', 'g'), formattedTitle);
});
return _.replace(contents, new RegExp('{TITLE}', 'g'), formattedTitle)
})
},
/**
@@ -443,29 +402,26 @@ module.exports = {
* @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); })
.filter((f) => { return !_.isEmpty(f) })
.join(' ')
.value();
.value()
return db.Entry.find(
{ $text: { $search: terms } },
{ score: { $meta: "textScore" }, title: 1 }
{ score: { $meta: 'textScore' }, title: 1 }
)
.sort({ score: { $meta: "textScore" } })
.sort({ score: { $meta: 'textScore' } })
.limit(10)
.exec()
.then((hits) => {
if (hits.length < 5) {
let regMatch = new RegExp('^' + _.split(terms, ' ')[0]);
let regMatch = new RegExp('^' + _.split(terms, ' ')[0])
return db.Entry.find({
_id: { $regex: regMatch }
}, '_id')
@@ -476,25 +432,21 @@ module.exports = {
return {
match: hits,
suggest: (matches) ? _.map(matches, '_id') : []
};
});
}
})
} else {
return {
match: _.filter(hits, (h) => { return h._doc.score >= 1; }),
match: _.filter(hits, (h) => { return h._doc.score >= 1 }),
suggest: []
};
}
}
}).catch((err) => {
winston.error(err);
winston.error(err)
return {
match: [],
suggest: []
};
});
}
})
}
};
}
+76 -103
View File
@@ -1,13 +1,11 @@
"use strict";
'use strict'
var Git = require("git-wrapper2-promise"),
Promise = require('bluebird'),
path = require('path'),
os = require('os'),
fs = Promise.promisifyAll(require("fs")),
moment = require('moment'),
_ = require('lodash'),
URL = require('url');
const Git = require('git-wrapper2-promise')
const Promise = require('bluebird')
const path = require('path')
const fs = Promise.promisifyAll(require('fs'))
const _ = require('lodash')
const URL = require('url')
/**
* Git Model
@@ -37,28 +35,26 @@ module.exports = {
* @return {Object} Git model instance
*/
init () {
let self = this;
let self = this
// -> Build repository path
if (_.isEmpty(appconfig.paths.repo)) {
self._repo.path = path.join(ROOTPATH, 'repo');
self._repo.path = path.join(ROOTPATH, 'repo')
} else {
self._repo.path = appconfig.paths.repo;
self._repo.path = appconfig.paths.repo
}
// -> Initialize repository
self.onReady = self._initRepo(appconfig);
self.onReady = self._initRepo(appconfig)
// Define signature
self._signature.name = appconfig.git.signature.name || 'Wiki';
self._signature.email = appconfig.git.signature.email || 'user@example.com';
return self;
self._signature.name = appconfig.git.signature.name || 'Wiki'
self._signature.email = appconfig.git.signature.email || 'user@example.com'
return self
},
/**
@@ -68,60 +64,54 @@ module.exports = {
* @return {Object} Promise
*/
_initRepo (appconfig) {
let self = this
let self = this;
winston.info('[' + PROCNAME + '][GIT] Checking Git repository...');
winston.info('[' + PROCNAME + '][GIT] Checking Git repository...')
// -> Check if path is accessible
return fs.mkdirAsync(self._repo.path).catch((err) => {
if (err.code !== 'EEXIST') {
winston.error('[' + PROCNAME + '][GIT] Invalid Git repository path or missing permissions.');
winston.error('[' + PROCNAME + '][GIT] Invalid Git repository path or missing permissions.')
}
}).then(() => {
self._git = new Git({ 'git-dir': self._repo.path });
self._git = new Git({ 'git-dir': self._repo.path })
// -> Check if path already contains a git working folder
return self._git.isRepo().then((isRepo) => {
self._repo.exists = isRepo;
return (!isRepo) ? self._git.exec('init') : true;
}).catch((err) => {
self._repo.exists = false;
});
self._repo.exists = isRepo
return (!isRepo) ? self._git.exec('init') : true
}).catch((err) => { // eslint-disable-line handle-callback-err
self._repo.exists = false
})
}).then(() => {
// Initialize remote
let urlObj = URL.parse(appconfig.git.url);
urlObj.auth = appconfig.git.auth.username + ((appconfig.git.auth.type !== 'ssh') ? ':' + appconfig.git.auth.password : '');
self._url = URL.format(urlObj);
let urlObj = URL.parse(appconfig.git.url)
urlObj.auth = appconfig.git.auth.username + ((appconfig.git.auth.type !== 'ssh') ? ':' + appconfig.git.auth.password : '')
self._url = URL.format(urlObj)
return self._git.exec('remote', 'show').then((cProc) => {
let out = cProc.stdout.toString();
let out = cProc.stdout.toString()
if (_.includes(out, 'origin')) {
return true;
return true
} else {
return Promise.join(
self._git.exec('config', ['--local', 'user.name', self._signature.name]),
self._git.exec('config', ['--local', 'user.email', self._signature.email])
).then(() => {
return self._git.exec('remote', ['add', 'origin', self._url]);
});
return self._git.exec('remote', ['add', 'origin', self._url])
})
}
});
})
}).catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Git remote error!');
throw err;
winston.error('[' + PROCNAME + '][GIT] Git remote error!')
throw err
}).then(() => {
winston.info('[' + PROCNAME + '][GIT] Git repository is OK.');
return true;
});
winston.info('[' + PROCNAME + '][GIT] Git repository is OK.')
return true
})
},
/**
@@ -130,9 +120,7 @@ module.exports = {
* @return {String} The repo path.
*/
getRepoPath () {
return this._repo.path || path.join(ROOTPATH, 'repo');
return this._repo.path || path.join(ROOTPATH, 'repo')
},
/**
@@ -141,49 +129,40 @@ module.exports = {
* @return {Promise} Resolve on sync success
*/
resync () {
let self = this;
let self = this
// Fetch
winston.info('[' + PROCNAME + '][GIT] Performing pull from remote repository...');
winston.info('[' + PROCNAME + '][GIT] Performing pull from remote repository...')
return self._git.pull('origin', self._repo.branch).then((cProc) => {
winston.info('[' + PROCNAME + '][GIT] Pull completed.');
winston.info('[' + PROCNAME + '][GIT] Pull completed.')
})
.catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Unable to fetch from git origin!');
throw err;
winston.error('[' + PROCNAME + '][GIT] Unable to fetch from git origin!')
throw err
})
.then(() => {
// Check for changes
return self._git.exec('log', 'origin/' + self._repo.branch + '..HEAD').then((cProc) => {
let out = cProc.stdout.toString();
let out = cProc.stdout.toString()
if (_.includes(out, 'commit')) {
winston.info('[' + PROCNAME + '][GIT] Performing push to remote repository...');
winston.info('[' + PROCNAME + '][GIT] Performing push to remote repository...')
return self._git.push('origin', self._repo.branch).then(() => {
return winston.info('[' + PROCNAME + '][GIT] Push completed.');
});
return winston.info('[' + PROCNAME + '][GIT] Push completed.')
})
} else {
winston.info('[' + PROCNAME + '][GIT] Push skipped. Repository is already in sync.');
winston.info('[' + PROCNAME + '][GIT] Push skipped. Repository is already in sync.')
}
return true;
});
return true
})
})
.catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Unable to push changes to remote!');
throw err;
});
winston.error('[' + PROCNAME + '][GIT] Unable to push changes to remote!')
throw err
})
},
/**
@@ -193,23 +172,21 @@ module.exports = {
* @return {Promise} Resolve on commit success
*/
commitDocument (entryPath) {
let self = this;
let gitFilePath = entryPath + '.md';
let commitMsg = '';
let self = this
let gitFilePath = entryPath + '.md'
let commitMsg = ''
return self._git.exec('ls-files', gitFilePath).then((cProc) => {
let out = cProc.stdout.toString();
return _.includes(out, gitFilePath);
let out = cProc.stdout.toString()
return _.includes(out, gitFilePath)
}).then((isTracked) => {
commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath;
return self._git.add(gitFilePath);
commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath
return self._git.add(gitFilePath)
}).then(() => {
return self._git.commit(commitMsg).catch((err) => {
if(_.includes(err.stdout, 'nothing to commit')) { return true; }
});
});
if (_.includes(err.stdout, 'nothing to commit')) { return true }
})
})
},
/**
@@ -220,20 +197,18 @@ module.exports = {
* @return {Promise<Boolean>} Resolve on success
*/
moveDocument (entryPath, newEntryPath) {
let self = this;
let gitFilePath = entryPath + '.md';
let gitNewFilePath = newEntryPath + '.md';
let self = this
let gitFilePath = entryPath + '.md'
let gitNewFilePath = newEntryPath + '.md'
return self._git.exec('mv', [gitFilePath, gitNewFilePath]).then((cProc) => {
let out = cProc.stdout.toString();
let out = cProc.stdout.toString()
if (_.includes(out, 'fatal')) {
let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ',')));
throw new Error(errorMsg);
let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ',')))
throw new Error(errorMsg)
}
return true;
});
return true
})
},
/**
@@ -243,16 +218,14 @@ module.exports = {
* @return {Promise} Resolve on commit success
*/
commitUploads (msg) {
let self = this;
msg = msg || "Uploads repository sync";
let self = this
msg = msg || 'Uploads repository sync'
return self._git.add('uploads').then(() => {
return self._git.commit(msg).catch((err) => {
if(_.includes(err.stdout, 'nothing to commit')) { return true; }
});
});
if (_.includes(err.stdout, 'nothing to commit')) { return true }
})
})
}
};
}
+7 -13
View File
@@ -1,6 +1,6 @@
"use strict";
'use strict'
const crypto = require('crypto');
const crypto = require('crypto')
/**
* Internal Authentication
@@ -10,23 +10,17 @@ module.exports = {
_curKey: false,
init (inKey) {
this._curKey = inKey
this._curKey = inKey;
return this;
return this
},
generateKey () {
return crypto.randomBytes(20).toString('hex');
return crypto.randomBytes(20).toString('hex')
},
validateKey (inKey) {
return inKey === this._curKey;
return inKey === this._curKey
}
};
}
+49 -60
View File
@@ -1,11 +1,11 @@
"use strict";
'use strict'
var path = require('path'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
multer = require('multer'),
os = require('os'),
_ = require('lodash');
const path = require('path')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const multer = require('multer')
const os = require('os')
const _ = require('lodash')
/**
* Local Data Storage
@@ -23,15 +23,13 @@ module.exports = {
* @return {Object} Local Data Storage model instance
*/
init () {
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
this.createBaseDirectories(appconfig);
this.initMulter(appconfig);
return this;
this.createBaseDirectories(appconfig)
this.initMulter(appconfig)
return this
},
/**
@@ -41,60 +39,56 @@ module.exports = {
* @return {boolean} Void
*/
initMulter (appconfig) {
let maxFileSizes = {
img: appconfig.uploads.maxImageFileSize * 1024 * 1024,
file: appconfig.uploads.maxOtherFileSize * 1024 * 1024
};
}
// -> IMAGES
this.uploadImgHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'));
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
}
}),
fileFilter: (req, f, cb) => {
// -> Check filesize
if (f.size > maxFileSizes.img) {
return cb(null, false);
return cb(null, false)
}
// -> Check MIME type (quick check only)
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
return cb(null, false);
return cb(null, false)
}
cb(null, true);
cb(null, true)
}
}).array('imgfile', 20);
}).array('imgfile', 20)
// -> FILES
this.uploadFileHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'));
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
}
}),
fileFilter: (req, f, cb) => {
// -> Check filesize
if (f.size > maxFileSizes.file) {
return cb(null, false);
return cb(null, false)
}
cb(null, true);
cb(null, true)
}
}).array('binfile', 20);
return true;
}).array('binfile', 20)
return true
},
/**
@@ -104,34 +98,31 @@ module.exports = {
* @return {Void} Void
*/
createBaseDirectories (appconfig) {
winston.info('[SERVER] Checking data directories...');
winston.info('[SERVER] Checking data directories...')
try {
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'))
if (os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'), '644');
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'), '644')
}
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'))
if (os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.repo, './upload'), '644');
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.repo, './upload'), '644')
}
} catch (err) {
winston.error(err);
winston.error(err)
}
winston.info('[SERVER] Data and Repository directories are OK.');
return;
winston.info('[SERVER] Data and Repository directories are OK.')
return
},
/**
@@ -140,7 +131,7 @@ module.exports = {
* @return {String} The uploads path.
*/
getUploadsPath () {
return this._uploadsPath;
return this._uploadsPath
},
/**
@@ -149,7 +140,7 @@ module.exports = {
* @return {String} The thumbs path.
*/
getThumbsPath () {
return this._uploadsThumbsPath;
return this._uploadsThumbsPath
},
/**
@@ -161,27 +152,25 @@ module.exports = {
* @return {Promise<String>} Promise of the accepted filename
*/
validateUploadsFilename (f, fld, isImage) {
let fObj = path.parse(f);
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, '');
let fext = _.toLower(fObj.ext);
let fObj = path.parse(f)
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9-]+/g, '')
let fext = _.toLower(fObj.ext)
if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
fext = '.png';
fext = '.png'
}
f = fname + fext;
let fpath = path.resolve(this._uploadsPath, fld, f);
f = fname + fext
let fpath = path.resolve(this._uploadsPath, fld, f)
return fs.statAsync(fpath).then((s) => {
throw new Error('File ' + f + ' already exists.');
throw new Error('File ' + f + ' already exists.')
}).catch((err) => {
if (err.code === 'ENOENT') {
return f;
return f
}
throw err
})
}
throw err;
});
},
};
}
+103 -109
View File
@@ -1,19 +1,18 @@
"use strict";
'use strict'
var Promise = require('bluebird'),
md = require('markdown-it'),
mdEmoji = require('markdown-it-emoji'),
mdTaskLists = require('markdown-it-task-lists'),
mdAbbr = require('markdown-it-abbr'),
mdAnchor = require('markdown-it-anchor'),
mdFootnote = require('markdown-it-footnote'),
mdExternalLinks = require('markdown-it-external-links'),
mdExpandTabs = require('markdown-it-expand-tabs'),
mdAttrs = require('markdown-it-attrs'),
hljs = require('highlight.js'),
cheerio = require('cheerio'),
_ = require('lodash'),
mdRemove = require('remove-markdown');
const md = require('markdown-it')
const mdEmoji = require('markdown-it-emoji')
const mdTaskLists = require('markdown-it-task-lists')
const mdAbbr = require('markdown-it-abbr')
const mdAnchor = require('markdown-it-anchor')
const mdFootnote = require('markdown-it-footnote')
const mdExternalLinks = require('markdown-it-external-links')
const mdExpandTabs = require('markdown-it-expand-tabs')
const mdAttrs = require('markdown-it-attrs')
const hljs = require('highlight.js')
const cheerio = require('cheerio')
const _ = require('lodash')
const mdRemove = require('remove-markdown')
// Load plugins
@@ -24,12 +23,12 @@ var mkdown = md({
highlight (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return '<pre class="hljs"><code>' + hljs.highlight(lang, str, true).value + '</code></pre>';
return '<pre class="hljs"><code>' + hljs.highlight(lang, str, true).value + '</code></pre>'
} catch (err) {
return '<pre><code>' + str + '</code></pre>';
return '<pre><code>' + str + '</code></pre>'
}
}
return '<pre><code>' + str + '</code></pre>';
return '<pre><code>' + str + '</code></pre>'
}
})
.use(mdEmoji)
@@ -50,30 +49,30 @@ var mkdown = md({
.use(mdExpandTabs, {
tabWidth: 4
})
.use(mdAttrs);
.use(mdAttrs)
// Rendering rules
mkdown.renderer.rules.emoji = function (token, idx) {
return '<i class="twa twa-' + _.replace(token[idx].markup, /_/g, '-') + '"></i>';
};
return '<i class="twa twa-' + _.replace(token[idx].markup, /_/g, '-') + '"></i>'
}
// Video rules
const videoRules = [
{
selector: 'a.youtube',
regexp: new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
regexp: new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/, 'i'),
output: '<iframe width="640" height="360" src="https://www.youtube.com/embed/{0}?rel=0" frameborder="0" allowfullscreen></iframe>'
},
{
selector: 'a.vimeo',
regexp: new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
regexp: new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
output: '<iframe src="https://player.vimeo.com/video/{0}" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
},
{
selector: 'a.dailymotion',
regexp: new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i'),
regexp: new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i'),
output: '<iframe width="640" height="360" src="//www.dailymotion.com/embed/video/{0}?endscreen-enable=false" frameborder="0" allowfullscreen></iframe>'
},
{
@@ -90,81 +89,79 @@ const videoRules = [
* @return {Array} TOC tree
*/
const parseTree = (content) => {
let tokens = md().parse(content, {});
let tocArray = [];
let tokens = md().parse(content, {})
let tocArray = []
// -> Extract headings and their respective levels
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== "heading_close") {
continue;
if (tokens[i].type !== 'heading_close') {
continue
}
const heading = tokens[i - 1];
const heading_close = tokens[i];
const heading = tokens[i - 1]
const headingclose = tokens[i]
if (heading.type === "inline") {
let content = "";
let anchor = "";
if (heading.children && heading.children[0].type === "link_open") {
content = heading.children[1].content;
anchor = _.kebabCase(content);
if (heading.type === 'inline') {
let content = ''
let anchor = ''
if (heading.children && heading.children[0].type === 'link_open') {
content = heading.children[1].content
anchor = _.kebabCase(content)
} else {
content = heading.content;
anchor = _.kebabCase(heading.children.reduce((acc, t) => acc + t.content, ""));
content = heading.content
anchor = _.kebabCase(heading.children.reduce((acc, t) => acc + t.content, ''))
}
tocArray.push({
content,
anchor,
level: +heading_close.tag.substr(1, 1)
});
level: +headingclose.tag.substr(1, 1)
})
}
}
// -> Exclude levels deeper than 2
_.remove(tocArray, (n) => { return n.level > 2; });
_.remove(tocArray, (n) => { return n.level > 2 })
// -> Build tree from flat array
return _.reduce(tocArray, (tree, v) => {
let treeLength = tree.length - 1;
let treeLength = tree.length - 1
if (v.level < 2) {
tree.push({
content: v.content,
anchor: v.anchor,
nodes: []
});
})
} else {
let lastNodeLevel = 1;
let lastNodeLevel = 1
let GetNodePath = (startPos) => {
lastNodeLevel++;
lastNodeLevel++
if (_.isEmpty(startPos)) {
startPos = 'nodes';
startPos = 'nodes'
}
if (lastNodeLevel === v.level) {
return startPos;
return startPos
} else {
return GetNodePath(startPos + '[' + (_.at(tree[treeLength], startPos).length - 1) + '].nodes');
return GetNodePath(startPos + '[' + (_.at(tree[treeLength], startPos).length - 1) + '].nodes')
}
};
let lastNodePath = GetNodePath();
let lastNode = _.get(tree[treeLength], lastNodePath);
}
let lastNodePath = GetNodePath()
let lastNode = _.get(tree[treeLength], lastNodePath)
if (lastNode) {
lastNode.push({
content: v.content,
anchor: v.anchor,
nodes: []
});
_.set(tree[treeLength], lastNodePath, lastNode);
})
_.set(tree[treeLength], lastNodePath, lastNode)
}
}
return tree;
}, []);
};
return tree
}, [])
}
/**
* Parse markdown content to HTML
@@ -173,86 +170,84 @@ const parseTree = (content) => {
* @return {String} HTML formatted content
*/
const parseContent = (content) => {
let output = mkdown.render(content);
let cr = cheerio.load(output);
let output = mkdown.render(content)
let cr = cheerio.load(output)
// -> Check for empty first element
let firstElm = cr.root().children().first()[0];
let firstElm = cr.root().children().first()[0]
if (firstElm.type === 'tag' && firstElm.name === 'p') {
let firstElmChildren = firstElm.children;
let firstElmChildren = firstElm.children
if (firstElmChildren.length < 1) {
firstElm.remove();
firstElm.remove()
} else if (firstElmChildren.length === 1 && firstElmChildren[0].type === 'tag' && firstElmChildren[0].name === 'img') {
cr(firstElm).addClass('is-gapless');
cr(firstElm).addClass('is-gapless')
}
}
// -> Remove links in headers
cr('h1 > a:not(.toc-anchor), h2 > a:not(.toc-anchor), h3 > a:not(.toc-anchor)').each((i, elm) => {
let txtLink = cr(elm).text();
cr(elm).replaceWith(txtLink);
});
let txtLink = cr(elm).text()
cr(elm).replaceWith(txtLink)
})
// -> Re-attach blockquote styling classes to their parents
cr.root().children('blockquote').each((i, elm) => {
if (cr(elm).children().length > 0) {
let bqLastChild = cr(elm).children().last()[0];
let bqLastChildClasses = cr(bqLastChild).attr('class');
let bqLastChild = cr(elm).children().last()[0]
let bqLastChildClasses = cr(bqLastChild).attr('class')
if (bqLastChildClasses && bqLastChildClasses.length > 0) {
cr(bqLastChild).removeAttr('class');
cr(elm).addClass(bqLastChildClasses);
cr(bqLastChild).removeAttr('class')
cr(elm).addClass(bqLastChildClasses)
}
}
});
})
// -> Enclose content below headers
cr('h2').each((i, elm) => {
let subH2Content = cr(elm).nextUntil('h1, h2');
cr(elm).after('<div class="indent-h2"></div>');
let subH2Container = cr(elm).next('.indent-h2');
let subH2Content = cr(elm).nextUntil('h1, h2')
cr(elm).after('<div class="indent-h2"></div>')
let subH2Container = cr(elm).next('.indent-h2')
_.forEach(subH2Content, (ch) => {
cr(subH2Container).append(ch);
});
});
cr(subH2Container).append(ch)
})
})
cr('h3').each((i, elm) => {
let subH3Content = cr(elm).nextUntil('h1, h2, h3');
cr(elm).after('<div class="indent-h3"></div>');
let subH3Container = cr(elm).next('.indent-h3');
let subH3Content = cr(elm).nextUntil('h1, h2, h3')
cr(elm).after('<div class="indent-h3"></div>')
let subH3Container = cr(elm).next('.indent-h3')
_.forEach(subH3Content, (ch) => {
cr(subH3Container).append(ch);
});
});
cr(subH3Container).append(ch)
})
})
// Replace video links with embeds
_.forEach(videoRules, (vrule) => {
cr(vrule.selector).each((i, elm) => {
let originLink = cr(elm).attr('href');
let originLink = cr(elm).attr('href')
if (vrule.regexp) {
let vidMatches = originLink.match(vrule.regexp);
let vidMatches = originLink.match(vrule.regexp)
if ((vidMatches && _.isArray(vidMatches))) {
vidMatches = _.filter(vidMatches, (f) => {
return f && _.isString(f);
});
originLink = _.last(vidMatches);
return f && _.isString(f)
})
originLink = _.last(vidMatches)
}
}
let processedLink = _.replace(vrule.output, '{0}', originLink);
cr(elm).replaceWith(processedLink);
});
});
let processedLink = _.replace(vrule.output, '{0}', originLink)
cr(elm).replaceWith(processedLink)
})
})
output = cr.html();
output = cr.html()
return output;
};
return output
}
/**
* Parse meta-data tags from content
@@ -261,16 +256,15 @@ const parseContent = (content) => {
* @return {Object} Properties found in the content and their values
*/
const parseMeta = (content) => {
let commentMeta = new RegExp('<!-- ?([a-zA-Z]+):(.*)-->','g');
let results = {}, match;
while(match = commentMeta.exec(content)) {
results[_.toLower(match[1])] = _.trim(match[2]);
let commentMeta = new RegExp('<!-- ?([a-zA-Z]+):(.*)-->', 'g')
let results = {}
let match
while ((match = commentMeta.exec(content)) !== null) {
results[_.toLower(match[1])] = _.trim(match[2])
}
return results;
};
return results
}
module.exports = {
@@ -285,7 +279,7 @@ module.exports = {
meta: parseMeta(content),
html: parseContent(content),
tree: parseTree(content)
};
}
},
parseContent,
@@ -312,7 +306,7 @@ module.exports = {
.replace(/(\b(\w{1,2})\b(\W|$))/g, '')
.replace(/\s\s+/g, ' ')
.value()
);
)
}
};
}
+72 -109
View File
@@ -1,16 +1,15 @@
"use strict";
'use strict'
var path = require('path'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
readChunk = require('read-chunk'),
fileType = require('file-type'),
mime = require('mime-types'),
farmhash = require('farmhash'),
moment = require('moment'),
chokidar = require('chokidar'),
sharp = require('sharp'),
_ = require('lodash');
const path = require('path')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const readChunk = require('read-chunk')
const fileType = require('file-type')
const mime = require('mime-types')
const farmhash = require('farmhash')
const chokidar = require('chokidar')
const sharp = require('sharp')
const _ = require('lodash')
/**
* Uploads - Agent
@@ -28,17 +27,15 @@ module.exports = {
* @return {Object} Uploads model instance
*/
init () {
let self = this
let self = this;
self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
self._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
self._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
// Disable Sharp cache, as it cause file locks issues when deleting uploads.
sharp.cache(false);
return self;
sharp.cache(false)
return self
},
/**
@@ -47,8 +44,7 @@ module.exports = {
* @return {Void} Void
*/
watch () {
let self = this;
let self = this
self._watcher = chokidar.watch(self._uploadsPath, {
persistent: true,
@@ -56,30 +52,24 @@ module.exports = {
cwd: self._uploadsPath,
depth: 1,
awaitWriteFinish: true
});
})
// -> Add new upload file
self._watcher.on('add', (p) => {
let pInfo = self.parseUploadsRelPath(p);
let pInfo = self.parseUploadsRelPath(p)
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true });
return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
}).then(() => {
return git.commitUploads('Uploaded ' + p);
});
});
return git.commitUploads('Uploaded ' + p)
})
})
// -> Remove upload file
self._watcher.on('unlink', (p) => {
let pInfo = self.parseUploadsRelPath(p);
return git.commitUploads('Deleted/Renamed ' + p);
});
return git.commitUploads('Deleted/Renamed ' + p)
})
},
/**
@@ -88,19 +78,16 @@ module.exports = {
* @return {Promise<Void>} Promise of the scan operation
*/
initialScan () {
let self = this;
let self = this
return fs.readdirAsync(self._uploadsPath).then((ls) => {
// Get all folders
return Promise.map(ls, (f) => {
return fs.statAsync(path.join(self._uploadsPath, f)).then((s) => { return { filename: f, stat: s }; });
}).filter((s) => { return s.stat.isDirectory(); }).then((arrDirs) => {
let folderNames = _.map(arrDirs, 'filename');
folderNames.unshift('');
return fs.statAsync(path.join(self._uploadsPath, f)).then((s) => { return { filename: f, stat: s } })
}).filter((s) => { return s.stat.isDirectory() }).then((arrDirs) => {
let folderNames = _.map(arrDirs, 'filename')
folderNames.unshift('')
// Add folders to DB
@@ -109,53 +96,43 @@ module.exports = {
return {
_id: 'f:' + f,
name: f
};
}));
}
}))
}).then(() => {
// Travel each directory and scan files
let allFiles = [];
let allFiles = []
return Promise.map(folderNames, (fldName) => {
let fldPath = path.join(self._uploadsPath, fldName);
let fldPath = path.join(self._uploadsPath, fldName)
return fs.readdirAsync(fldPath).then((fList) => {
return Promise.map(fList, (f) => {
return upl.processFile(fldName, f).then((mData) => {
if (mData) {
allFiles.push(mData);
allFiles.push(mData)
}
return true;
});
}, {concurrency: 3});
});
return true
})
}, {concurrency: 3})
})
}, {concurrency: 1}).finally(() => {
// Add files to DB
return db.UplFile.remove({}).then(() => {
if (_.isArray(allFiles) && allFiles.length > 0) {
return db.UplFile.insertMany(allFiles);
return db.UplFile.insertMany(allFiles)
} else {
return true;
return true
}
});
});
});
});
})
})
})
})
}).then(() => {
// Watch for new changes
return upl.watch();
});
return upl.watch()
})
},
/**
@@ -165,13 +142,11 @@ module.exports = {
* @return {Object} Parsed path (folder and filename)
*/
parseUploadsRelPath (f) {
let fObj = path.parse(f);
let fObj = path.parse(f)
return {
folder: fObj.dir,
filename: fObj.base
};
}
},
/**
@@ -182,25 +157,23 @@ module.exports = {
* @return {Promise<Object>} Promise of the file metadata
*/
processFile (fldName, f) {
let self = this
let self = this;
let fldPath = path.join(self._uploadsPath, fldName);
let fPath = path.join(fldPath, f);
let fPathObj = path.parse(fPath);
let fUid = farmhash.fingerprint32(fldName + '/' + f);
let fldPath = path.join(self._uploadsPath, fldName)
let fPath = path.join(fldPath, f)
let fPathObj = path.parse(fPath)
let fUid = farmhash.fingerprint32(fldName + '/' + f)
return fs.statAsync(fPath).then((s) => {
if(!s.isFile()) { return false; }
if (!s.isFile()) { return false }
// Get MIME info
let mimeInfo = fileType(readChunk.sync(fPath, 0, 262));
let mimeInfo = fileType(readChunk.sync(fPath, 0, 262))
if (_.isNil(mimeInfo)) {
mimeInfo = {
mime: mime.lookup(fPathObj.ext) || 'application/octet-stream'
};
}
}
// Images
@@ -208,9 +181,8 @@ module.exports = {
if (s.size < 3145728) { // ignore files larger than 3MB
if (_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return self.getImageMetadata(fPath).then((mImgData) => {
let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png'));
let cacheThumbnailPathStr = path.format(cacheThumbnailPath);
let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png'))
let cacheThumbnailPathStr = path.format(cacheThumbnailPath)
let mData = {
_id: fUid,
@@ -221,23 +193,20 @@ module.exports = {
filename: f,
basename: fPathObj.name,
filesize: s.size
};
}
// Generate thumbnail
return fs.statAsync(cacheThumbnailPathStr).then((st) => {
return st.isFile();
}).catch((err) => {
return false;
return st.isFile()
}).catch((err) => { // eslint-disable-line handle-callback-err
return false
}).then((thumbExists) => {
return (thumbExists) ? mData : fs.ensureDirAsync(cacheThumbnailPath.dir).then(() => {
return self.generateThumbnail(fPath, cacheThumbnailPathStr);
}).return(mData);
});
});
return self.generateThumbnail(fPath, cacheThumbnailPathStr)
}).return(mData)
})
})
}
}
@@ -251,10 +220,8 @@ module.exports = {
filename: f,
basename: fPathObj.name,
filesize: s.size
};
});
}
})
},
/**
@@ -265,7 +232,6 @@ module.exports = {
* @return {Promise<Object>} Promise returning the resized image info
*/
generateThumbnail (sourcePath, destPath) {
return sharp(sourcePath)
.withoutEnlargement()
.resize(150, 150)
@@ -273,8 +239,7 @@ module.exports = {
.embed()
.flatten()
.toFormat('png')
.toFile(destPath);
.toFile(destPath)
},
/**
@@ -284,9 +249,7 @@ module.exports = {
* @return {Object} The image metadata.
*/
getImageMetadata (sourcePath) {
return sharp(sourcePath).metadata();
return sharp(sourcePath).metadata()
}
};
}
+82 -110
View File
@@ -1,16 +1,15 @@
"use strict";
'use strict'
const path = require('path'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
multer = require('multer'),
request = require('request'),
url = require('url'),
farmhash = require('farmhash'),
_ = require('lodash');
const path = require('path')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const request = require('request')
const url = require('url')
const farmhash = require('farmhash')
const _ = require('lodash')
var regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
const maxDownloadFileSize = 3145728; // 3 MB
var regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
const maxDownloadFileSize = 3145728 // 3 MB
/**
* Uploads
@@ -26,12 +25,10 @@ module.exports = {
* @return {Object} Uploads model instance
*/
init () {
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
return this;
return this
},
/**
@@ -40,7 +37,7 @@ module.exports = {
* @return {String} The thumbs path.
*/
getThumbsPath () {
return this._uploadsThumbsPath;
return this._uploadsThumbsPath
},
/**
@@ -50,8 +47,8 @@ module.exports = {
*/
getUploadsFolders () {
return db.UplFolder.find({}, 'name').sort('name').exec().then((results) => {
return (results) ? _.map(results, 'name') : [{ name: '' }];
});
return (results) ? _.map(results, 'name') : [{ name: '' }]
})
},
/**
@@ -61,13 +58,12 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
createUploadsFolder (folderName) {
let self = this
let self = this;
folderName = _.kebabCase(_.trim(folderName));
folderName = _.kebabCase(_.trim(folderName))
if (_.isEmpty(folderName) || !regFolderName.test(folderName)) {
return Promise.resolve(self.getUploadsFolders());
return Promise.resolve(self.getUploadsFolders())
}
return fs.ensureDirAsync(path.join(self._uploadsPath, folderName)).then(() => {
@@ -77,11 +73,10 @@ module.exports = {
name: folderName
}, {
upsert: true
});
})
}).then(() => {
return self.getUploadsFolders();
});
return self.getUploadsFolders()
})
},
/**
@@ -91,11 +86,9 @@ module.exports = {
* @return {Boolean} True if valid
*/
validateUploadsFolder (folderName) {
return db.UplFolder.findOne({ name: folderName }).then((f) => {
return (f) ? path.resolve(this._uploadsPath, folderName) : false;
});
return (f) ? path.resolve(this._uploadsPath, folderName) : false
})
},
/**
@@ -108,7 +101,7 @@ module.exports = {
if (_.isArray(arrFiles) || _.isPlainObject(arrFiles)) {
// this._uploadsDb.Files.insert(arrFiles);
}
return;
return
},
/**
@@ -119,12 +112,10 @@ module.exports = {
* @return {Array<Object>} The files matching the query
*/
getUploadsFiles (cat, fld) {
return db.UplFile.find({
category: cat,
folder: 'f:' + fld
}).sort('filename').exec();
}).sort('filename').exec()
},
/**
@@ -134,22 +125,20 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
deleteUploadsFile (uid) {
let self = this;
let self = this
return db.UplFile.findOneAndRemove({ _id: uid }).then((f) => {
if (f) {
return self.deleteUploadsFileTry(f, 0);
return self.deleteUploadsFileTry(f, 0)
}
return true;
});
return true
})
},
deleteUploadsFileTry (f, attempt) {
let self = this
let self = this;
let fFolder = (f.folder && f.folder !== 'f:') ? f.folder.slice(2) : './';
let fFolder = (f.folder && f.folder !== 'f:') ? f.folder.slice(2) : './'
return Promise.join(
fs.removeAsync(path.join(self._uploadsThumbsPath, f._id + '.png')),
@@ -157,14 +146,13 @@ module.exports = {
).catch((err) => {
if (err.code === 'EBUSY' && attempt < 5) {
return Promise.delay(100).then(() => {
return self.deleteUploadsFileTry(f, attempt + 1);
});
return self.deleteUploadsFileTry(f, attempt + 1)
})
} else {
winston.warn('Unable to delete uploads file ' + f.filename + '. File is locked by another process and multiple attempts failed.');
return true;
winston.warn('Unable to delete uploads file ' + f.filename + '. File is locked by another process and multiple attempts failed.')
return true
}
});
})
},
/**
@@ -175,62 +163,52 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
downloadFromUrl (fFolder, fUrl) {
let self = this;
let fUrlObj = url.parse(fUrl);
let fUrlFilename = _.last(_.split(fUrlObj.pathname, '/'));
let destFolder = _.chain(fFolder).trim().toLower().value();
let fUrlObj = url.parse(fUrl)
let fUrlFilename = _.last(_.split(fUrlObj.pathname, '/'))
let destFolder = _.chain(fFolder).trim().toLower().value()
return upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
return Promise.reject(new Error('Invalid Folder'));
return Promise.reject(new Error('Invalid Folder'))
}
return lcdata.validateUploadsFilename(fUrlFilename, destFolder).then((destFilename) => {
let destFilePath = path.resolve(destFolderPath, destFilename);
let destFilePath = path.resolve(destFolderPath, destFilename)
return new Promise((resolve, reject) => {
let rq = request({
url: fUrl,
method: 'GET',
followRedirect: true,
maxRedirects: 5,
timeout: 10000
});
})
let destFileStream = fs.createWriteStream(destFilePath);
let curFileSize = 0;
let destFileStream = fs.createWriteStream(destFilePath)
let curFileSize = 0
rq.on('data', (data) => {
curFileSize += data.length;
curFileSize += data.length
if (curFileSize > maxDownloadFileSize) {
rq.abort();
destFileStream.destroy();
fs.remove(destFilePath);
reject(new Error('Remote file is too large!'));
rq.abort()
destFileStream.destroy()
fs.remove(destFilePath)
reject(new Error('Remote file is too large!'))
}
}).on('error', (err) => {
destFileStream.destroy();
fs.remove(destFilePath);
reject(err);
});
destFileStream.destroy()
fs.remove(destFilePath)
reject(err)
})
destFileStream.on('finish', () => {
resolve(true);
});
rq.pipe(destFileStream);
});
});
});
resolve(true)
})
rq.pipe(destFileStream)
})
})
})
},
/**
@@ -242,49 +220,46 @@ module.exports = {
* @return {Promise} Promise of the operation
*/
moveUploadsFile (uid, fld, nFilename) {
let self = this;
let self = this
return db.UplFolder.findById('f:' + fld).then((folder) => {
if (folder) {
return db.UplFile.findById(uid).then((originFile) => {
// -> Check if rename is valid
let nameCheck = null;
let nameCheck = null
if (nFilename) {
let originFileObj = path.parse(originFile.filename);
nameCheck = lcdata.validateUploadsFilename(nFilename + originFileObj.ext, folder.name);
let originFileObj = path.parse(originFile.filename)
nameCheck = lcdata.validateUploadsFilename(nFilename + originFileObj.ext, folder.name)
} else {
nameCheck = Promise.resolve(originFile.filename);
nameCheck = Promise.resolve(originFile.filename)
}
return nameCheck.then((destFilename) => {
let originFolder = (originFile.folder && originFile.folder !== 'f:') ? originFile.folder.slice(2) : './';
let sourceFilePath = path.resolve(self._uploadsPath, originFolder, originFile.filename);
let destFilePath = path.resolve(self._uploadsPath, folder.name, destFilename);
let preMoveOps = [];
let originFolder = (originFile.folder && originFile.folder !== 'f:') ? originFile.folder.slice(2) : './'
let sourceFilePath = path.resolve(self._uploadsPath, originFolder, originFile.filename)
let destFilePath = path.resolve(self._uploadsPath, folder.name, destFilename)
let preMoveOps = []
// -> Check for invalid operations
if (sourceFilePath === destFilePath) {
return Promise.reject(new Error('Invalid Operation!'));
return Promise.reject(new Error('Invalid Operation!'))
}
// -> Delete DB entry
preMoveOps.push(db.UplFile.findByIdAndRemove(uid));
preMoveOps.push(db.UplFile.findByIdAndRemove(uid))
// -> Move thumbnail ahead to avoid re-generation
if (originFile.category === 'image') {
let fUid = farmhash.fingerprint32(folder.name + '/' + destFilename);
let sourceThumbPath = path.resolve(self._uploadsThumbsPath, originFile._id + '.png');
let destThumbPath = path.resolve(self._uploadsThumbsPath, fUid + '.png');
preMoveOps.push(fs.moveAsync(sourceThumbPath, destThumbPath));
let fUid = farmhash.fingerprint32(folder.name + '/' + destFilename)
let sourceThumbPath = path.resolve(self._uploadsThumbsPath, originFile._id + '.png')
let destThumbPath = path.resolve(self._uploadsThumbsPath, fUid + '.png')
preMoveOps.push(fs.moveAsync(sourceThumbPath, destThumbPath))
} else {
preMoveOps.push(Promise.resolve(true));
preMoveOps.push(Promise.resolve(true))
}
// -> Proceed to move actual file
@@ -292,17 +267,14 @@ module.exports = {
return Promise.all(preMoveOps).then(() => {
return fs.moveAsync(sourceFilePath, destFilePath, {
clobber: false
});
});
})
});
})
})
})
} else {
return Promise.reject(new Error('Invalid Destination Folder'));
return Promise.reject(new Error('Invalid Destination Folder'))
}
});
})
}
};
}
+10 -13
View File
@@ -1,7 +1,6 @@
"use strict";
'use strict'
var Promise = require('bluebird'),
moment = require('moment-timezone');
const moment = require('moment-timezone')
/**
* Authentication middleware
@@ -12,29 +11,27 @@ var Promise = require('bluebird'),
* @return {any} void
*/
module.exports = (req, res, next) => {
// Is user authenticated ?
if (!req.isAuthenticated()) {
return res.redirect('/login');
return res.redirect('/login')
}
// Check permissions
if (!rights.check(req, 'read')) {
return res.render('error-forbidden');
return res.render('error-forbidden')
}
// Set i18n locale
req.i18n.changeLanguage(req.user.lang);
res.locals.userMoment = moment;
res.locals.userMoment.locale(req.user.lang);
req.i18n.changeLanguage(req.user.lang)
res.locals.userMoment = moment
res.locals.userMoment.locale(req.user.lang)
// Expose user data
res.locals.user = req.user;
res.locals.user = req.user
return next();
};
return next()
}
+4 -6
View File
@@ -1,4 +1,4 @@
"use strict";
'use strict'
/**
* Flash middleware
@@ -9,9 +9,7 @@
* @return {any} void
*/
module.exports = (req, res, next) => {
res.locals.appflash = req.flash('alert')
res.locals.appflash = req.flash('alert');
next();
};
next()
}
+9 -9
View File
@@ -1,3 +1,5 @@
'use strict'
/**
* Security Middleware
*
@@ -7,22 +9,20 @@
* @return {any} void
*/
module.exports = function (req, res, next) {
// -> Disable X-Powered-By
app.disable('x-powered-by');
app.disable('x-powered-by')
// -> Disable Frame Embedding
res.set('X-Frame-Options', 'deny');
res.set('X-Frame-Options', 'deny')
// -> Re-enable XSS Fitler if disabled
res.set('X-XSS-Protection', '1; mode=block');
res.set('X-XSS-Protection', '1; mode=block')
// -> Disable MIME-sniffing
res.set('X-Content-Type-Options', 'nosniff');
res.set('X-Content-Type-Options', 'nosniff')
// -> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge');
res.set('X-UA-Compatible', 'IE=edge')
return next();
};
return next()
}
+3 -3
View File
@@ -1,4 +1,4 @@
"use strict";
'use strict'
/**
* BruteForce schema
@@ -13,6 +13,6 @@ var bruteForceSchema = Mongoose.Schema({
firstRequest: Date
},
expires: { type: Date, index: { expires: '1d' } }
});
})
module.exports = Mongoose.model('Bruteforce', bruteForceSchema);
module.exports = Mongoose.model('Bruteforce', bruteForceSchema)
+4 -7
View File
@@ -1,7 +1,4 @@
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Entry schema
@@ -33,7 +30,7 @@ var entrySchema = Mongoose.Schema({
},
{
timestamps: {}
});
})
entrySchema.index({
_id: 'text',
@@ -48,6 +45,6 @@ entrySchema.index({
content: 1
},
name: 'EntriesTextIndex'
});
})
module.exports = Mongoose.model('Entry', entrySchema);
module.exports = Mongoose.model('Entry', entrySchema)
+3 -9
View File
@@ -1,7 +1,4 @@
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Upload File schema
@@ -42,9 +39,6 @@ var uplFileSchema = Mongoose.Schema({
required: true
}
},
{
timestamps: {}
});
}, { timestamps: {} })
module.exports = Mongoose.model('UplFile', uplFileSchema);
module.exports = Mongoose.model('UplFile', uplFileSchema)
+3 -9
View File
@@ -1,7 +1,4 @@
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Upload Folder schema
@@ -17,9 +14,6 @@ var uplFolderSchema = Mongoose.Schema({
index: true
}
},
{
timestamps: {}
});
}, { timestamps: {} })
module.exports = Mongoose.model('UplFolder', uplFolderSchema);
module.exports = Mongoose.model('UplFolder', uplFolderSchema)
+19 -24
View File
@@ -1,8 +1,8 @@
"use strict";
'use strict'
const Promise = require('bluebird'),
bcrypt = require('bcryptjs-then'),
_ = require('lodash');
const Promise = require('bluebird')
const bcrypt = require('bcryptjs-then')
const _ = require('lodash')
/**
* Region schema
@@ -41,21 +41,17 @@ var userSchema = Mongoose.Schema({
deny: Boolean
}]
},
{
timestamps: {}
});
}, { timestamps: {} })
userSchema.statics.processProfile = (profile) => {
let primaryEmail = '';
let primaryEmail = ''
if (_.isArray(profile.emails)) {
let e = _.find(profile.emails, ['primary', true]);
primaryEmail = (e) ? e.value : _.first(profile.emails).value;
let e = _.find(profile.emails, ['primary', true])
primaryEmail = (e) ? e.value : _.first(profile.emails).value
} else if (_.isString(profile.email) && profile.email.length > 5) {
primaryEmail = profile.email;
primaryEmail = profile.email
} else {
return Promise.reject(new Error('Invalid User Email'));
return Promise.reject(new Error('Invalid User Email'))
}
return db.User.findOneAndUpdate({
@@ -70,19 +66,18 @@ userSchema.statics.processProfile = (profile) => {
new: true,
upsert: true
}).then((user) => {
return (user) ? user : Promise.reject(new Error('User Upsert failed.'));
});
};
return user || Promise.reject(new Error('User Upsert failed.'))
})
}
userSchema.statics.hashPassword = (rawPwd) => {
return bcrypt.hash(rawPwd);
};
return bcrypt.hash(rawPwd)
}
userSchema.methods.validatePassword = function (rawPwd) {
return bcrypt.compare(rawPwd, this.password).then((isValid) => {
return (isValid) ? true : Promise.reject(new Error('Invalid Login'));
});
};
return (isValid) ? true : Promise.reject(new Error('Invalid Login'))
})
}
module.exports = Mongoose.model('User', userSchema);
module.exports = Mongoose.model('User', userSchema)
+30
View File
@@ -129,5 +129,35 @@
"twemoji-awesome": "^1.0.4",
"vue": "^2.1.10"
},
"standard": {
"globals": [
"app",
"appconfig",
"appdata",
"bgAgent",
"db",
"entries",
"git",
"mark",
"lang",
"lcdata",
"rights",
"upl",
"winston",
"ws",
"Mongoose",
"CORE_PATH",
"ROOTPATH",
"IS_DEBUG",
"PROCNAME",
"WSInternalKey"
],
"ignore": [
"assets/**/*",
"data/**/*",
"node_modules/**/*",
"repo/**/*"
]
},
"snyk": true
}
+103 -101
View File
@@ -1,122 +1,124 @@
"use strict";
'use strict'
// ===========================================
// Wiki.js
// 1.0.0
// Licensed under AGPLv3
// ===========================================
global.PROCNAME = 'SERVER';
global.ROOTPATH = __dirname;
global.IS_DEBUG = process.env.NODE_ENV === 'development';
global.PROCNAME = 'SERVER'
global.ROOTPATH = __dirname
global.IS_DEBUG = process.env.NODE_ENV === 'development'
if (IS_DEBUG) {
global.CORE_PATH = ROOTPATH + '/../core/';
global.CORE_PATH = ROOTPATH + '/../core/'
} else {
global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/';
global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/'
}
process.env.VIPS_WARNING = false
// ----------------------------------------
// Load Winston
// ----------------------------------------
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
winston.info('[SERVER] Wiki.js is initializing...');
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG)
winston.info('[SERVER] Wiki.js is initializing...')
// ----------------------------------------
// Load global modules
// ----------------------------------------
let appconf = require(CORE_PATH + 'core-libs/config')();
global.appconfig = appconf.config;
global.appdata = appconf.data;
global.lcdata = require('./libs/local').init();
global.db = require(CORE_PATH + 'core-libs/mongodb').init();
global.entries = require('./libs/entries').init();
global.git = require('./libs/git').init(false);
global.lang = require('i18next');
global.mark = require('./libs/markdown');
global.upl = require('./libs/uploads').init();
let appconf = require(CORE_PATH + 'core-libs/config')()
global.appconfig = appconf.config
global.appdata = appconf.data
global.lcdata = require('./libs/local').init()
global.db = require(CORE_PATH + 'core-libs/mongodb').init()
global.entries = require('./libs/entries').init()
global.git = require('./libs/git').init(false)
global.lang = require('i18next')
global.mark = require('./libs/markdown')
global.upl = require('./libs/uploads').init()
// ----------------------------------------
// Load modules
// ----------------------------------------
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 passportSocketIo = require('passport.socketio');
const path = require('path');
const session = require('express-session');
const sessionMongoStore = require('connect-mongo')(session);
const socketio = require('socket.io');
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 i18nextBackend = require('i18next-node-fs-backend')
const i18nextMw = require('i18next-express-middleware')
const passport = require('passport')
const passportSocketIo = require('passport.socketio')
const path = require('path')
const session = require('express-session')
const SessionMongoStore = require('connect-mongo')(session)
const socketio = require('socket.io')
var mw = autoload(CORE_PATH + '/core-middlewares');
var ctrl = autoload(path.join(ROOTPATH, '/controllers'));
var libInternalAuth = require('./libs/internalAuth');
var mw = autoload(CORE_PATH + '/core-middlewares')
var ctrl = autoload(path.join(ROOTPATH, '/controllers'))
var libInternalAuth = require('./libs/internalAuth')
global.WSInternalKey = libInternalAuth.generateKey();
global.WSInternalKey = libInternalAuth.generateKey()
// ----------------------------------------
// Define Express App
// ----------------------------------------
global.app = express();
app.use(compression());
global.app = express()
app.use(compression())
// ----------------------------------------
// Security
// ----------------------------------------
app.use(mw.security);
app.use(mw.security)
// ----------------------------------------
// Public Assets
// ----------------------------------------
app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
app.use(express.static(path.join(ROOTPATH, 'assets')));
app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')))
app.use(express.static(path.join(ROOTPATH, 'assets')))
// ----------------------------------------
// Passport Authentication
// ----------------------------------------
var strategy = require(CORE_PATH + 'core-libs/auth')(passport);
global.rights = require(CORE_PATH + 'core-libs/rights');
rights.init();
require(CORE_PATH + 'core-libs/auth')(passport)
global.rights = require(CORE_PATH + 'core-libs/rights')
rights.init()
var sessionStore = new sessionMongoStore({
var sessionStore = new SessionMongoStore({
mongooseConnection: db.connection,
touchAfter: 15
});
})
app.use(cookieParser());
app.use(cookieParser())
app.use(session({
name: 'requarkswiki.sid',
store: sessionStore,
secret: appconfig.sessionSecret,
resave: false,
saveUninitialized: false
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
}))
app.use(flash())
app.use(passport.initialize())
app.use(passport.session())
// ----------------------------------------
// Localization Engine
// ----------------------------------------
lang
.use(i18next_backend)
.use(i18next_mw.LanguageDetector)
.use(i18nextBackend)
.use(i18nextMw.LanguageDetector)
.init({
load: 'languageOnly',
ns: ['common', 'auth'],
@@ -128,90 +130,90 @@ lang
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json'
}
});
})
// ----------------------------------------
// View Engine Setup
// ----------------------------------------
app.use(i18next_mw.handle(lang));
app.set('views', path.join(ROOTPATH, 'views'));
app.set('view engine', 'pug');
app.use(i18nextMw.handle(lang))
app.set('views', path.join(ROOTPATH, 'views'))
app.set('view engine', 'pug')
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
// ----------------------------------------
// View accessible data
// ----------------------------------------
app.locals._ = require('lodash');
app.locals.moment = require('moment');
app.locals.appconfig = appconfig;
app.use(mw.flash);
app.locals._ = require('lodash')
app.locals.moment = require('moment')
app.locals.appconfig = appconfig
app.use(mw.flash)
// ----------------------------------------
// Controllers
// ----------------------------------------
app.use('/', ctrl.auth);
app.use('/', ctrl.auth)
app.use('/uploads', mw.auth, ctrl.uploads);
app.use('/admin', mw.auth, ctrl.admin);
app.use('/', mw.auth, ctrl.pages);
app.use('/uploads', mw.auth, ctrl.uploads)
app.use('/admin', mw.auth, ctrl.admin)
app.use('/', mw.auth, ctrl.pages)
// ----------------------------------------
// Error handling
// ----------------------------------------
app.use(function (req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
var err = new Error('Not Found')
err.status = 404
next(err)
})
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.status(err.status || 500)
res.render('error', {
message: err.message,
error: IS_DEBUG ? err : {}
});
});
})
})
// ----------------------------------------
// Start HTTP server
// ----------------------------------------
winston.info('[SERVER] Starting HTTP/WS 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);
app.set('port', appconfig.port)
var server = http.createServer(app)
var io = socketio(server)
server.listen(appconfig.port);
server.listen(appconfig.port)
server.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error;
throw error
}
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error('Listening on port ' + appconfig.port + ' requires elevated privileges!');
process.exit(1);
break;
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;
console.error('Port ' + appconfig.port + ' is already in use!')
process.exit(1)
break
default:
throw error;
throw error
}
});
})
server.on('listening', () => {
winston.info('[SERVER] HTTP/WS server started successfully! [RUNNING]');
});
winston.info('[SERVER] HTTP/WS server started successfully! [RUNNING]')
})
// ----------------------------------------
// WebSocket
@@ -224,21 +226,21 @@ io.use(passportSocketIo.authorize({
passport,
cookieParser,
success: (data, accept) => {
accept();
accept()
},
fail: (data, message, error, accept) => {
return accept(new Error(message));
return accept(new Error(message))
}
}));
}))
io.on('connection', ctrl.ws);
io.on('connection', ctrl.ws)
// ----------------------------------------
// Start child processes
// ----------------------------------------
global.bgAgent = fork('agent.js');
global.bgAgent = fork('agent.js')
process.on('exit', (code) => {
bgAgent.disconnect();
});
bgAgent.disconnect()
})
+3
View File
@@ -0,0 +1,3 @@
'use strict'
// TODO
-11
View File
@@ -1,11 +0,0 @@
"use strict";
let path = require('path'),
fs = require('fs');
// ========================================
// Load global modules
// ========================================
global._ = require('lodash');
global.winston = require('winston');
+5 -10
View File
@@ -19,20 +19,15 @@ html
// CSS
link(type='text/css', rel='stylesheet', href='/css/libs.css')
link(type='text/css', rel='stylesheet', href='/css/app.css')
link(type='text/css', rel='stylesheet', href='/css/error.css')
body(class='server-error')
section.hero.is-warning.is-fullheight
.hero-body
body(class='is-error')
.container
a(href='/'): img(src='/favicons/android-icon-96x96.png')
h1.title(style={ 'margin-top': '30px'})= message
h2.subtitle(style={ 'margin-bottom': '50px'}) Oops, something went wrong
a.button.is-warning.is-inverted(href='/') Go Home
h1= message
h2 Oops, something went wrong
a.button.is-amber.is-inverted.is-featured(href='/') Go Home
if error.stack
section.section
.container.is-fluid
.content
h3 Detailed debug trail:
pre: code #{error.stack}
+5
View File
@@ -53,6 +53,11 @@ block content
a(href='/admin')
i.icon-head
span Account
else
li
a(href='/login')
i.icon-unlock
span Login
aside.stickyscroll(data-margin-top=40)
.sidebar-label
i.icon-th-list