Commit 414dc386 authored by NGPixel's avatar NGPixel

Standard JS code conversion + fixes

parent a508b2a7
# 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
\ No newline at end of file
...@@ -4,207 +4,188 @@ ...@@ -4,207 +4,188 @@
// Licensed under AGPLv3 // Licensed under AGPLv3
// =========================================== // ===========================================
global.PROCNAME = 'AGENT'; global.PROCNAME = 'AGENT'
global.ROOTPATH = __dirname; global.ROOTPATH = __dirname
global.IS_DEBUG = process.env.NODE_ENV === 'development'; global.IS_DEBUG = process.env.NODE_ENV === 'development'
if(IS_DEBUG) { if (IS_DEBUG) {
global.CORE_PATH = ROOTPATH + '/../core/'; global.CORE_PATH = ROOTPATH + '/../core/'
} else { } else {
global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/'; global.CORE_PATH = ROOTPATH + '/node_modules/requarks-core/'
} }
// ---------------------------------------- // ----------------------------------------
// Load Winston // 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 // 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')(); let appconf = require(CORE_PATH + 'core-libs/config')()
global.appconfig = appconf.config; global.appconfig = appconf.config
global.appdata = appconf.data; global.appdata = appconf.data
global.db = require(CORE_PATH + 'core-libs/mongodb').init(); global.db = require(CORE_PATH + 'core-libs/mongodb').init()
global.upl = require('./libs/uploads-agent').init(); global.upl = require('./libs/uploads-agent').init()
global.git = require('./libs/git').init(); global.git = require('./libs/git').init()
global.entries = require('./libs/entries').init(); global.entries = require('./libs/entries').init()
global.mark = require('./libs/markdown'); global.mark = require('./libs/markdown')
// ---------------------------------------- // ----------------------------------------
// Load modules // Load modules
// ---------------------------------------- // ----------------------------------------
var _ = require('lodash'); var moment = require('moment')
var moment = require('moment'); var Promise = require('bluebird')
var Promise = require('bluebird'); var fs = Promise.promisifyAll(require('fs-extra'))
var fs = Promise.promisifyAll(require("fs-extra")); var klaw = require('klaw')
var klaw = require('klaw'); var path = require('path')
var path = require('path'); var Cron = require('cron').CronJob
var cron = require('cron').CronJob;
// ---------------------------------------- // ----------------------------------------
// Start Cron // Start Cron
// ---------------------------------------- // ----------------------------------------
var jobIsBusy = false; var jobIsBusy = false
var jobUplWatchStarted = false; var jobUplWatchStarted = false
var job = new cron({ var job = new Cron({
cronTime: '0 */5 * * * *', cronTime: '0 */5 * * * *',
onTick: () => { onTick: () => {
// Make sure we don't start two concurrent jobs // Make sure we don't start two concurrent jobs
if(jobIsBusy) { 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)'); winston.warn('[AGENT] Previous job has not completed gracefully or is still running! Skipping for now. (This is not normal, you should investigate)')
return; return
} }
winston.info('[AGENT] Running all jobs...'); winston.info('[AGENT] Running all jobs...')
jobIsBusy = true; jobIsBusy = true
// Prepare async job collector // Prepare async job collector
let jobs = []; let jobs = []
let repoPath = path.resolve(ROOTPATH, appconfig.paths.repo); let repoPath = path.resolve(ROOTPATH, appconfig.paths.repo)
let dataPath = path.resolve(ROOTPATH, appconfig.paths.data); let dataPath = path.resolve(ROOTPATH, appconfig.paths.data)
let uploadsPath = path.join(repoPath, 'uploads'); let uploadsTempPath = path.join(dataPath, 'temp-upload')
let uploadsTempPath = path.join(dataPath, 'temp-upload');
// ---------------------------------------- // ----------------------------------------
// REGULAR JOBS // REGULAR JOBS
// ---------------------------------------- // ----------------------------------------
//***************************************** //* ****************************************
//-> Sync with Git remote // -> Sync with Git remote
//***************************************** //* ****************************************
jobs.push(git.onReady.then(() => { jobs.push(git.onReady.then(() => {
return git.resync().then(() => { return git.resync().then(() => {
// -> Stream all documents
//-> Stream all documents let cacheJobs = []
let jobCbStreamDocsResolve = null
let cacheJobs = []; let jobCbStreamDocs = new Promise((resolve, reject) => {
let jobCbStreamDocs_resolve = null, jobCbStreamDocsResolve = resolve
jobCbStreamDocs = new Promise((resolve, reject) => { })
jobCbStreamDocs_resolve = resolve;
});
klaw(repoPath).on('data', function (item) { klaw(repoPath).on('data', function (item) {
if(path.extname(item.path) === '.md' && path.basename(item.path) !== 'README.md') { 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)); // -> Purge outdated cache
let cachePath = entries.getCachePath(entryPath);
//-> Purge outdated cache
cacheJobs.push( cacheJobs.push(
fs.statAsync(cachePath).then((st) => { 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) => { }).catch((err) => {
return (err.code !== 'EEXIST') ? err : 'new'; return (err.code !== 'EEXIST') ? err : 'new'
}).then((fileStatus) => { }).then((fileStatus) => {
// -> Delete expired cache file
//-> Delete expired cache file if (fileStatus === 'expired') {
return fs.unlinkAsync(cachePath).return(fileStatus)
if(fileStatus === 'expired') {
return fs.unlinkAsync(cachePath).return(fileStatus);
} }
return fileStatus; return fileStatus
}).then((fileStatus) => { }).then((fileStatus) => {
// -> Update cache and search index
//-> Update cache and search index if (fileStatus !== 'active') {
return entries.updateCache(entryPath)
if(fileStatus !== 'active') {
return entries.updateCache(entryPath);
} }
return true; return true
}) })
)
);
} }
}).on('end', () => { }).on('end', () => {
jobCbStreamDocs_resolve(Promise.all(cacheJobs)); jobCbStreamDocsResolve(Promise.all(cacheJobs))
}); })
return jobCbStreamDocs;
}); return jobCbStreamDocs
})); })
}))
//***************************************** //* ****************************************
//-> Clear failed temporary upload files // -> Clear failed temporary upload files
//***************************************** //* ****************************************
jobs.push( jobs.push(
fs.readdirAsync(uploadsTempPath).then((ls) => { fs.readdirAsync(uploadsTempPath).then((ls) => {
let fifteenAgo = moment().subtract(15, 'minutes')
let fifteenAgo = moment().subtract(15, 'minutes');
return Promise.map(ls, (f) => { return Promise.map(ls, (f) => {
return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s }; }); return fs.statAsync(path.join(uploadsTempPath, f)).then((s) => { return { filename: f, stat: s } })
}).filter((s) => { return s.stat.isFile(); }).then((arrFiles) => { }).filter((s) => { return s.stat.isFile() }).then((arrFiles) => {
return Promise.map(arrFiles, (f) => { return Promise.map(arrFiles, (f) => {
if (moment(f.stat.ctime).isBefore(fifteenAgo, 'minute')) {
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 { } else {
return true; return true
} }
});
});
}) })
); })
})
)
// ---------------------------------------- // ----------------------------------------
// Run // Run
// ---------------------------------------- // ----------------------------------------
Promise.all(jobs).then(() => { 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) { if (!jobUplWatchStarted) {
jobUplWatchStarted = true; jobUplWatchStarted = true
upl.initialScan().then(() => { upl.initialScan().then(() => {
job.start(); job.start()
}); })
} }
return true; return true
}).catch((err) => { }).catch((err) => {
winston.error('[AGENT] One or more jobs have failed: ', err); winston.error('[AGENT] One or more jobs have failed: ', err)
}).finally(() => { }).finally(() => {
jobIsBusy = false; jobIsBusy = false
}); })
}, },
start: false, start: false,
timeZone: 'UTC', timeZone: 'UTC',
runOnInit: true runOnInit: true
}); })
// ---------------------------------------- // ----------------------------------------
// Shutdown gracefully // Shutdown gracefully
// ---------------------------------------- // ----------------------------------------
process.on('disconnect', () => { process.on('disconnect', () => {
winston.warn('[AGENT] Lost connection to main server. Exiting...'); winston.warn('[AGENT] Lost connection to main server. Exiting...')
job.stop(); job.stop()
process.exit(); process.exit()
}); })
process.on('exit', () => { process.on('exit', () => {
job.stop(); job.stop()
}); })
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
"use strict"; 'use strict'
jQuery( document ).ready(function( $ ) {
jQuery(document).ready(function ($) {
// ==================================== // ====================================
// Scroll // Scroll
// ==================================== // ====================================
...@@ -9,51 +8,50 @@ jQuery( document ).ready(function( $ ) { ...@@ -9,51 +8,50 @@ jQuery( document ).ready(function( $ ) {
$('a').smoothScroll({ $('a').smoothScroll({
speed: 400, speed: 400,
offset: -70 offset: -70
}); })
var sticky = new Sticky('.stickyscroll'); var sticky = new Sticky('.stickyscroll')
// ==================================== // ====================================
// Notifications // Notifications
// ==================================== // ====================================
$(window).bind('beforeunload', () => { $(window).bind('beforeunload', () => {
$('#notifload').addClass('active'); $('#notifload').addClass('active')
}); })
$(document).ajaxSend(() => { $(document).ajaxSend(() => {
$('#notifload').addClass('active'); $('#notifload').addClass('active')
}).ajaxComplete(() => { }).ajaxComplete(() => {
$('#notifload').removeClass('active'); $('#notifload').removeClass('active')
}); })
var alerts = new Alerts(); var alerts = new Alerts()
if(alertsData) { if (alertsData) {
_.forEach(alertsData, (alertRow) => { _.forEach(alertsData, (alertRow) => {
alerts.push(alertRow); alerts.push(alertRow)
}); })
} }
// ==================================== // ====================================
// Establish WebSocket connection // Establish WebSocket connection
// ==================================== // ====================================
var socket = io(window.location.origin); var socket = io(window.location.origin)
//=include components/search.js // =include components/search.js
// ==================================== // ====================================
// Pages logic // Pages logic
// ==================================== // ====================================
//=include pages/view.js // =include pages/view.js
//=include pages/create.js // =include pages/create.js
//=include pages/edit.js // =include pages/edit.js
//=include pages/source.js // =include pages/source.js
//=include pages/admin.js // =include pages/admin.js
})
});
//=include helpers/form.js // =include helpers/form.js
//=include helpers/pages.js // =include helpers/pages.js
//=include components/alerts.js // =include components/alerts.js
\ No newline at end of file
"use strict"; 'use strict'
/** /**
* Alerts * Alerts
...@@ -10,9 +10,8 @@ class Alerts { ...@@ -10,9 +10,8 @@ class Alerts {
* *
* @class * @class
*/ */
constructor() { constructor () {
let self = this
let self = this;
self.mdl = new Vue({ self.mdl = new Vue({
el: '#alerts', el: '#alerts',
...@@ -21,13 +20,12 @@ class Alerts { ...@@ -21,13 +20,12 @@ class Alerts {
}, },
methods: { methods: {
acknowledge: (uid) => { acknowledge: (uid) => {
self.close(uid); self.close(uid)
} }
} }
}); })
self.uidNext = 1;
self.uidNext = 1
} }
/** /**
...@@ -36,9 +34,8 @@ class Alerts { ...@@ -36,9 +34,8 @@ class Alerts {
* @param {Object} options Alert properties * @param {Object} options Alert properties
* @return {null} Void * @return {null} Void
*/ */
push(options) { push (options) {
let self = this
let self = this;
let nAlert = _.defaults(options, { let nAlert = _.defaults(options, {
_uid: self.uidNext, _uid: self.uidNext,
...@@ -46,18 +43,17 @@ class Alerts { ...@@ -46,18 +43,17 @@ class Alerts {
message: '---', message: '---',
sticky: false, sticky: false,
title: '---' title: '---'
}); })
self.mdl.children.push(nAlert); self.mdl.children.push(nAlert)
if(!nAlert.sticky) { if (!nAlert.sticky) {
_.delay(() => { _.delay(() => {
self.close(nAlert._uid); self.close(nAlert._uid)
}, 5000); }, 5000)
} }
self.uidNext++; self.uidNext++
} }
/** /**
...@@ -66,13 +62,13 @@ class Alerts { ...@@ -66,13 +62,13 @@ class Alerts {
* @param {String} title The title * @param {String} title The title
* @param {String} message The message * @param {String} message The message
*/ */
pushError(title, message) { pushError (title, message) {
this.push({ this.push({
class: 'error', class: 'error',
message, message,
sticky: false, sticky: false,
title title
}); })
} }
/** /**
...@@ -81,13 +77,13 @@ class Alerts { ...@@ -81,13 +77,13 @@ class Alerts {
* @param {String} title The title * @param {String} title The title
* @param {String} message The message * @param {String} message The message
*/ */
pushSuccess(title, message) { pushSuccess (title, message) {
this.push({ this.push({
class: 'success', class: 'success',
message, message,
sticky: false, sticky: false,
title title
}); })
} }
/** /**
...@@ -95,21 +91,19 @@ class Alerts { ...@@ -95,21 +91,19 @@ class Alerts {
* *
* @param {Integer} uid The unique ID of the alert * @param {Integer} uid The unique ID of the alert
*/ */
close(uid) { close (uid) {
let self = this
let self = this;
let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid]); let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid])
let nAlert = _.nth(self.mdl.children, nAlertIdx); let nAlert = _.nth(self.mdl.children, nAlertIdx)
if(nAlertIdx >= 0 && nAlert) { if (nAlertIdx >= 0 && nAlert) {
nAlert.class += ' exit'; nAlert.class += ' exit'
Vue.set(self.mdl.children, nAlertIdx, nAlert); Vue.set(self.mdl.children, nAlertIdx, nAlert)
_.delay(() => { _.delay(() => {
self.mdl.children.splice(nAlertIdx, 1); self.mdl.children.splice(nAlertIdx, 1)
}, 500); }, 500)
} }
} }
} }
let modelist = ace.require("ace/ext/modelist"); let modelist = ace.require('ace/ext/modelist')
let codeEditor = null; let codeEditor = null
// ACE - Mode Loader // ACE - Mode Loader
let modelistLoaded = []; let modelistLoaded = []
let loadAceMode = (m) => { let loadAceMode = (m) => {
return $.ajax({ return $.ajax({
url: '/js/ace/mode-' + m + '.js', url: '/js/ace/mode-' + m + '.js',
dataType: "script", dataType: 'script',
cache: true, cache: true,
beforeSend: () => { beforeSend: () => {
if(_.includes(modelistLoaded, m)) { if (_.includes(modelistLoaded, m)) {
return false; return false
} }
}, },
success: () => { success: () => {
modelistLoaded.push(m); modelistLoaded.push(m)
} }
}); })
}; }
// Vue Code Block instance // Vue Code Block instance
...@@ -33,46 +33,42 @@ let vueCodeBlock = new Vue({ ...@@ -33,46 +33,42 @@ let vueCodeBlock = new Vue({
watch: { watch: {
modeSelected: (val, oldVal) => { modeSelected: (val, oldVal) => {
loadAceMode(val).done(() => { loadAceMode(val).done(() => {
ace.require("ace/mode/" + val); ace.require('ace/mode/' + val)
codeEditor.getSession().setMode("ace/mode/" + val); codeEditor.getSession().setMode('ace/mode/' + val)
}); })
} }
}, },
methods: { methods: {
open: (ev) => { open: (ev) => {
$('#modal-editor-codeblock').addClass('is-active')
$('#modal-editor-codeblock').addClass('is-active');
_.delay(() => { _.delay(() => {
codeEditor = ace.edit("codeblock-editor"); codeEditor = ace.edit('codeblock-editor')
codeEditor.setTheme("ace/theme/tomorrow_night"); codeEditor.setTheme('ace/theme/tomorrow_night')
codeEditor.getSession().setMode("ace/mode/" + vueCodeBlock.modeSelected); codeEditor.getSession().setMode('ace/mode/' + vueCodeBlock.modeSelected)
codeEditor.setOption('fontSize', '14px'); codeEditor.setOption('fontSize', '14px')
codeEditor.setOption('hScrollBarAlwaysVisible', false); codeEditor.setOption('hScrollBarAlwaysVisible', false)
codeEditor.setOption('wrap', true); codeEditor.setOption('wrap', true)
codeEditor.setValue(vueCodeBlock.initContent);
codeEditor.focus(); codeEditor.setValue(vueCodeBlock.initContent)
codeEditor.renderer.updateFull();
}, 300);
codeEditor.focus()
codeEditor.renderer.updateFull()
}, 300)
}, },
cancel: (ev) => { cancel: (ev) => {
mdeModalOpenState = false; mdeModalOpenState = false
$('#modal-editor-codeblock').removeClass('is-active'); $('#modal-editor-codeblock').removeClass('is-active')
vueCodeBlock.initContent = ''; vueCodeBlock.initContent = ''
}, },
insertCode: (ev) => { insertCode: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
if(mde.codemirror.doc.somethingSelected()) { mde.codemirror.execCommand('singleSelection')
mde.codemirror.execCommand('singleSelection');
} }
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'; let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
mde.codemirror.doc.replaceSelection(codeBlockText);
vueCodeBlock.cancel();
mde.codemirror.doc.replaceSelection(codeBlockText)
vueCodeBlock.cancel()
} }
} }
}); })
\ No newline at end of file
...@@ -3,7 +3,7 @@ const videoRules = { ...@@ -3,7 +3,7 @@ const videoRules = {
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'), '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'), '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') '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 // Vue Video instance
...@@ -14,36 +14,34 @@ let vueVideo = new Vue({ ...@@ -14,36 +14,34 @@ let vueVideo = new Vue({
}, },
methods: { methods: {
open: (ev) => { open: (ev) => {
$('#modal-editor-video').addClass('is-active'); $('#modal-editor-video').addClass('is-active')
$('#modal-editor-video input').focus(); $('#modal-editor-video input').focus()
}, },
cancel: (ev) => { cancel: (ev) => {
mdeModalOpenState = false; mdeModalOpenState = false
$('#modal-editor-video').removeClass('is-active'); $('#modal-editor-video').removeClass('is-active')
vueVideo.link = ''; vueVideo.link = ''
}, },
insertVideo: (ev) => { insertVideo: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
if(mde.codemirror.doc.somethingSelected()) { mde.codemirror.execCommand('singleSelection')
mde.codemirror.execCommand('singleSelection');
} }
// Guess video type // Guess video type
let videoType = _.findKey(videoRules, (vr) => { let videoType = _.findKey(videoRules, (vr) => {
return vr.test(vueVideo.link); return vr.test(vueVideo.link)
}); })
if(_.isNil(videoType)) { if (_.isNil(videoType)) {
videoType = 'video'; videoType = 'video'
} }
// Insert video tag // Insert video tag
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'; let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
mde.codemirror.doc.replaceSelection(videoText);
vueVideo.cancel();
mde.codemirror.doc.replaceSelection(videoText)
vueVideo.cancel()
} }
} }
}); })
\ No newline at end of file
...@@ -3,183 +3,179 @@ ...@@ -3,183 +3,179 @@
// Markdown Editor // Markdown Editor
// ==================================== // ====================================
if($('#mk-editor').length === 1) { if ($('#mk-editor').length === 1) {
let mdeModalOpenState = false
let mdeModalOpenState = false; let mdeCurrentEditor = null
let mdeCurrentEditor = null;
Vue.filter('filesize', (v) => { Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v)); return _.toUpper(filesize(v))
}); })
//=include editor-image.js // =include editor-image.js
//=include editor-file.js // =include editor-file.js
//=include editor-video.js // =include editor-video.js
//=include editor-codeblock.js // =include editor-codeblock.js
var mde = new SimpleMDE({ var mde = new SimpleMDE({
autofocus: true, autofocus: true,
autoDownloadFontAwesome: false, autoDownloadFontAwesome: false,
element: $("#mk-editor").get(0), element: $('#mk-editor').get(0),
placeholder: 'Enter Markdown formatted content here...', placeholder: 'Enter Markdown formatted content here...',
spellChecker: false, spellChecker: false,
status: false, status: false,
toolbar: [{ toolbar: [{
name: "bold", name: 'bold',
action: SimpleMDE.toggleBold, action: SimpleMDE.toggleBold,
className: "icon-bold", className: 'icon-bold',
title: "Bold", title: 'Bold'
}, },
{ {
name: "italic", name: 'italic',
action: SimpleMDE.toggleItalic, action: SimpleMDE.toggleItalic,
className: "icon-italic", className: 'icon-italic',
title: "Italic", title: 'Italic'
}, },
{ {
name: "strikethrough", name: 'strikethrough',
action: SimpleMDE.toggleStrikethrough, action: SimpleMDE.toggleStrikethrough,
className: "icon-strikethrough", className: 'icon-strikethrough',
title: "Strikethrough", title: 'Strikethrough'
}, },
'|', '|',
{ {
name: "heading-1", name: 'heading-1',
action: SimpleMDE.toggleHeading1, action: SimpleMDE.toggleHeading1,
className: "icon-header fa-header-x fa-header-1", className: 'icon-header fa-header-x fa-header-1',
title: "Big Heading", title: 'Big Heading'
}, },
{ {
name: "heading-2", name: 'heading-2',
action: SimpleMDE.toggleHeading2, action: SimpleMDE.toggleHeading2,
className: "icon-header fa-header-x fa-header-2", className: 'icon-header fa-header-x fa-header-2',
title: "Medium Heading", title: 'Medium Heading'
}, },
{ {
name: "heading-3", name: 'heading-3',
action: SimpleMDE.toggleHeading3, action: SimpleMDE.toggleHeading3,
className: "icon-header fa-header-x fa-header-3", className: 'icon-header fa-header-x fa-header-3',
title: "Small Heading", title: 'Small Heading'
}, },
{ {
name: "quote", name: 'quote',
action: SimpleMDE.toggleBlockquote, action: SimpleMDE.toggleBlockquote,
className: "icon-quote-left", className: 'icon-quote-left',
title: "Quote", title: 'Quote'
}, },
'|', '|',
{ {
name: "unordered-list", name: 'unordered-list',
action: SimpleMDE.toggleUnorderedList, action: SimpleMDE.toggleUnorderedList,
className: "icon-th-list", className: 'icon-th-list',
title: "Bullet List", title: 'Bullet List'
}, },
{ {
name: "ordered-list", name: 'ordered-list',
action: SimpleMDE.toggleOrderedList, action: SimpleMDE.toggleOrderedList,
className: "icon-list-ol", className: 'icon-list-ol',
title: "Numbered List", title: 'Numbered List'
}, },
'|', '|',
{ {
name: "link", name: 'link',
action: (editor) => { action: (editor) => {
/*if(!mdeModalOpenState) { /* if(!mdeModalOpenState) {
mdeModalOpenState = true; mdeModalOpenState = true;
$('#modal-editor-link').slideToggle(); $('#modal-editor-link').slideToggle();
}*/ } */
}, },
className: "icon-link2", className: 'icon-link2',
title: "Insert Link", title: 'Insert Link'
}, },
{ {
name: "image", name: 'image',
action: (editor) => { action: (editor) => {
if(!mdeModalOpenState) { if (!mdeModalOpenState) {
vueImage.open(); vueImage.open()
} }
}, },
className: "icon-image", className: 'icon-image',
title: "Insert Image", title: 'Insert Image'
}, },
{ {
name: "file", name: 'file',
action: (editor) => { action: (editor) => {
if(!mdeModalOpenState) { if (!mdeModalOpenState) {
vueFile.open(); vueFile.open()
} }
}, },
className: "icon-paper", className: 'icon-paper',
title: "Insert File", title: 'Insert File'
}, },
{ {
name: "video", name: 'video',
action: (editor) => { action: (editor) => {
if(!mdeModalOpenState) { if (!mdeModalOpenState) {
vueVideo.open(); vueVideo.open()
} }
}, },
className: "icon-video-camera2", className: 'icon-video-camera2',
title: "Insert Video Player", title: 'Insert Video Player'
}, },
'|', '|',
{ {
name: "inline-code", name: 'inline-code',
action: (editor) => { action: (editor) => {
if (!editor.codemirror.doc.somethingSelected()) {
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) => { curSel = _.map(curSel, (s) => {
return '`' + s + '`'; return '`' + s + '`'
}); })
editor.codemirror.doc.replaceSelections(curSel); editor.codemirror.doc.replaceSelections(curSel)
}, },
className: "icon-terminal", className: 'icon-terminal',
title: "Inline Code", title: 'Inline Code'
}, },
{ {
name: "code-block", name: 'code-block',
action: (editor) => { action: (editor) => {
if(!mdeModalOpenState) { if (!mdeModalOpenState) {
mdeModalOpenState = true; mdeModalOpenState = true
if(mde.codemirror.doc.somethingSelected()) { if (mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection(); vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
} }
vueCodeBlock.open(); vueCodeBlock.open()
} }
}, },
className: "icon-code", className: 'icon-code',
title: "Code Block", title: 'Code Block'
}, },
'|', '|',
{ {
name: "table", name: 'table',
action: (editor) => { action: (editor) => {
//todo // todo
}, },
className: "icon-table", className: 'icon-table',
title: "Insert Table", title: 'Insert Table'
}, },
{ {
name: "horizontal-rule", name: 'horizontal-rule',
action: SimpleMDE.drawHorizontalRule, action: SimpleMDE.drawHorizontalRule,
className: "icon-minus2", className: 'icon-minus2',
title: "Horizontal Rule", title: 'Horizontal Rule'
} }
], ],
shortcuts: { shortcuts: {
"toggleBlockquote": null, 'toggleBlockquote': null,
"toggleFullScreen": null 'toggleFullScreen': null
} }
}); })
//-> Save // -> Save
let saveCurrentDocument = (ev) => { let saveCurrentDocument = (ev) => {
$.ajax(window.location.href, { $.ajax(window.location.href, {
...@@ -189,29 +185,28 @@ if($('#mk-editor').length === 1) { ...@@ -189,29 +185,28 @@ if($('#mk-editor').length === 1) {
dataType: 'json', dataType: 'json',
method: 'PUT' method: 'PUT'
}).then((rData, rStatus, rXHR) => { }).then((rData, rStatus, rXHR) => {
if(rData.ok) { if (rData.ok) {
window.location.assign('/' + pageEntryPath); window.location.assign('/' + pageEntryPath)
} else { } else {
alerts.pushError('Something went wrong', rData.error); alerts.pushError('Something went wrong', rData.error)
} }
}, (rXHR, rStatus, err) => { }, (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) => { $('.btn-edit-save, .btn-create-save').on('click', (ev) => {
saveCurrentDocument(ev); saveCurrentDocument(ev)
}); })
$(window).bind('keydown', (ev) => { $(window).bind('keydown', (ev) => {
if (ev.ctrlKey || ev.metaKey) { if (ev.ctrlKey || ev.metaKey) {
switch (String.fromCharCode(ev.which).toLowerCase()) { switch (String.fromCharCode(ev.which).toLowerCase()) {
case 's': case 's':
ev.preventDefault(); ev.preventDefault()
saveCurrentDocument(ev); saveCurrentDocument(ev)
break; break
} }
} }
}); })
} }
"use strict"; 'use strict'
if($('#search-input').length) { if ($('#search-input').length) {
$('#search-input').focus()
$('#search-input').focus(); $('.searchresults').css('display', 'block')
$('.searchresults').css('display', 'block');
var vueHeader = new Vue({ var vueHeader = new Vue({
el: '#header-container', el: '#header-container',
...@@ -20,65 +19,63 @@ if($('#search-input').length) { ...@@ -20,65 +19,63 @@ if($('#search-input').length) {
}, },
watch: { watch: {
searchq: (val, oldVal) => { searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0; vueHeader.searchmoveidx = 0
if(val.length >= 3) { if (val.length >= 3) {
vueHeader.searchactive = true; vueHeader.searchactive = true
vueHeader.searchload++; vueHeader.searchload++
socket.emit('search', { terms: val }, (data) => { socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match; vueHeader.searchres = data.match
vueHeader.searchsuggest = data.suggest; vueHeader.searchsuggest = data.suggest
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest); vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
if(vueHeader.searchload > 0) { vueHeader.searchload--; } if (vueHeader.searchload > 0) { vueHeader.searchload-- }
}); })
} else { } else {
vueHeader.searchactive = false; vueHeader.searchactive = false
vueHeader.searchres = []; vueHeader.searchres = []
vueHeader.searchsuggest = []; vueHeader.searchsuggest = []
vueHeader.searchmovearr = []; vueHeader.searchmovearr = []
vueHeader.searchload = 0; vueHeader.searchload = 0
} }
}, },
searchmoveidx: (val, oldVal) => { searchmoveidx: (val, oldVal) => {
if(val > 0) { if (val > 0) {
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ? vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ?
'res.' + vueHeader.searchmovearr[val - 1]._id : 'res.' + vueHeader.searchmovearr[val - 1]._id :
'sug.' + vueHeader.searchmovearr[val - 1]; 'sug.' + vueHeader.searchmovearr[val - 1]
} else { } else {
vueHeader.searchmovekey = ''; vueHeader.searchmovekey = ''
} }
} }
}, },
methods: { methods: {
useSuggestion: (sug) => { useSuggestion: (sug) => {
vueHeader.searchq = sug; vueHeader.searchq = sug
}, },
closeSearch: () => { closeSearch: () => {
vueHeader.searchq = ''; vueHeader.searchq = ''
}, },
moveSelectSearch: () => { moveSelectSearch: () => {
if(vueHeader.searchmoveidx < 1) { return; } if (vueHeader.searchmoveidx < 1) { return }
let i = vueHeader.searchmoveidx - 1; let i = vueHeader.searchmoveidx - 1
if(vueHeader.searchmovearr[i]) { if (vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id); window.location.assign('/' + vueHeader.searchmovearr[i]._id)
} else { } else {
vueHeader.searchq = vueHeader.searchmovearr[i]; vueHeader.searchq = vueHeader.searchmovearr[i]
} }
}, },
moveDownSearch: () => { moveDownSearch: () => {
if(vueHeader.searchmoveidx < vueHeader.searchmovearr.length) { if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++; vueHeader.searchmoveidx++
} }
}, },
moveUpSearch: () => { moveUpSearch: () => {
if(vueHeader.searchmoveidx > 0) { if (vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--; vueHeader.searchmoveidx--
} }
} }
} }
}); })
$('main').on('click', vueHeader.closeSearch);
$('main').on('click', vueHeader.closeSearch)
} }
function setInputSelection(input, startPos, endPos) { function setInputSelection (input, startPos, endPos) {
input.focus(); input.focus()
if (typeof input.selectionStart != "undefined") { if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos; input.selectionStart = startPos
input.selectionEnd = endPos; input.selectionEnd = endPos
} else if (document.selection && document.selection.createRange) { } else if (document.selection && document.selection.createRange) {
// IE branch // IE branch
input.select(); input.select()
var range = document.selection.createRange(); var range = document.selection.createRange()
range.collapse(true); range.collapse(true)
range.moveEnd("character", endPos); range.moveEnd('character', endPos)
range.moveStart("character", startPos); range.moveStart('character', startPos)
range.select(); range.select()
} }
} }
function makeSafePath(rawPath) { function makeSafePath (rawPath) {
let rawParts = _.split(_.trim(rawPath), '/')
let rawParts = _.split(_.trim(rawPath), '/');
rawParts = _.map(rawParts, (r) => { rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r))); return _.kebabCase(_.deburr(_.trim(r)))
}); })
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r); }), '/');
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
} }
"use strict"; 'use strict'
jQuery( document ).ready(function( $ ) { jQuery(document).ready(function ($) {
$('#login-user').focus()
$('#login-user').focus(); })
});
\ No newline at end of file
...@@ -11,20 +11,18 @@ let vueCreateUser = new Vue({ ...@@ -11,20 +11,18 @@ let vueCreateUser = new Vue({
}, },
methods: { methods: {
open: (ev) => { open: (ev) => {
$('#modal-admin-users-create').addClass('is-active'); $('#modal-admin-users-create').addClass('is-active')
$('#modal-admin-users-create input').first().focus(); $('#modal-admin-users-create input').first().focus()
}, },
cancel: (ev) => { cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active'); $('#modal-admin-users-create').removeClass('is-active')
vueCreateUser.email = ''; vueCreateUser.email = ''
vueCreateUser.provider = 'local'; vueCreateUser.provider = 'local'
}, },
create: (ev) => { create: (ev) => {
vueCreateUser.cancel()
vueCreateUser.cancel();
} }
} }
}); })
$('.btn-create-prompt').on('click', vueCreateUser.open); $('.btn-create-prompt').on('click', vueCreateUser.open)
\ No newline at end of file
...@@ -8,15 +8,15 @@ let vueDeleteUser = new Vue({ ...@@ -8,15 +8,15 @@ let vueDeleteUser = new Vue({
}, },
methods: { methods: {
open: (ev) => { open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active'); $('#modal-admin-users-delete').addClass('is-active')
}, },
cancel: (ev) => { cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active'); $('#modal-admin-users-delete').removeClass('is-active')
}, },
deleteUser: (ev) => { deleteUser: (ev) => {
vueDeleteUser.cancel(); vueDeleteUser.cancel()
} }
} }
}); })
$('.btn-deluser-prompt').on('click', vueDeleteUser.open); $('.btn-deluser-prompt').on('click', vueDeleteUser.open)
\ No newline at end of file
//-> Create New Document // -> Create New Document
let suggestedCreatePath = currentBasePath + '/new-page'; let suggestedCreatePath = currentBasePath + '/new-page'
$('.btn-create-prompt').on('click', (ev) => { $('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath); $('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active'); $('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length); setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden'); $('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
}); })
$('#txt-create-prompt').on('keypress', (ev) => { $('#txt-create-prompt').on('keypress', (ev) => {
if(ev.which === 13) { if (ev.which === 13) {
$('.btn-create-go').trigger('click'); $('.btn-create-go').trigger('click')
} }
}); })
$('.btn-create-go').on('click', (ev) => { $('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val())
let newDocPath = makeSafePath($('#txt-create-prompt').val()); if (_.isEmpty(newDocPath)) {
if(_.isEmpty(newDocPath)) { $('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden');
} else { } else {
$('#txt-create-prompt').parent().addClass('is-loading'); $('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath); window.location.assign('/create/' + newDocPath)
} }
})
});
\ No newline at end of file
//-> Move Existing Document // -> Move Existing Document
if(currentBasePath !== '') { 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) => { $('.btn-move-prompt').on('click', (ev) => {
$('#txt-move-prompt').val(currentBasePath); $('#txt-move-prompt').val(currentBasePath)
$('#modal-move-prompt').toggleClass('is-active'); $('#modal-move-prompt').toggleClass('is-active')
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length); setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length)
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden'); $('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
}); })
$('#txt-move-prompt').on('keypress', (ev) => { $('#txt-move-prompt').on('keypress', (ev) => {
if(ev.which === 13) { if (ev.which === 13) {
$('.btn-move-go').trigger('click'); $('.btn-move-go').trigger('click')
} }
}); })
$('.btn-move-go').on('click', (ev) => { $('.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') {
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 { } else {
$('#txt-move-prompt').parent().addClass('is-loading'); $('#txt-move-prompt').parent().addClass('is-loading')
$.ajax(window.location.href, { $.ajax(window.location.href, {
data: { data: {
...@@ -35,15 +34,13 @@ $('.btn-move-go').on('click', (ev) => { ...@@ -35,15 +34,13 @@ $('.btn-move-go').on('click', (ev) => {
dataType: 'json', dataType: 'json',
method: 'PUT' method: 'PUT'
}).then((rData, rStatus, rXHR) => { }).then((rData, rStatus, rXHR) => {
if(rData.ok) { if (rData.ok) {
window.location.assign('/' + newDocPath); window.location.assign('/' + newDocPath)
} else { } else {
alerts.pushError('Something went wrong', rData.error); alerts.pushError('Something went wrong', rData.error)
} }
}, (rXHR, rStatus, err) => { }, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.'); alerts.pushError('Something went wrong', 'Save operation failed.')
}); })
} }
})
});
\ No newline at end of file
if($('#page-type-admin-profile').length) { if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({ let vueProfile = new Vue({
el: '#page-type-admin-profile', el: '#page-type-admin-profile',
data: { data: {
...@@ -10,31 +9,29 @@ if($('#page-type-admin-profile').length) { ...@@ -10,31 +9,29 @@ if($('#page-type-admin-profile').length) {
}, },
methods: { methods: {
saveUser: (ev) => { saveUser: (ev) => {
if(vueProfile.password !== vueProfile.passwordVerify) { if (vueProfile.password !== vueProfile.passwordVerify) {
alerts.pushError('Error', "Passwords don't match!"); alerts.pushError('Error', "Passwords don't match!")
return; return
} }
$.post(window.location.href, { $.post(window.location.href, {
password: vueProfile.password, password: vueProfile.password,
name: vueProfile.name name: vueProfile.name
}).done((resp) => { }).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.'); alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => { }).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp); alerts.pushError('Error', resp)
}) })
} }
}, },
created: function() { created: function () {
this.name = usrDataName; this.name = usrDataName
} }
}); })
} else if ($('#page-type-admin-users').length) {
} else if($('#page-type-admin-users').length) {
//=include ../modals/admin-users-create.js
} else if($('#page-type-admin-users-edit').length) { // =include ../modals/admin-users-create.js
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({ let vueEditUser = new Vue({
el: '#page-type-admin-users-edit', el: '#page-type-admin-users-edit',
data: { data: {
...@@ -52,7 +49,7 @@ if($('#page-type-admin-profile').length) { ...@@ -52,7 +49,7 @@ if($('#page-type-admin-profile').length) {
path: '/', path: '/',
exact: false, exact: false,
deny: false deny: false
}); })
}, },
removeRightsRow: (idx) => { removeRightsRow: (idx) => {
_.pullAt(vueEditUser.rights, idx) _.pullAt(vueEditUser.rights, idx)
...@@ -60,7 +57,7 @@ if($('#page-type-admin-profile').length) { ...@@ -60,7 +57,7 @@ if($('#page-type-admin-profile').length) {
}, },
saveUser: (ev) => { saveUser: (ev) => {
let formattedRights = _.cloneDeep(vueEditUser.rights) let formattedRights = _.cloneDeep(vueEditUser.rights)
switch(vueEditUser.roleoverride) { switch (vueEditUser.roleoverride) {
case 'admin': case 'admin':
formattedRights.push({ formattedRights.push({
role: 'admin', role: 'admin',
...@@ -68,35 +65,32 @@ if($('#page-type-admin-profile').length) { ...@@ -68,35 +65,32 @@ if($('#page-type-admin-profile').length) {
exact: false, exact: false,
deny: false deny: false
}) })
break; break
} }
$.post(window.location.href, { $.post(window.location.href, {
password: vueEditUser.password, password: vueEditUser.password,
name: vueEditUser.name, name: vueEditUser.name,
rights: JSON.stringify(formattedRights) rights: JSON.stringify(formattedRights)
}).done((resp) => { }).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.'); alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => { }).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp); alerts.pushError('Error', resp)
}) })
} }
}, },
created: function() { created: function () {
this.id = usrData._id
this.email = usrData.email
this.name = usrData.name
this.id = usrData._id; if (_.find(usrData.rights, { role: 'admin' })) {
this.email = usrData.email; this.rights = _.reject(usrData.rights, ['role', 'admin'])
this.name = usrData.name; this.roleoverride = 'admin'
if(_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin']);
this.roleoverride = 'admin';
} else { } else {
this.rights = usrData.rights; this.rights = usrData.rights
} }
} }
}); })
//=include ../modals/admin-users-delete.js
// =include ../modals/admin-users-delete.js
} }
if($('#page-type-create').length) { if ($('#page-type-create').length) {
let pageEntryPath = $('#page-type-create').data('entrypath')
let pageEntryPath = $('#page-type-create').data('entrypath'); // -> Discard
//-> Discard
$('.btn-create-discard').on('click', (ev) => { $('.btn-create-discard').on('click', (ev) => {
$('#modal-create-discard').toggleClass('is-active'); $('#modal-create-discard').toggleClass('is-active')
}); })
//=include ../components/editor.js
// =include ../components/editor.js
} }
if($('#page-type-edit').length) { if ($('#page-type-edit').length) {
let pageEntryPath = $('#page-type-edit').data('entrypath')
let pageEntryPath = $('#page-type-edit').data('entrypath'); // -> Discard
//-> Discard
$('.btn-edit-discard').on('click', (ev) => { $('.btn-edit-discard').on('click', (ev) => {
$('#modal-edit-discard').toggleClass('is-active'); $('#modal-edit-discard').toggleClass('is-active')
}); })
//=include ../components/editor.js
// =include ../components/editor.js
} }
if($('#page-type-source').length) { 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"); let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
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') : '';
//=include ../modals/create.js
//=include ../modals/move.js
// =include ../modals/create.js
// =include ../modals/move.js
} }
if($('#page-type-view').length) { 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
// =include ../modals/create.js
// =include ../modals/move.js
} }
"use strict"; 'use strict'
var express = require('express'); var express = require('express')
var router = express.Router(); var router = express.Router()
const Promise = require('bluebird'); const Promise = require('bluebird')
const validator = require('validator'); const validator = require('validator')
const _ = require('lodash'); const _ = require('lodash')
/** /**
* Admin * Admin
*/ */
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.redirect('/admin/profile'); res.redirect('/admin/profile')
}); })
router.get('/profile', (req, res) => { router.get('/profile', (req, res) => {
if (res.locals.isGuest) {
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) => { router.post('/profile', (req, res) => {
if (res.locals.isGuest) {
if(res.locals.isGuest) { return res.render('error-forbidden')
return res.render('error-forbidden');
} }
return db.User.findById(req.user.id).then((usr) => { 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 !== '********') { if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password); let nPwd = _.trim(req.body.password)
if(nPwd.length < 6) { if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!')) return Promise.reject(new Error('New Password too short!'))
} else { } else {
return db.User.hashPassword(nPwd).then((pwd) => { return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd; usr.password = pwd
return usr.save(); return usr.save()
}); })
} }
} else { } else {
return usr.save(); return usr.save()
} }
}).then(() => { }).then(() => {
return res.json({ msg: 'OK' }); return res.json({ msg: 'OK' })
}).catch((err) => { }).catch((err) => {
res.status(400).json({ msg: err.message }); res.status(400).json({ msg: err.message })
}) })
})
});
router.get('/stats', (req, res) => { router.get('/stats', (req, res) => {
if (res.locals.isGuest) {
if(res.locals.isGuest) { return res.render('error-forbidden')
return res.render('error-forbidden');
} }
Promise.all([ Promise.all([
...@@ -64,99 +59,88 @@ router.get('/stats', (req, res) => { ...@@ -64,99 +59,88 @@ router.get('/stats', (req, res) => {
db.User.count() db.User.count()
]).spread((totalEntries, totalUploads, totalUsers) => { ]).spread((totalEntries, totalUploads, totalUsers) => {
return res.render('pages/admin/stats', { return res.render('pages/admin/stats', {
totalEntries, totalUploads, totalUsers, totalEntries, totalUploads, totalUsers, adminTab: 'stats'
adminTab: 'stats' }) || true
}) || true;
}).catch((err) => { }).catch((err) => {
throw err; throw err
}); })
})
});
router.get('/users', (req, res) => { router.get('/users', (req, res) => {
if (!res.locals.rights.manage) {
if(!res.locals.rights.manage) { return res.render('error-forbidden')
return res.render('error-forbidden');
} }
db.User.find({}) db.User.find({})
.select('-password -rights') .select('-password -rights')
.sort('name email') .sort('name email')
.exec().then((usrs) => { .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) => { router.get('/users/:id', (req, res) => {
if (!res.locals.rights.manage) {
if(!res.locals.rights.manage) { return res.render('error-forbidden')
return res.render('error-forbidden');
} }
if(!validator.isMongoId(req.params.id)) { if (!validator.isMongoId(req.params.id)) {
return res.render('error-forbidden'); return res.render('error-forbidden')
} }
db.User.findById(req.params.id) db.User.findById(req.params.id)
.select('-password -providerId') .select('-password -providerId')
.exec().then((usr) => { .exec().then((usr) => {
let usrOpts = { let usrOpts = {
canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin), canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin),
canChangeName: (usr.email !== 'guest'), canChangeName: (usr.email !== 'guest'),
canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'), canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'),
canChangeRole: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin)), 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)) 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) => { router.post('/users/:id', (req, res) => {
if (!res.locals.rights.manage) {
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)) { 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) => { return db.User.findById(req.params.id).then((usr) => {
usr.name = _.trim(req.body.name); usr.name = _.trim(req.body.name)
usr.rights = JSON.parse(req.body.rights); usr.rights = JSON.parse(req.body.rights)
if(usr.provider === 'local' && req.body.password !== '********') { if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password); let nPwd = _.trim(req.body.password)
if(nPwd.length < 6) { if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!')) return Promise.reject(new Error('New Password too short!'))
} else { } else {
return db.User.hashPassword(nPwd).then((pwd) => { return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd; usr.password = pwd
return usr.save(); return usr.save()
}); })
} }
} else { } else {
return usr.save(); return usr.save()
} }
}).then(() => { }).then(() => {
return res.json({ msg: 'OK' }); return res.json({ msg: 'OK' })
}).catch((err) => { }).catch((err) => {
res.status(400).json({ msg: err.message }); res.status(400).json({ msg: err.message })
}) })
})
});
router.get('/settings', (req, res) => { router.get('/settings', (req, res) => {
if (!res.locals.rights.manage) {
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
\ No newline at end of file
var express = require('express'); 'use strict'
var router = express.Router();
var passport = require('passport'); const express = require('express')
var ExpressBrute = require('express-brute'); const router = express.Router()
var ExpressBruteMongooseStore = require('express-brute-mongoose'); const passport = require('passport')
var moment = require('moment'); const ExpressBrute = require('express-brute')
const ExpressBruteMongooseStore = require('express-brute-mongoose')
const moment = require('moment')
/** /**
* Setup Express-Brute * Setup Express-Brute
*/ */
var EBstore = new ExpressBruteMongooseStore(db.Bruteforce); const EBstore = new ExpressBruteMongooseStore(db.Bruteforce)
var bruteforce = new ExpressBrute(EBstore, { const bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5, freeRetries: 5,
minWait: 60 * 1000, minWait: 60 * 1000,
maxWait: 5 * 60 * 1000, maxWait: 5 * 60 * 1000,
refreshTimeoutOnRequest: false, refreshTimeoutOnRequest: false,
failCallback(req, res, next, nextValidRequestDate) { failCallback (req, res, next, nextValidRequestDate) {
req.flash('alert', { req.flash('alert', {
class: 'error', class: 'error',
title: 'Too many attempts!', title: 'Too many attempts!',
message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.', message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.',
iconClass: 'fa-times' iconClass: 'fa-times'
}); })
res.redirect('/login'); res.redirect('/login')
} }
}); })
/** /**
* Login form * Login form
*/ */
router.get('/login', function(req, res, next) { router.get('/login', function (req, res, next) {
res.render('auth/login', { res.render('auth/login', {
usr: res.locals.usr 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); } router.post('/login', bruteforce.prevent, function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
if (err) { return next(err) }
if (!user) { if (!user) {
req.flash('alert', { req.flash('alert', {
title: 'Invalid login', title: 'Invalid login',
message: "The email or password is invalid." message: 'The email or password is invalid.'
}); })
return res.redirect('/login'); return res.redirect('/login')
} }
req.logIn(user, function(err) { req.logIn(user, function (err) {
if (err) { return next(err); } if (err) { return next(err) }
req.brute.reset(function () { req.brute.reset(function () {
return res.redirect('/'); return res.redirect('/')
}); })
}); })
})(req, res, next)
})(req, res, next); })
});
/** /**
* Social Login * Social Login
*/ */
router.get('/login/ms', passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] })); 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/google', passport.authenticate('google', { scope: ['profile', 'email'] }))
router.get('/login/facebook', passport.authenticate('facebook', { scope: ['public_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/ms/callback', passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/google/callback', passport.authenticate('google', { 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/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }))
/** /**
* Logout * Logout
*/ */
router.get('/logout', function(req, res) { router.get('/logout', function (req, res) {
req.logout(); req.logout()
res.redirect('/'); res.redirect('/')
}); })
module.exports = router; module.exports = router
\ No newline at end of file
"use strict"; 'use strict'
var express = require('express'); const express = require('express')
var router = express.Router(); const router = express.Router()
var _ = require('lodash'); const _ = require('lodash')
// ========================================== // ==========================================
// EDIT MODE // EDIT MODE
...@@ -12,12 +12,11 @@ var _ = require('lodash'); ...@@ -12,12 +12,11 @@ var _ = require('lodash');
* Edit document in Markdown * Edit document in Markdown
*/ */
router.get('/edit/*', (req, res, next) => { router.get('/edit/*', (req, res, next) => {
if (!res.locals.rights.write) {
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, { entries.fetchOriginal(safePath, {
parseMarkdown: false, parseMarkdown: false,
...@@ -27,117 +26,109 @@ router.get('/edit/*', (req, res, next) => { ...@@ -27,117 +26,109 @@ router.get('/edit/*', (req, res, next) => {
includeParentInfo: false, includeParentInfo: false,
cache: false cache: false
}).then((pageData) => { }).then((pageData) => {
if(pageData) { if (pageData) {
res.render('pages/edit', { pageData }); res.render('pages/edit', { pageData })
} else { } else {
throw new Error('Invalid page path.'); throw new Error('Invalid page path.')
} }
return true; return true
}).catch((err) => { }).catch((err) => {
res.render('error', { res.render('error', {
message: err.message, message: err.message,
error: {} error: {}
}); })
}); })
})
});
router.put('/edit/*', (req, res, next) => { router.put('/edit/*', (req, res, next) => {
if (!res.locals.rights.write) {
if(!res.locals.rights.write) {
return res.json({ return res.json({
ok: false, ok: false,
error: 'Forbidden' 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(() => { entries.update(safePath, req.body.markdown).then(() => {
return res.json({ return res.json({
ok: true ok: true
}) || true; }) || true
}).catch((err) => { }).catch((err) => {
res.json({ res.json({
ok: false, ok: false,
error: err.message error: err.message
}); })
}); })
})
});
// ========================================== // ==========================================
// CREATE MODE // CREATE MODE
// ========================================== // ==========================================
router.get('/create/*', (req, res, next) => { router.get('/create/*', (req, res, next) => {
if (!res.locals.rights.write) {
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', { return res.render('error', {
message: 'You cannot create a document with this name as it is reserved by the system.', message: 'You cannot create a document with this name as it is reserved by the system.',
error: {} error: {}
}); })
} }
let safePath = entries.parsePath(_.replace(req.path, '/create', '')); let safePath = entries.parsePath(_.replace(req.path, '/create', ''))
entries.exists(safePath).then((docExists) => { entries.exists(safePath).then((docExists) => {
if(!docExists) { if (!docExists) {
return entries.getStarter(safePath).then((contents) => { return entries.getStarter(safePath).then((contents) => {
let pageData = { let pageData = {
markdown: contents, markdown: contents,
meta: { meta: {
title: _.startCase(safePath), title: _.startCase(safePath),
path: safePath path: safePath
} }
}; }
res.render('pages/create', { pageData }); res.render('pages/create', { pageData })
return true;
return true
}).catch((err) => { }).catch((err) => {
throw new Error('Could not load starter content!'); winston.warn(err)
}); throw new Error('Could not load starter content!')
})
} else { } else {
throw new Error('This entry already exists!'); throw new Error('This entry already exists!')
} }
}).catch((err) => { }).catch((err) => {
res.render('error', { res.render('error', {
message: err.message, message: err.message,
error: {} error: {}
}); })
}); })
})
});
router.put('/create/*', (req, res, next) => { router.put('/create/*', (req, res, next) => {
if (!res.locals.rights.write) {
if(!res.locals.rights.write) {
return res.json({ return res.json({
ok: false, ok: false,
error: 'Forbidden' 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(() => { entries.create(safePath, req.body.markdown).then(() => {
return res.json({ return res.json({
ok: true ok: true
}) || true; }) || true
}).catch((err) => { }).catch((err) => {
return res.json({ return res.json({
ok: false, ok: false,
error: err.message error: err.message
}); })
}); })
})
});
// ========================================== // ==========================================
// VIEW MODE // VIEW MODE
...@@ -147,8 +138,7 @@ router.put('/create/*', (req, res, next) => { ...@@ -147,8 +138,7 @@ router.put('/create/*', (req, res, next) => {
* View source of a document * View source of a document
*/ */
router.get('/source/*', (req, res, next) => { 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, { entries.fetchOriginal(safePath, {
parseMarkdown: false, parseMarkdown: false,
...@@ -158,91 +148,84 @@ router.get('/source/*', (req, res, next) => { ...@@ -158,91 +148,84 @@ router.get('/source/*', (req, res, next) => {
includeParentInfo: false, includeParentInfo: false,
cache: false cache: false
}).then((pageData) => { }).then((pageData) => {
if(pageData) { if (pageData) {
res.render('pages/source', { pageData }); res.render('pages/source', { pageData })
} else { } else {
throw new Error('Invalid page path.'); throw new Error('Invalid page path.')
} }
return true; return true
}).catch((err) => { }).catch((err) => {
res.render('error', { res.render('error', {
message: err.message, message: err.message,
error: {} error: {}
}); })
}); })
})
});
/** /**
* View document * View document
*/ */
router.get('/*', (req, res, next) => { router.get('/*', (req, res, next) => {
let safePath = entries.parsePath(req.path)
let safePath = entries.parsePath(req.path);
entries.fetch(safePath).then((pageData) => { entries.fetch(safePath).then((pageData) => {
if(pageData) { if (pageData) {
res.render('pages/view', { pageData }); res.render('pages/view', { pageData })
} else { } else {
res.render('error-notexist', { res.render('error-notexist', {
newpath: safePath newpath: safePath
}); })
} }
return true; return true
}).error((err) => { }).error((err) => {
if (safePath === 'home') {
if(safePath === 'home') { res.render('pages/welcome')
res.render('pages/welcome');
} else { } else {
res.render('error-notexist', { res.render('error-notexist', {
message: err.message, message: err.message,
newpath: safePath newpath: safePath
}); })
} }
}).catch((err) => { }).catch((err) => {
res.render('error', { res.render('error', {
message: err.message, message: err.message,
error: {} error: {}
}); })
}); })
})
});
/** /**
* Move document * Move document
*/ */
router.put('/*', (req, res, next) => { router.put('/*', (req, res, next) => {
if (!res.locals.rights.write) {
if(!res.locals.rights.write) {
return res.json({ return res.json({
ok: false, ok: false,
error: 'Forbidden' error: 'Forbidden'
}); })
} }
let safePath = entries.parsePath(req.path); let safePath = entries.parsePath(req.path)
if(_.isEmpty(req.body.move)) { if (_.isEmpty(req.body.move)) {
return res.json({ return res.json({
ok: false, ok: false,
error: 'Invalid document action call.' error: 'Invalid document action call.'
}); })
} }
let safeNewPath = entries.parsePath(req.body.move); let safeNewPath = entries.parsePath(req.body.move)
entries.move(safePath, safeNewPath).then(() => { entries.move(safePath, safeNewPath).then(() => {
res.json({ res.json({
ok: true ok: true
}); })
}).catch((err) => { }).catch((err) => {
res.json({ res.json({
ok: false, ok: false,
error: err.message error: err.message
}); })
}); })
})
});
module.exports = router; module.exports = router
\ No newline at end of file
"use strict"; 'use strict'
var express = require('express'); const express = require('express')
var router = express.Router(); const router = express.Router()
var readChunk = require('read-chunk'), const readChunk = require('read-chunk')
fileType = require('file-type'), const fileType = require('file-type')
Promise = require('bluebird'), const Promise = require('bluebird')
fs = Promise.promisifyAll(require('fs-extra')), const fs = Promise.promisifyAll(require('fs-extra'))
path = require('path'), const path = require('path')
_ = require('lodash'); const _ = require('lodash')
var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$"); const validPathRe = new RegExp('^([a-z0-9\\/-]+\\.[a-z0-9]+)$')
var validPathThumbsRe = new RegExp("^([0-9]+\\.png)$"); const validPathThumbsRe = new RegExp('^([0-9]+\\.png)$')
// ========================================== // ==========================================
// SERVE UPLOADS FILES // SERVE UPLOADS FILES
// ========================================== // ==========================================
router.get('/t/*', (req, res, next) => { router.get('/t/*', (req, res, next) => {
let fileName = req.params[0]
let fileName = req.params[0]; if (!validPathThumbsRe.test(fileName)) {
if(!validPathThumbsRe.test(fileName)) { return res.sendStatus(404).end()
return res.sendStatus(404).end();
} }
//todo: Authentication-based access // todo: Authentication-based access
res.sendFile(fileName, { res.sendFile(fileName, {
root: lcdata.getThumbsPath(), root: lcdata.getThumbsPath(),
dotfiles: 'deny' dotfiles: 'deny'
}, (err) => { }, (err) => {
if (err) { if (err) {
res.status(err.status).end(); res.status(err.status).end()
} }
}); })
})
});
router.post('/img', lcdata.uploadImgHandler, (req, res, next) => { 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) => { upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
if(!destFolderPath) { res.json({ ok: false, msg: 'Invalid Folder' })
res.json({ ok: false, msg: 'Invalid Folder' }); return true
return true;
} }
Promise.map(req.files, (f) => { Promise.map(req.files, (f) => {
let destFilename = ''
let destFilename = ''; let destFilePath = ''
let destFilePath = '';
return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => { return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
destFilename = fname; return readChunk(f.path, 0, 262)
destFilePath = path.resolve(destFolderPath, destFilename);
return readChunk(f.path, 0, 262);
}).then((buf) => { }).then((buf) => {
// -> Check MIME type by magic number
//-> Check MIME type by magic number let mimeInfo = fileType(buf)
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
let mimeInfo = fileType(buf); return Promise.reject(new Error('Invalid file type.'))
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return Promise.reject(new Error('Invalid file type.'));
} }
return true; return true
}).then(() => { }).then(() => {
// -> Move file to final destination
//-> Move file to final destination return fs.moveAsync(f.path, destFilePath, { clobber: false })
return fs.moveAsync(f.path, destFilePath, { clobber: false });
}).then(() => { }).then(() => {
return { return {
ok: true, ok: true,
filename: destFilename, filename: destFilename,
filesize: f.size filesize: f.size
}; }
}).reflect(); }).reflect()
}, {concurrency: 3}).then((results) => { }, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => { let uplResults = _.map(results, (r) => {
if(r.isFulfilled()) { if (r.isFulfilled()) {
return r.value(); return r.value()
} else { } else {
return { return {
ok: false, ok: false,
msg: r.reason().message msg: r.reason().message
};
} }
}); }
res.json({ ok: true, results: uplResults }); })
return true; res.json({ ok: true, results: uplResults })
return true
}).catch((err) => { }).catch((err) => {
res.json({ ok: false, msg: err.message }); res.json({ ok: false, msg: err.message })
return true; return true
}); })
})
}); })
});
router.post('/file', lcdata.uploadFileHandler, (req, res, next) => { 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) => { upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
if(!destFolderPath) { res.json({ ok: false, msg: 'Invalid Folder' })
res.json({ ok: false, msg: 'Invalid Folder' }); return true
return true;
} }
Promise.map(req.files, (f) => { Promise.map(req.files, (f) => {
let destFilename = ''
let destFilename = ''; let destFilePath = ''
let destFilePath = '';
return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => { return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
destFilename = fname; // -> Move file to final destination
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(() => { }).then(() => {
return { return {
ok: true, ok: true,
filename: destFilename, filename: destFilename,
filesize: f.size filesize: f.size
}; }
}).reflect(); }).reflect()
}, {concurrency: 3}).then((results) => { }, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => { let uplResults = _.map(results, (r) => {
if(r.isFulfilled()) { if (r.isFulfilled()) {
return r.value(); return r.value()
} else { } else {
return { return {
ok: false, ok: false,
msg: r.reason().message msg: r.reason().message
};
} }
}); }
res.json({ ok: true, results: uplResults }); })
return true; res.json({ ok: true, results: uplResults })
return true
}).catch((err) => { }).catch((err) => {
res.json({ ok: false, msg: err.message }); res.json({ ok: false, msg: err.message })
return true; return true
}); })
})
}); })
});
router.get('/*', (req, res, next) => { router.get('/*', (req, res, next) => {
let fileName = req.params[0]
let fileName = req.params[0]; if (!validPathRe.test(fileName)) {
if(!validPathRe.test(fileName)) { return res.sendStatus(404).end()
return res.sendStatus(404).end();
} }
//todo: Authentication-based access // todo: Authentication-based access
res.sendFile(fileName, { res.sendFile(fileName, {
root: git.getRepoPath() + '/uploads/', root: git.getRepoPath() + '/uploads/',
dotfiles: 'deny' dotfiles: 'deny'
}, (err) => { }, (err) => {
if (err) { if (err) {
res.status(err.status).end(); res.status(err.status).end()
} }
}); })
})
});
module.exports = router; module.exports = router
\ No newline at end of file
"use strict"; 'use strict'
module.exports = (socket) => { const _ = require('lodash')
if(!socket.request.user.logged_in) { module.exports = (socket) => {
return; if (!socket.request.user.logged_in) {
return
} }
//----------------------------------------- // -----------------------------------------
// SEARCH // SEARCH
//----------------------------------------- // -----------------------------------------
socket.on('search', (data, cb) => { socket.on('search', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
entries.search(data.terms).then((results) => { entries.search(data.terms).then((results) => {
return cb(results) || true; return cb(results) || true
}); })
}); })
//----------------------------------------- // -----------------------------------------
// UPLOADS // UPLOADS
//----------------------------------------- // -----------------------------------------
socket.on('uploadsGetFolders', (data, cb) => { socket.on('uploadsGetFolders', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.getUploadsFolders().then((f) => { upl.getUploadsFolders().then((f) => {
return cb(f) || true; return cb(f) || true
}); })
}); })
socket.on('uploadsCreateFolder', (data, cb) => { socket.on('uploadsCreateFolder', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.createUploadsFolder(data.foldername).then((f) => { upl.createUploadsFolder(data.foldername).then((f) => {
return cb(f) || true; return cb(f) || true
}); })
}); })
socket.on('uploadsGetImages', (data, cb) => { socket.on('uploadsGetImages', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.getUploadsFiles('image', data.folder).then((f) => { upl.getUploadsFiles('image', data.folder).then((f) => {
return cb(f) || true; return cb(f) || true
}); })
}); })
socket.on('uploadsGetFiles', (data, cb) => { socket.on('uploadsGetFiles', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.getUploadsFiles('binary', data.folder).then((f) => { upl.getUploadsFiles('binary', data.folder).then((f) => {
return cb(f) || true; return cb(f) || true
}); })
}); })
socket.on('uploadsDeleteFile', (data, cb) => { socket.on('uploadsDeleteFile', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.deleteUploadsFile(data.uid).then((f) => { upl.deleteUploadsFile(data.uid).then((f) => {
return cb(f) || true; return cb(f) || true
}); })
}); })
socket.on('uploadsFetchFileFromURL', (data, cb) => { socket.on('uploadsFetchFileFromURL', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.downloadFromUrl(data.folder, data.fetchUrl).then((f) => { upl.downloadFromUrl(data.folder, data.fetchUrl).then((f) => {
return cb({ ok: true }) || true; return cb({ ok: true }) || true
}).catch((err) => { }).catch((err) => {
return cb({ return cb({
ok: false, ok: false,
msg: err.message msg: err.message
}) || true; }) || true
}); })
}); })
socket.on('uploadsRenameFile', (data, cb) => { socket.on('uploadsRenameFile', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder, data.filename).then((f) => { upl.moveUploadsFile(data.uid, data.folder, data.filename).then((f) => {
return cb({ ok: true }) || true; return cb({ ok: true }) || true
}).catch((err) => { }).catch((err) => {
return cb({ return cb({
ok: false, ok: false,
msg: err.message msg: err.message
}) || true; }) || true
}); })
}); })
socket.on('uploadsMoveFile', (data, cb) => { socket.on('uploadsMoveFile', (data, cb) => {
cb = cb || _.noop; cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder).then((f) => { upl.moveUploadsFile(data.uid, data.folder).then((f) => {
return cb({ ok: true }) || true; return cb({ ok: true }) || true
}).catch((err) => { }).catch((err) => {
return cb({ return cb({
ok: false, ok: false,
msg: err.message msg: err.message
}) || true; }) || true
}); })
}); })
}
};
\ No newline at end of file
var gulp = require("gulp"); 'use strict'
var watch = require('gulp-watch');
var merge = require('merge-stream'); const gulp = require('gulp')
var babel = require("gulp-babel"); const watch = require('gulp-watch')
var uglify = require('gulp-uglify'); const merge = require('merge-stream')
var concat = require('gulp-concat'); const babel = require('gulp-babel')
var nodemon = require('gulp-nodemon'); const uglify = require('gulp-uglify')
var plumber = require('gulp-plumber'); const concat = require('gulp-concat')
var zip = require('gulp-zip'); const nodemon = require('gulp-nodemon')
var tar = require('gulp-tar'); const plumber = require('gulp-plumber')
var gzip = require('gulp-gzip'); const zip = require('gulp-zip')
var sass = require('gulp-sass'); const tar = require('gulp-tar')
var cleanCSS = require('gulp-clean-css'); const gzip = require('gulp-gzip')
var include = require("gulp-include"); const sass = require('gulp-sass')
var run = require('run-sequence'); const cleanCSS = require('gulp-clean-css')
var _ = require('lodash'); const include = require('gulp-include')
const run = require('run-sequence')
/** /**
* Paths * Paths
* *
* @type {Object} * @type {Object}
*/ */
var paths = { const paths = {
scripts: { scripts: {
combine: [ combine: [
'./node_modules/socket.io-client/dist/socket.io.min.js', './node_modules/socket.io-client/dist/socket.io.min.js',
...@@ -81,135 +82,129 @@ var paths = { ...@@ -81,135 +82,129 @@ var paths = {
'!.babelrc', '!.gitattributes', '!.gitignore', '!.snyk', '!.travis.yml', '!.babelrc', '!.gitattributes', '!.gitignore', '!.snyk', '!.travis.yml',
'!gulpfile.js', '!inch.json', '!config.yml', '!wiki.sublime-project' '!gulpfile.js', '!inch.json', '!config.yml', '!wiki.sublime-project'
] ]
}; }
/** /**
* TASK - Starts server in development mode * TASK - Starts server in development mode
*/ */
gulp.task('server', ['scripts', 'css', 'fonts'], function() { gulp.task('server', ['scripts', 'css', 'fonts'], function () {
nodemon({ nodemon({
script: './server', script: './server',
ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'], ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'],
ext: 'js json', ext: 'js json',
env: { 'NODE_ENV': 'development' } env: { 'NODE_ENV': 'development' }
}); })
}); })
/** /**
* TASK - Process all scripts processes * TASK - Process all scripts processes
*/ */
gulp.task("scripts", ['scripts-libs', 'scripts-app']); gulp.task('scripts', ['scripts-libs', 'scripts-app'])
/** /**
* TASK - Combine js libraries * TASK - Combine js libraries
*/ */
gulp.task("scripts-libs", function () { gulp.task('scripts-libs', function () {
return merge( return merge(
gulp.src(paths.scripts.combine) gulp.src(paths.scripts.combine)
.pipe(concat('libs.js', {newLine: ';\n'})) .pipe(concat('libs.js', {newLine: ';\n'}))
.pipe(uglify({ mangle: false })) .pipe(uglify({ mangle: false }))
.pipe(gulp.dest("./assets/js")), .pipe(gulp.dest('./assets/js')),
gulp.src(paths.scripts.ace) 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 * 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) return gulp.src(paths.scripts.compile)
.pipe(plumber()) .pipe(plumber())
.pipe(include({ extensions: "js" })) .pipe(include({ extensions: 'js' }))
.pipe(babel()) .pipe(babel())
.pipe(uglify()) .pipe(uglify())
.pipe(plumber.stop()) .pipe(plumber.stop())
.pipe(gulp.dest("./assets/js")); .pipe(gulp.dest('./assets/js'))
})
});
/** /**
* TASK - Process all css processes * TASK - Process all css processes
*/ */
gulp.task("css", ['css-libs', 'css-app']); gulp.task('css', ['css-libs', 'css-app'])
/** /**
* TASK - Combine css libraries * TASK - Combine css libraries
*/ */
gulp.task("css-libs", function () { gulp.task('css-libs', function () {
return gulp.src(paths.css.combine) return gulp.src(paths.css.combine)
.pipe(plumber()) .pipe(plumber())
.pipe(concat('libs.css')) .pipe(concat('libs.css'))
.pipe(cleanCSS({ keepSpecialComments: 0 })) .pipe(cleanCSS({ keepSpecialComments: 0 }))
.pipe(plumber.stop()) .pipe(plumber.stop())
.pipe(gulp.dest("./assets/css")); .pipe(gulp.dest('./assets/css'))
}); })
/** /**
* TASK - Combine app css * TASK - Combine app css
*/ */
gulp.task("css-app", function () { gulp.task('css-app', function () {
return gulp.src(paths.css.compile) return gulp.src(paths.css.compile)
.pipe(plumber()) .pipe(plumber())
.pipe(sass.sync({ includePaths: paths.css.includes })) .pipe(sass.sync({ includePaths: paths.css.includes }))
.pipe(cleanCSS({ keepSpecialComments: 0 })) .pipe(cleanCSS({ keepSpecialComments: 0 }))
.pipe(plumber.stop()) .pipe(plumber.stop())
.pipe(gulp.dest("./assets/css")); .pipe(gulp.dest('./assets/css'))
}); })
/** /**
* TASK - Copy web fonts * TASK - Copy web fonts
*/ */
gulp.task("fonts", function () { gulp.task('fonts', function () {
return gulp.src(paths.fonts) return gulp.src(paths.fonts)
.pipe(gulp.dest("./assets/fonts")); .pipe(gulp.dest('./assets/fonts'))
}); })
/** /**
* TASK - Start dev watchers * TASK - Start dev watchers
*/ */
gulp.task('watch', function() { gulp.task('watch', function () {
return merge( return merge(
watch(paths.scripts.watch, {base: './'}, function() { return gulp.start('scripts-app'); }), watch(paths.scripts.watch, {base: './'}, function () { return gulp.start('scripts-app') }),
watch(paths.css.watch, {base: './'}, function() { return gulp.start('css-app'); }) watch(paths.css.watch, {base: './'}, function () { return gulp.start('css-app') })
); )
}); })
/** /**
* TASK - Starts development server with watchers * 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.fonts.pop(); gulp.task('dev', function () {
paths.fonts.push('../core/core-client/fonts/**/*'); paths.css.includes.pop()
paths.css.includes.push('../core')
return run('default'); paths.fonts.pop()
paths.fonts.push('../core/core-client/fonts/**/*')
}); return run('default')
})
/** /**
* TASK - Creates deployment packages * TASK - Creates deployment packages
*/ */
gulp.task('deploy', ['scripts', 'css', 'fonts'], function() { gulp.task('deploy', ['scripts', 'css', 'fonts'], function () {
var zipStream = gulp.src(paths.deploy) var zipStream = gulp.src(paths.deploy)
.pipe(zip('wiki-js.zip')) .pipe(zip('wiki-js.zip'))
.pipe(gulp.dest('dist')); .pipe(gulp.dest('dist'))
var targzStream = gulp.src(paths.deploy) var targzStream = gulp.src(paths.deploy)
.pipe(tar('wiki-js.tar')) .pipe(tar('wiki-js.tar'))
.pipe(gzip()) .pipe(gzip())
.pipe(gulp.dest('dist')); .pipe(gulp.dest('dist'))
return merge(zipStream, targzStream); return merge(zipStream, targzStream)
}); })
"use strict"; 'use strict'
var Git = require("git-wrapper2-promise"), const Git = require('git-wrapper2-promise')
Promise = require('bluebird'), const Promise = require('bluebird')
path = require('path'), const path = require('path')
os = require('os'), const fs = Promise.promisifyAll(require('fs'))
fs = Promise.promisifyAll(require("fs")), const _ = require('lodash')
moment = require('moment'), const URL = require('url')
_ = require('lodash'),
URL = require('url');
/** /**
* Git Model * Git Model
...@@ -36,29 +34,27 @@ module.exports = { ...@@ -36,29 +34,27 @@ module.exports = {
* *
* @return {Object} Git model instance * @return {Object} Git model instance
*/ */
init() { init () {
let self = this
let self = this; // -> Build repository path
//-> Build repository path if (_.isEmpty(appconfig.paths.repo)) {
self._repo.path = path.join(ROOTPATH, 'repo')
if(_.isEmpty(appconfig.paths.repo)) {
self._repo.path = path.join(ROOTPATH, 'repo');
} else { } else {
self._repo.path = appconfig.paths.repo; self._repo.path = appconfig.paths.repo
} }
//-> Initialize repository // -> Initialize repository
self.onReady = self._initRepo(appconfig); self.onReady = self._initRepo(appconfig)
// Define signature // Define signature
self._signature.name = appconfig.git.signature.name || 'Wiki'; self._signature.name = appconfig.git.signature.name || 'Wiki'
self._signature.email = appconfig.git.signature.email || 'user@example.com'; self._signature.email = appconfig.git.signature.email || 'user@example.com'
return self;
return self
}, },
/** /**
...@@ -67,61 +63,55 @@ module.exports = { ...@@ -67,61 +63,55 @@ module.exports = {
* @param {Object} appconfig The application config * @param {Object} appconfig The application config
* @return {Object} Promise * @return {Object} Promise
*/ */
_initRepo(appconfig) { _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 // -> Check if path is accessible
return fs.mkdirAsync(self._repo.path).catch((err) => { return fs.mkdirAsync(self._repo.path).catch((err) => {
if(err.code !== 'EEXIST') { 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(() => { }).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
//-> Check if path already contains a git working folder
return self._git.isRepo().then((isRepo) => { return self._git.isRepo().then((isRepo) => {
self._repo.exists = isRepo; self._repo.exists = isRepo
return (!isRepo) ? self._git.exec('init') : true; return (!isRepo) ? self._git.exec('init') : true
}).catch((err) => { }).catch((err) => { // eslint-disable-line handle-callback-err
self._repo.exists = false; self._repo.exists = false
}); })
}).then(() => { }).then(() => {
// Initialize remote // Initialize remote
let urlObj = URL.parse(appconfig.git.url); let urlObj = URL.parse(appconfig.git.url)
urlObj.auth = appconfig.git.auth.username + ((appconfig.git.auth.type !== 'ssh') ? ':' + appconfig.git.auth.password : ''); urlObj.auth = appconfig.git.auth.username + ((appconfig.git.auth.type !== 'ssh') ? ':' + appconfig.git.auth.password : '')
self._url = URL.format(urlObj); self._url = URL.format(urlObj)
return self._git.exec('remote', 'show').then((cProc) => { return self._git.exec('remote', 'show').then((cProc) => {
let out = cProc.stdout.toString(); let out = cProc.stdout.toString()
if(_.includes(out, 'origin')) { if (_.includes(out, 'origin')) {
return true; return true
} else { } else {
return Promise.join( return Promise.join(
self._git.exec('config', ['--local', 'user.name', self._signature.name]), self._git.exec('config', ['--local', 'user.name', self._signature.name]),
self._git.exec('config', ['--local', 'user.email', self._signature.email]) self._git.exec('config', ['--local', 'user.email', self._signature.email])
).then(() => { ).then(() => {
return self._git.exec('remote', ['add', 'origin', self._url]); return self._git.exec('remote', ['add', 'origin', self._url])
}); })
} }
}); })
}).catch((err) => { }).catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Git remote error!'); winston.error('[' + PROCNAME + '][GIT] Git remote error!')
throw err; throw err
}).then(() => { }).then(() => {
winston.info('[' + PROCNAME + '][GIT] Git repository is OK.'); winston.info('[' + PROCNAME + '][GIT] Git repository is OK.')
return true; return true
}); })
}, },
/** /**
...@@ -129,10 +119,8 @@ module.exports = { ...@@ -129,10 +119,8 @@ module.exports = {
* *
* @return {String} The repo path. * @return {String} The repo path.
*/ */
getRepoPath() { getRepoPath () {
return this._repo.path || path.join(ROOTPATH, 'repo')
return this._repo.path || path.join(ROOTPATH, 'repo');
}, },
/** /**
...@@ -140,50 +128,41 @@ module.exports = { ...@@ -140,50 +128,41 @@ module.exports = {
* *
* @return {Promise} Resolve on sync success * @return {Promise} Resolve on sync success
*/ */
resync() { resync () {
let self = this
let self = this;
// Fetch // 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) => { return self._git.pull('origin', self._repo.branch).then((cProc) => {
winston.info('[' + PROCNAME + '][GIT] Pull completed.'); winston.info('[' + PROCNAME + '][GIT] Pull completed.')
}) })
.catch((err) => { .catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Unable to fetch from git origin!'); winston.error('[' + PROCNAME + '][GIT] Unable to fetch from git origin!')
throw err; throw err
}) })
.then(() => { .then(() => {
// Check for changes // Check for changes
return self._git.exec('log', 'origin/' + self._repo.branch + '..HEAD').then((cProc) => { 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')) { 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 self._git.push('origin', self._repo.branch).then(() => {
return winston.info('[' + PROCNAME + '][GIT] Push completed.'); return winston.info('[' + PROCNAME + '][GIT] Push completed.')
}); })
} else { } 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) => { .catch((err) => {
winston.error('[' + PROCNAME + '][GIT] Unable to push changes to remote!'); winston.error('[' + PROCNAME + '][GIT] Unable to push changes to remote!')
throw err; throw err
}); })
}, },
/** /**
...@@ -192,24 +171,22 @@ module.exports = { ...@@ -192,24 +171,22 @@ module.exports = {
* @param {String} entryPath The entry path * @param {String} entryPath The entry path
* @return {Promise} Resolve on commit success * @return {Promise} Resolve on commit success
*/ */
commitDocument(entryPath) { commitDocument (entryPath) {
let self = this
let self = this; let gitFilePath = entryPath + '.md'
let gitFilePath = entryPath + '.md'; let commitMsg = ''
let commitMsg = '';
return self._git.exec('ls-files', gitFilePath).then((cProc) => { return self._git.exec('ls-files', gitFilePath).then((cProc) => {
let out = cProc.stdout.toString(); let out = cProc.stdout.toString()
return _.includes(out, gitFilePath); return _.includes(out, gitFilePath)
}).then((isTracked) => { }).then((isTracked) => {
commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath; commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath
return self._git.add(gitFilePath); return self._git.add(gitFilePath)
}).then(() => { }).then(() => {
return self._git.commit(commitMsg).catch((err) => { 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 }
}); })
}); })
}, },
/** /**
...@@ -219,21 +196,19 @@ module.exports = { ...@@ -219,21 +196,19 @@ module.exports = {
* @param {String} newEntryPath The new entry path * @param {String} newEntryPath The new entry path
* @return {Promise<Boolean>} Resolve on success * @return {Promise<Boolean>} Resolve on success
*/ */
moveDocument(entryPath, newEntryPath) { moveDocument (entryPath, newEntryPath) {
let self = this
let self = this; let gitFilePath = entryPath + '.md'
let gitFilePath = entryPath + '.md'; let gitNewFilePath = newEntryPath + '.md'
let gitNewFilePath = newEntryPath + '.md';
return self._git.exec('mv', [gitFilePath, gitNewFilePath]).then((cProc) => { return self._git.exec('mv', [gitFilePath, gitNewFilePath]).then((cProc) => {
let out = cProc.stdout.toString(); let out = cProc.stdout.toString()
if(_.includes(out, 'fatal')) { if (_.includes(out, 'fatal')) {
let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ','))); let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ',')))
throw new Error(errorMsg); throw new Error(errorMsg)
} }
return true; return true
}); })
}, },
/** /**
...@@ -242,17 +217,15 @@ module.exports = { ...@@ -242,17 +217,15 @@ module.exports = {
* @param {String} msg The commit message * @param {String} msg The commit message
* @return {Promise} Resolve on commit success * @return {Promise} Resolve on commit success
*/ */
commitUploads(msg) { commitUploads (msg) {
let self = this
let self = this; msg = msg || 'Uploads repository sync'
msg = msg || "Uploads repository sync";
return self._git.add('uploads').then(() => { return self._git.add('uploads').then(() => {
return self._git.commit(msg).catch((err) => { 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 }
}); })
}); })
} }
}; }
\ No newline at end of file
"use strict"; 'use strict'
const crypto = require('crypto'); const crypto = require('crypto')
/** /**
* Internal Authentication * Internal Authentication
...@@ -9,24 +9,18 @@ module.exports = { ...@@ -9,24 +9,18 @@ module.exports = {
_curKey: false, _curKey: false,
init(inKey) { init (inKey) {
this._curKey = inKey
this._curKey = inKey;
return this;
return this
}, },
generateKey() { generateKey () {
return crypto.randomBytes(20).toString('hex')
return crypto.randomBytes(20).toString('hex');
}, },
validateKey(inKey) { validateKey (inKey) {
return inKey === this._curKey
return inKey === this._curKey;
} }
}; }
\ No newline at end of file
"use strict"; 'use strict'
var path = require('path'), const path = require('path')
Promise = require('bluebird'), const Promise = require('bluebird')
fs = Promise.promisifyAll(require('fs-extra')), const fs = Promise.promisifyAll(require('fs-extra'))
multer = require('multer'), const multer = require('multer')
os = require('os'), const os = require('os')
_ = require('lodash'); const _ = require('lodash')
/** /**
* Local Data Storage * Local Data Storage
...@@ -22,16 +22,14 @@ module.exports = { ...@@ -22,16 +22,14 @@ module.exports = {
* *
* @return {Object} Local Data Storage model instance * @return {Object} Local Data Storage model instance
*/ */
init() { 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.createBaseDirectories(appconfig)
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs'); this.initMulter(appconfig)
this.createBaseDirectories(appconfig);
this.initMulter(appconfig);
return this;
return this
}, },
/** /**
...@@ -40,61 +38,57 @@ module.exports = { ...@@ -40,61 +38,57 @@ module.exports = {
* @param {Object} appconfig The application config * @param {Object} appconfig The application config
* @return {boolean} Void * @return {boolean} Void
*/ */
initMulter(appconfig) { initMulter (appconfig) {
let maxFileSizes = { let maxFileSizes = {
img: appconfig.uploads.maxImageFileSize * 1024 * 1024, img: appconfig.uploads.maxImageFileSize * 1024 * 1024,
file: appconfig.uploads.maxOtherFileSize * 1024 * 1024 file: appconfig.uploads.maxOtherFileSize * 1024 * 1024
}; }
//-> IMAGES // -> IMAGES
this.uploadImgHandler = multer({ this.uploadImgHandler = multer({
storage: multer.diskStorage({ storage: multer.diskStorage({
destination: (req, f, cb) => { 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) => { fileFilter: (req, f, cb) => {
// -> Check filesize
//-> Check filesize if (f.size > maxFileSizes.img) {
return cb(null, false)
if(f.size > maxFileSizes.img) {
return cb(null, false);
} }
//-> Check MIME type (quick check only) // -> Check MIME type (quick check only)
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) { 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 // -> FILES
this.uploadFileHandler = multer({ this.uploadFileHandler = multer({
storage: multer.diskStorage({ storage: multer.diskStorage({
destination: (req, f, cb) => { 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) => { fileFilter: (req, f, cb) => {
// -> Check filesize
//-> Check filesize if (f.size > maxFileSizes.file) {
return cb(null, false)
if(f.size > maxFileSizes.file) {
return cb(null, false);
} }
cb(null, true); cb(null, true)
} }
}).array('binfile', 20); }).array('binfile', 20)
return true;
return true
}, },
/** /**
...@@ -103,35 +97,32 @@ module.exports = { ...@@ -103,35 +97,32 @@ module.exports = {
* @param {Object} appconfig The application config * @param {Object} appconfig The application config
* @return {Void} Void * @return {Void} Void
*/ */
createBaseDirectories(appconfig) { createBaseDirectories (appconfig) {
winston.info('[SERVER] Checking data directories...')
winston.info('[SERVER] Checking data directories...');
try { try {
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data)); 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, './cache'))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs')); 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, './temp-upload'))
if(os.type() !== 'Windows_NT') { 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))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads')); fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'))
if(os.type() !== 'Windows_NT') { 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) { } catch (err) {
winston.error(err); winston.error(err)
} }
winston.info('[SERVER] Data and Repository directories are OK.'); winston.info('[SERVER] Data and Repository directories are OK.')
return;
return
}, },
/** /**
...@@ -139,8 +130,8 @@ module.exports = { ...@@ -139,8 +130,8 @@ module.exports = {
* *
* @return {String} The uploads path. * @return {String} The uploads path.
*/ */
getUploadsPath() { getUploadsPath () {
return this._uploadsPath; return this._uploadsPath
}, },
/** /**
...@@ -148,8 +139,8 @@ module.exports = { ...@@ -148,8 +139,8 @@ module.exports = {
* *
* @return {String} The thumbs path. * @return {String} The thumbs path.
*/ */
getThumbsPath() { getThumbsPath () {
return this._uploadsThumbsPath; return this._uploadsThumbsPath
}, },
/** /**
...@@ -160,28 +151,26 @@ module.exports = { ...@@ -160,28 +151,26 @@ module.exports = {
* @param {boolean} isImage Indicates if image * @param {boolean} isImage Indicates if image
* @return {Promise<String>} Promise of the accepted filename * @return {Promise<String>} Promise of the accepted filename
*/ */
validateUploadsFilename(f, fld, isImage) { validateUploadsFilename (f, fld, isImage) {
let fObj = path.parse(f)
let fObj = path.parse(f); let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9-]+/g, '')
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, ''); let fext = _.toLower(fObj.ext)
let fext = _.toLower(fObj.ext);
if(isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) { if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
fext = '.png'; fext = '.png'
} }
f = fname + fext; f = fname + fext
let fpath = path.resolve(this._uploadsPath, fld, f); let fpath = path.resolve(this._uploadsPath, fld, f)
return fs.statAsync(fpath).then((s) => { return fs.statAsync(fpath).then((s) => {
throw new Error('File ' + f + ' already exists.'); throw new Error('File ' + f + ' already exists.')
}).catch((err) => { }).catch((err) => {
if(err.code === 'ENOENT') { if (err.code === 'ENOENT') {
return f; return f
}
throw err
})
} }
throw err;
});
},
}; }
\ No newline at end of file
"use strict"; 'use strict'
var path = require('path'), const path = require('path')
Promise = require('bluebird'), const Promise = require('bluebird')
fs = Promise.promisifyAll(require('fs-extra')), const fs = Promise.promisifyAll(require('fs-extra'))
readChunk = require('read-chunk'), const readChunk = require('read-chunk')
fileType = require('file-type'), const fileType = require('file-type')
mime = require('mime-types'), const mime = require('mime-types')
farmhash = require('farmhash'), const farmhash = require('farmhash')
moment = require('moment'), const chokidar = require('chokidar')
chokidar = require('chokidar'), const sharp = require('sharp')
sharp = require('sharp'), const _ = require('lodash')
_ = require('lodash');
/** /**
* Uploads - Agent * Uploads - Agent
...@@ -27,18 +26,16 @@ module.exports = { ...@@ -27,18 +26,16 @@ module.exports = {
* *
* @return {Object} Uploads model instance * @return {Object} Uploads model instance
*/ */
init() { 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. // Disable Sharp cache, as it cause file locks issues when deleting uploads.
sharp.cache(false); sharp.cache(false)
return self;
return self
}, },
/** /**
...@@ -46,9 +43,8 @@ module.exports = { ...@@ -46,9 +43,8 @@ module.exports = {
* *
* @return {Void} Void * @return {Void} Void
*/ */
watch() { watch () {
let self = this
let self = this;
self._watcher = chokidar.watch(self._uploadsPath, { self._watcher = chokidar.watch(self._uploadsPath, {
persistent: true, persistent: true,
...@@ -56,30 +52,24 @@ module.exports = { ...@@ -56,30 +52,24 @@ module.exports = {
cwd: self._uploadsPath, cwd: self._uploadsPath,
depth: 1, depth: 1,
awaitWriteFinish: true awaitWriteFinish: true
}); })
//-> Add new upload file // -> Add new upload file
self._watcher.on('add', (p) => { 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 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(() => { }).then(() => {
return git.commitUploads('Uploaded ' + p); return git.commitUploads('Uploaded ' + p)
}); })
})
});
//-> Remove upload file // -> Remove upload file
self._watcher.on('unlink', (p) => { self._watcher.on('unlink', (p) => {
return git.commitUploads('Deleted/Renamed ' + p)
let pInfo = self.parseUploadsRelPath(p); })
return git.commitUploads('Deleted/Renamed ' + p);
});
}, },
/** /**
...@@ -87,20 +77,17 @@ module.exports = { ...@@ -87,20 +77,17 @@ module.exports = {
* *
* @return {Promise<Void>} Promise of the scan operation * @return {Promise<Void>} Promise of the scan operation
*/ */
initialScan() { initialScan () {
let self = this
let self = this;
return fs.readdirAsync(self._uploadsPath).then((ls) => { return fs.readdirAsync(self._uploadsPath).then((ls) => {
// Get all folders // Get all folders
return Promise.map(ls, (f) => { return Promise.map(ls, (f) => {
return fs.statAsync(path.join(self._uploadsPath, f)).then((s) => { return { filename: f, stat: s }; }); return fs.statAsync(path.join(self._uploadsPath, f)).then((s) => { return { filename: f, stat: s } })
}).filter((s) => { return s.stat.isDirectory(); }).then((arrDirs) => { }).filter((s) => { return s.stat.isDirectory() }).then((arrDirs) => {
let folderNames = _.map(arrDirs, 'filename')
let folderNames = _.map(arrDirs, 'filename'); folderNames.unshift('')
folderNames.unshift('');
// Add folders to DB // Add folders to DB
...@@ -109,53 +96,43 @@ module.exports = { ...@@ -109,53 +96,43 @@ module.exports = {
return { return {
_id: 'f:' + f, _id: 'f:' + f,
name: f name: f
}; }
})); }))
}).then(() => { }).then(() => {
// Travel each directory and scan files // Travel each directory and scan files
let allFiles = []; let allFiles = []
return Promise.map(folderNames, (fldName) => { 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 fs.readdirAsync(fldPath).then((fList) => {
return Promise.map(fList, (f) => { return Promise.map(fList, (f) => {
return upl.processFile(fldName, f).then((mData) => { return upl.processFile(fldName, f).then((mData) => {
if(mData) { if (mData) {
allFiles.push(mData); allFiles.push(mData)
} }
return true; return true
}); })
}, {concurrency: 3}); }, {concurrency: 3})
}); })
}, {concurrency: 1}).finally(() => { }, {concurrency: 1}).finally(() => {
// Add files to DB // Add files to DB
return db.UplFile.remove({}).then(() => { return db.UplFile.remove({}).then(() => {
if(_.isArray(allFiles) && allFiles.length > 0) { if (_.isArray(allFiles) && allFiles.length > 0) {
return db.UplFile.insertMany(allFiles); return db.UplFile.insertMany(allFiles)
} else { } else {
return true; return true
} }
}); })
})
}); })
})
});
});
}).then(() => { }).then(() => {
// Watch for new changes // Watch for new changes
return upl.watch(); return upl.watch()
})
});
}, },
/** /**
...@@ -164,14 +141,12 @@ module.exports = { ...@@ -164,14 +141,12 @@ module.exports = {
* @param {String} f Relative Uploads path * @param {String} f Relative Uploads path
* @return {Object} Parsed path (folder and filename) * @return {Object} Parsed path (folder and filename)
*/ */
parseUploadsRelPath(f) { parseUploadsRelPath (f) {
let fObj = path.parse(f)
let fObj = path.parse(f);
return { return {
folder: fObj.dir, folder: fObj.dir,
filename: fObj.base filename: fObj.base
}; }
}, },
/** /**
...@@ -181,36 +156,33 @@ module.exports = { ...@@ -181,36 +156,33 @@ module.exports = {
* @param {String} f The filename * @param {String} f The filename
* @return {Promise<Object>} Promise of the file metadata * @return {Promise<Object>} Promise of the file metadata
*/ */
processFile(fldName, f) { processFile (fldName, f) {
let self = this
let self = this; let fldPath = path.join(self._uploadsPath, fldName)
let fPath = path.join(fldPath, f)
let fldPath = path.join(self._uploadsPath, fldName); let fPathObj = path.parse(fPath)
let fPath = path.join(fldPath, f); let fUid = farmhash.fingerprint32(fldName + '/' + f)
let fPathObj = path.parse(fPath);
let fUid = farmhash.fingerprint32(fldName + '/' + f);
return fs.statAsync(fPath).then((s) => { return fs.statAsync(fPath).then((s) => {
if (!s.isFile()) { return false }
if(!s.isFile()) { return false; }
// Get MIME info // Get MIME info
let mimeInfo = fileType(readChunk.sync(fPath, 0, 262)); let mimeInfo = fileType(readChunk.sync(fPath, 0, 262))
if(_.isNil(mimeInfo)) { if (_.isNil(mimeInfo)) {
mimeInfo = { mimeInfo = {
mime: mime.lookup(fPathObj.ext) || 'application/octet-stream' mime: mime.lookup(fPathObj.ext) || 'application/octet-stream'
}; }
} }
// Images // Images
if(s.size < 3145728) { // ignore files larger than 3MB if (s.size < 3145728) { // ignore files larger than 3MB
if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { if (_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return self.getImageMetadata(fPath).then((mImgData) => { return self.getImageMetadata(fPath).then((mImgData) => {
let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png'))
let cacheThumbnailPath = path.parse(path.join(self._uploadsThumbsPath, fUid + '.png')); let cacheThumbnailPathStr = path.format(cacheThumbnailPath)
let cacheThumbnailPathStr = path.format(cacheThumbnailPath);
let mData = { let mData = {
_id: fUid, _id: fUid,
...@@ -221,23 +193,20 @@ module.exports = { ...@@ -221,23 +193,20 @@ module.exports = {
filename: f, filename: f,
basename: fPathObj.name, basename: fPathObj.name,
filesize: s.size filesize: s.size
}; }
// Generate thumbnail // Generate thumbnail
return fs.statAsync(cacheThumbnailPathStr).then((st) => { return fs.statAsync(cacheThumbnailPathStr).then((st) => {
return st.isFile(); return st.isFile()
}).catch((err) => { }).catch((err) => { // eslint-disable-line handle-callback-err
return false; return false
}).then((thumbExists) => { }).then((thumbExists) => {
return (thumbExists) ? mData : fs.ensureDirAsync(cacheThumbnailPath.dir).then(() => { return (thumbExists) ? mData : fs.ensureDirAsync(cacheThumbnailPath.dir).then(() => {
return self.generateThumbnail(fPath, cacheThumbnailPathStr); return self.generateThumbnail(fPath, cacheThumbnailPathStr)
}).return(mData); }).return(mData)
})
}); })
});
} }
} }
...@@ -251,10 +220,8 @@ module.exports = { ...@@ -251,10 +220,8 @@ module.exports = {
filename: f, filename: f,
basename: fPathObj.name, basename: fPathObj.name,
filesize: s.size filesize: s.size
}; }
})
});
}, },
/** /**
...@@ -264,17 +231,15 @@ module.exports = { ...@@ -264,17 +231,15 @@ module.exports = {
* @param {String} destPath The destination path * @param {String} destPath The destination path
* @return {Promise<Object>} Promise returning the resized image info * @return {Promise<Object>} Promise returning the resized image info
*/ */
generateThumbnail(sourcePath, destPath) { generateThumbnail (sourcePath, destPath) {
return sharp(sourcePath) return sharp(sourcePath)
.withoutEnlargement() .withoutEnlargement()
.resize(150,150) .resize(150, 150)
.background('white') .background('white')
.embed() .embed()
.flatten() .flatten()
.toFormat('png') .toFormat('png')
.toFile(destPath); .toFile(destPath)
}, },
/** /**
...@@ -283,10 +248,8 @@ module.exports = { ...@@ -283,10 +248,8 @@ module.exports = {
* @param {String} sourcePath The source path * @param {String} sourcePath The source path
* @return {Object} The image metadata. * @return {Object} The image metadata.
*/ */
getImageMetadata(sourcePath) { getImageMetadata (sourcePath) {
return sharp(sourcePath).metadata()
return sharp(sourcePath).metadata();
} }
}; }
\ No newline at end of file
"use strict"; 'use strict'
var Promise = require('bluebird'), const moment = require('moment-timezone')
moment = require('moment-timezone');
/** /**
* Authentication middleware * Authentication middleware
...@@ -12,29 +11,27 @@ var Promise = require('bluebird'), ...@@ -12,29 +11,27 @@ var Promise = require('bluebird'),
* @return {any} void * @return {any} void
*/ */
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
// Is user authenticated ? // Is user authenticated ?
if (!req.isAuthenticated()) { if (!req.isAuthenticated()) {
return res.redirect('/login'); return res.redirect('/login')
} }
// Check permissions // Check permissions
if(!rights.check(req, 'read')) { if (!rights.check(req, 'read')) {
return res.render('error-forbidden'); return res.render('error-forbidden')
} }
// Set i18n locale // Set i18n locale
req.i18n.changeLanguage(req.user.lang); req.i18n.changeLanguage(req.user.lang)
res.locals.userMoment = moment; res.locals.userMoment = moment
res.locals.userMoment.locale(req.user.lang); res.locals.userMoment.locale(req.user.lang)
// Expose user data // Expose user data
res.locals.user = req.user; res.locals.user = req.user
return next();
}; return next()
\ No newline at end of file }
"use strict"; 'use strict'
/** /**
* Flash middleware * Flash middleware
...@@ -9,9 +9,7 @@ ...@@ -9,9 +9,7 @@
* @return {any} void * @return {any} void
*/ */
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
res.locals.appflash = req.flash('alert')
res.locals.appflash = req.flash('alert'); next()
}
next();
};
\ No newline at end of file
'use strict'
/** /**
* Security Middleware * Security Middleware
* *
...@@ -6,23 +8,21 @@ ...@@ -6,23 +8,21 @@
* @param {Function} next next callback function * @param {Function} next next callback function
* @return {any} void * @return {any} void
*/ */
module.exports = function(req, res, next) { module.exports = function (req, res, next) {
// -> Disable X-Powered-By
//-> Disable X-Powered-By app.disable('x-powered-by')
app.disable('x-powered-by');
//-> Disable Frame Embedding
res.set('X-Frame-Options', 'deny');
//-> Re-enable XSS Fitler if disabled // -> Disable Frame Embedding
res.set('X-XSS-Protection', '1; mode=block'); res.set('X-Frame-Options', 'deny')
//-> Disable MIME-sniffing // -> Re-enable XSS Fitler if disabled
res.set('X-Content-Type-Options', 'nosniff'); res.set('X-XSS-Protection', '1; mode=block')
//-> Disable IE Compatibility Mode // -> Disable MIME-sniffing
res.set('X-UA-Compatible', 'IE=edge'); res.set('X-Content-Type-Options', 'nosniff')
return next(); // -> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge')
}; return next()
\ No newline at end of file }
"use strict"; 'use strict'
/** /**
* BruteForce schema * BruteForce schema
...@@ -13,6 +13,6 @@ var bruteForceSchema = Mongoose.Schema({ ...@@ -13,6 +13,6 @@ var bruteForceSchema = Mongoose.Schema({
firstRequest: Date firstRequest: Date
}, },
expires: { type: Date, index: { expires: '1d' } } expires: { type: Date, index: { expires: '1d' } }
}); })
module.exports = Mongoose.model('Bruteforce', bruteForceSchema); module.exports = Mongoose.model('Bruteforce', bruteForceSchema)
\ No newline at end of file
"use strict"; 'use strict'
const Promise = require('bluebird'),
_ = require('lodash');
/** /**
* Entry schema * Entry schema
...@@ -31,9 +28,9 @@ var entrySchema = Mongoose.Schema({ ...@@ -31,9 +28,9 @@ var entrySchema = Mongoose.Schema({
} }
}, },
{ {
timestamps: {} timestamps: {}
}); })
entrySchema.index({ entrySchema.index({
_id: 'text', _id: 'text',
...@@ -48,6 +45,6 @@ entrySchema.index({ ...@@ -48,6 +45,6 @@ entrySchema.index({
content: 1 content: 1
}, },
name: 'EntriesTextIndex' name: 'EntriesTextIndex'
}); })
module.exports = Mongoose.model('Entry', entrySchema); module.exports = Mongoose.model('Entry', entrySchema)
\ No newline at end of file
"use strict"; 'use strict'
const Promise = require('bluebird'),
_ = require('lodash');
/** /**
* Upload File schema * Upload File schema
...@@ -42,9 +39,6 @@ var uplFileSchema = Mongoose.Schema({ ...@@ -42,9 +39,6 @@ var uplFileSchema = Mongoose.Schema({
required: true required: true
} }
}, }, { timestamps: {} })
{
timestamps: {}
});
module.exports = Mongoose.model('UplFile', uplFileSchema); module.exports = Mongoose.model('UplFile', uplFileSchema)
\ No newline at end of file
"use strict"; 'use strict'
const Promise = require('bluebird'),
_ = require('lodash');
/** /**
* Upload Folder schema * Upload Folder schema
...@@ -17,9 +14,6 @@ var uplFolderSchema = Mongoose.Schema({ ...@@ -17,9 +14,6 @@ var uplFolderSchema = Mongoose.Schema({
index: true index: true
} }
}, }, { timestamps: {} })
{
timestamps: {}
});
module.exports = Mongoose.model('UplFolder', uplFolderSchema); module.exports = Mongoose.model('UplFolder', uplFolderSchema)
\ No newline at end of file
"use strict"; 'use strict'
const Promise = require('bluebird'), const Promise = require('bluebird')
bcrypt = require('bcryptjs-then'), const bcrypt = require('bcryptjs-then')
_ = require('lodash'); const _ = require('lodash')
/** /**
* Region schema * Region schema
...@@ -41,21 +41,17 @@ var userSchema = Mongoose.Schema({ ...@@ -41,21 +41,17 @@ var userSchema = Mongoose.Schema({
deny: Boolean deny: Boolean
}] }]
}, }, { timestamps: {} })
{
timestamps: {}
});
userSchema.statics.processProfile = (profile) => { userSchema.statics.processProfile = (profile) => {
let primaryEmail = ''
let primaryEmail = ''; if (_.isArray(profile.emails)) {
if(_.isArray(profile.emails)) { let e = _.find(profile.emails, ['primary', true])
let e = _.find(profile.emails, ['primary', true]); primaryEmail = (e) ? e.value : _.first(profile.emails).value
primaryEmail = (e) ? e.value : _.first(profile.emails).value; } else if (_.isString(profile.email) && profile.email.length > 5) {
} else if(_.isString(profile.email) && profile.email.length > 5) { primaryEmail = profile.email
primaryEmail = profile.email;
} else { } else {
return Promise.reject(new Error('Invalid User Email')); return Promise.reject(new Error('Invalid User Email'))
} }
return db.User.findOneAndUpdate({ return db.User.findOneAndUpdate({
...@@ -70,19 +66,18 @@ userSchema.statics.processProfile = (profile) => { ...@@ -70,19 +66,18 @@ userSchema.statics.processProfile = (profile) => {
new: true, new: true,
upsert: true upsert: true
}).then((user) => { }).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) => { userSchema.statics.hashPassword = (rawPwd) => {
return bcrypt.hash(rawPwd); return bcrypt.hash(rawPwd)
}; }
userSchema.methods.validatePassword = function(rawPwd) { userSchema.methods.validatePassword = function (rawPwd) {
return bcrypt.compare(rawPwd, this.password).then((isValid) => { 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)
\ No newline at end of file
...@@ -129,5 +129,35 @@ ...@@ -129,5 +129,35 @@
"twemoji-awesome": "^1.0.4", "twemoji-awesome": "^1.0.4",
"vue": "^2.1.10" "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 "snyk": true
} }
'use strict'
// TODO
"use strict";
let path = require('path'),
fs = require('fs');
// ========================================
// Load global modules
// ========================================
global._ = require('lodash');
global.winston = require('winston');
\ No newline at end of file
...@@ -19,20 +19,15 @@ html ...@@ -19,20 +19,15 @@ html
// CSS // CSS
link(type='text/css', rel='stylesheet', href='/css/libs.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') body(class='is-error')
section.hero.is-warning.is-fullheight
.hero-body
.container .container
a(href='/'): img(src='/favicons/android-icon-96x96.png') a(href='/'): img(src='/favicons/android-icon-96x96.png')
h1.title(style={ 'margin-top': '30px'})= message h1= message
h2.subtitle(style={ 'margin-bottom': '50px'}) Oops, something went wrong h2 Oops, something went wrong
a.button.is-warning.is-inverted(href='/') Go Home a.button.is-amber.is-inverted.is-featured(href='/') Go Home
if error.stack if error.stack
section.section
.container.is-fluid
.content
h3 Detailed debug trail: h3 Detailed debug trail:
pre: code #{error.stack} pre: code #{error.stack}
\ No newline at end of file
...@@ -53,6 +53,11 @@ block content ...@@ -53,6 +53,11 @@ block content
a(href='/admin') a(href='/admin')
i.icon-head i.icon-head
span Account span Account
else
li
a(href='/login')
i.icon-unlock
span Login
aside.stickyscroll(data-margin-top=40) aside.stickyscroll(data-margin-top=40)
.sidebar-label .sidebar-label
i.icon-th-list i.icon-th-list
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment