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
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";
jQuery( document ).ready(function( $ ) {
'use strict'
jQuery(document).ready(function ($) {
// ====================================
// Scroll
// ====================================
$('a').smoothScroll({
speed: 400,
offset: -70
});
$('a').smoothScroll({
speed: 400,
offset: -70
})
var sticky = new Sticky('.stickyscroll');
var sticky = new Sticky('.stickyscroll')
// ====================================
// Notifications
// ====================================
$(window).bind('beforeunload', () => {
$('#notifload').addClass('active');
});
$(document).ajaxSend(() => {
$('#notifload').addClass('active');
}).ajaxComplete(() => {
$('#notifload').removeClass('active');
});
$(window).bind('beforeunload', () => {
$('#notifload').addClass('active')
})
$(document).ajaxSend(() => {
$('#notifload').addClass('active')
}).ajaxComplete(() => {
$('#notifload').removeClass('active')
})
var alerts = new Alerts();
if(alertsData) {
_.forEach(alertsData, (alertRow) => {
alerts.push(alertRow);
});
}
var alerts = new Alerts()
if (alertsData) {
_.forEach(alertsData, (alertRow) => {
alerts.push(alertRow)
})
}
// ====================================
// 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
// ====================================
//=include pages/view.js
//=include pages/create.js
//=include pages/edit.js
//=include pages/source.js
//=include pages/admin.js
});
// =include pages/view.js
// =include pages/create.js
// =include pages/edit.js
// =include pages/source.js
// =include pages/admin.js
})
//=include helpers/form.js
//=include helpers/pages.js
// =include helpers/form.js
// =include helpers/pages.js
//=include components/alerts.js
\ No newline at end of file
// =include components/alerts.js
"use strict";
'use strict'
/**
* Alerts
......@@ -10,25 +10,23 @@ class Alerts {
*
* @class
*/
constructor() {
let self = this;
self.mdl = new Vue({
el: '#alerts',
data: {
children: []
},
methods: {
acknowledge: (uid) => {
self.close(uid);
}
}
});
self.uidNext = 1;
}
constructor () {
let self = this
self.mdl = new Vue({
el: '#alerts',
data: {
children: []
},
methods: {
acknowledge: (uid) => {
self.close(uid)
}
}
})
self.uidNext = 1
}
/**
* Show a new Alert
......@@ -36,29 +34,27 @@ class Alerts {
* @param {Object} options Alert properties
* @return {null} Void
*/
push(options) {
let self = this;
let nAlert = _.defaults(options, {
_uid: self.uidNext,
class: 'info',
message: '---',
sticky: false,
title: '---'
});
push (options) {
let self = this
self.mdl.children.push(nAlert);
let nAlert = _.defaults(options, {
_uid: self.uidNext,
class: 'info',
message: '---',
sticky: false,
title: '---'
})
if(!nAlert.sticky) {
_.delay(() => {
self.close(nAlert._uid);
}, 5000);
}
self.mdl.children.push(nAlert)
self.uidNext++;
if (!nAlert.sticky) {
_.delay(() => {
self.close(nAlert._uid)
}, 5000)
}
}
self.uidNext++
}
/**
* Shorthand method for pushing errors
......@@ -66,14 +62,14 @@ class Alerts {
* @param {String} title The title
* @param {String} message The message
*/
pushError(title, message) {
this.push({
class: 'error',
message,
sticky: false,
title
});
}
pushError (title, message) {
this.push({
class: 'error',
message,
sticky: false,
title
})
}
/**
* Shorthand method for pushing success messages
......@@ -81,35 +77,33 @@ class Alerts {
* @param {String} title The title
* @param {String} message The message
*/
pushSuccess(title, message) {
this.push({
class: 'success',
message,
sticky: false,
title
});
}
pushSuccess (title, message) {
this.push({
class: 'success',
message,
sticky: false,
title
})
}
/**
* Close an alert
*
* @param {Integer} uid The unique ID of the alert
*/
close(uid) {
let self = this;
let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid]);
let nAlert = _.nth(self.mdl.children, nAlertIdx);
if(nAlertIdx >= 0 && nAlert) {
nAlert.class += ' exit';
Vue.set(self.mdl.children, nAlertIdx, nAlert);
_.delay(() => {
self.mdl.children.splice(nAlertIdx, 1);
}, 500);
}
}
}
\ No newline at end of file
close (uid) {
let self = this
let nAlertIdx = _.findIndex(self.mdl.children, ['_uid', uid])
let nAlert = _.nth(self.mdl.children, nAlertIdx)
if (nAlertIdx >= 0 && nAlert) {
nAlert.class += ' exit'
Vue.set(self.mdl.children, nAlertIdx, nAlert)
_.delay(() => {
self.mdl.children.splice(nAlertIdx, 1)
}, 500)
}
}
}
let modelist = ace.require("ace/ext/modelist");
let codeEditor = null;
let modelist = ace.require('ace/ext/modelist')
let codeEditor = null
// ACE - Mode Loader
let modelistLoaded = [];
let modelistLoaded = []
let loadAceMode = (m) => {
return $.ajax({
url: '/js/ace/mode-' + m + '.js',
dataType: "script",
cache: true,
beforeSend: () => {
if(_.includes(modelistLoaded, m)) {
return false;
}
},
success: () => {
modelistLoaded.push(m);
}
});
};
return $.ajax({
url: '/js/ace/mode-' + m + '.js',
dataType: 'script',
cache: true,
beforeSend: () => {
if (_.includes(modelistLoaded, m)) {
return false
}
},
success: () => {
modelistLoaded.push(m)
}
})
}
// Vue Code Block instance
let vueCodeBlock = new Vue({
el: '#modal-editor-codeblock',
data: {
modes: modelist.modesByName,
modeSelected: 'text',
initContent: ''
},
watch: {
modeSelected: (val, oldVal) => {
loadAceMode(val).done(() => {
ace.require("ace/mode/" + val);
codeEditor.getSession().setMode("ace/mode/" + val);
});
}
},
methods: {
open: (ev) => {
el: '#modal-editor-codeblock',
data: {
modes: modelist.modesByName,
modeSelected: 'text',
initContent: ''
},
watch: {
modeSelected: (val, oldVal) => {
loadAceMode(val).done(() => {
ace.require('ace/mode/' + val)
codeEditor.getSession().setMode('ace/mode/' + val)
})
}
},
methods: {
open: (ev) => {
$('#modal-editor-codeblock').addClass('is-active')
$('#modal-editor-codeblock').addClass('is-active');
_.delay(() => {
codeEditor = ace.edit('codeblock-editor')
codeEditor.setTheme('ace/theme/tomorrow_night')
codeEditor.getSession().setMode('ace/mode/' + vueCodeBlock.modeSelected)
codeEditor.setOption('fontSize', '14px')
codeEditor.setOption('hScrollBarAlwaysVisible', false)
codeEditor.setOption('wrap', true)
_.delay(() => {
codeEditor = ace.edit("codeblock-editor");
codeEditor.setTheme("ace/theme/tomorrow_night");
codeEditor.getSession().setMode("ace/mode/" + vueCodeBlock.modeSelected);
codeEditor.setOption('fontSize', '14px');
codeEditor.setOption('hScrollBarAlwaysVisible', false);
codeEditor.setOption('wrap', true);
codeEditor.setValue(vueCodeBlock.initContent)
codeEditor.setValue(vueCodeBlock.initContent);
codeEditor.focus()
codeEditor.renderer.updateFull()
}, 300)
},
cancel: (ev) => {
mdeModalOpenState = false
$('#modal-editor-codeblock').removeClass('is-active')
vueCodeBlock.initContent = ''
},
insertCode: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection')
}
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
codeEditor.focus();
codeEditor.renderer.updateFull();
}, 300);
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-codeblock').removeClass('is-active');
vueCodeBlock.initContent = '';
},
insertCode: (ev) => {
if(mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
}
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n';
mde.codemirror.doc.replaceSelection(codeBlockText);
vueCodeBlock.cancel();
}
}
});
\ No newline at end of file
mde.codemirror.doc.replaceSelection(codeBlockText)
vueCodeBlock.cancel()
}
}
})
const videoRules = {
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
'vimeo': new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
};
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
'vimeo': new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
}
// Vue Video instance
let vueVideo = new Vue({
el: '#modal-editor-video',
data: {
link: ''
},
methods: {
open: (ev) => {
$('#modal-editor-video').addClass('is-active');
$('#modal-editor-video input').focus();
},
cancel: (ev) => {
mdeModalOpenState = false;
$('#modal-editor-video').removeClass('is-active');
vueVideo.link = '';
},
insertVideo: (ev) => {
if(mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
}
el: '#modal-editor-video',
data: {
link: ''
},
methods: {
open: (ev) => {
$('#modal-editor-video').addClass('is-active')
$('#modal-editor-video input').focus()
},
cancel: (ev) => {
mdeModalOpenState = false
$('#modal-editor-video').removeClass('is-active')
vueVideo.link = ''
},
insertVideo: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection')
}
// Guess video type
let videoType = _.findKey(videoRules, (vr) => {
return vr.test(vueVideo.link);
});
if(_.isNil(videoType)) {
videoType = 'video';
}
let videoType = _.findKey(videoRules, (vr) => {
return vr.test(vueVideo.link)
})
if (_.isNil(videoType)) {
videoType = 'video'
}
// Insert video tag
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n';
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
mde.codemirror.doc.replaceSelection(videoText);
vueVideo.cancel();
}
}
});
\ No newline at end of file
mde.codemirror.doc.replaceSelection(videoText)
vueVideo.cancel()
}
}
})
......@@ -3,215 +3,210 @@
// Markdown Editor
// ====================================
if($('#mk-editor').length === 1) {
let mdeModalOpenState = false;
let mdeCurrentEditor = null;
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v));
});
//=include editor-image.js
//=include editor-file.js
//=include editor-video.js
//=include editor-codeblock.js
var mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $("#mk-editor").get(0),
placeholder: 'Enter Markdown formatted content here...',
spellChecker: false,
status: false,
toolbar: [{
name: "bold",
action: SimpleMDE.toggleBold,
className: "icon-bold",
title: "Bold",
},
{
name: "italic",
action: SimpleMDE.toggleItalic,
className: "icon-italic",
title: "Italic",
},
{
name: "strikethrough",
action: SimpleMDE.toggleStrikethrough,
className: "icon-strikethrough",
title: "Strikethrough",
},
'|',
{
name: "heading-1",
action: SimpleMDE.toggleHeading1,
className: "icon-header fa-header-x fa-header-1",
title: "Big Heading",
},
{
name: "heading-2",
action: SimpleMDE.toggleHeading2,
className: "icon-header fa-header-x fa-header-2",
title: "Medium Heading",
},
{
name: "heading-3",
action: SimpleMDE.toggleHeading3,
className: "icon-header fa-header-x fa-header-3",
title: "Small Heading",
},
{
name: "quote",
action: SimpleMDE.toggleBlockquote,
className: "icon-quote-left",
title: "Quote",
},
'|',
{
name: "unordered-list",
action: SimpleMDE.toggleUnorderedList,
className: "icon-th-list",
title: "Bullet List",
},
{
name: "ordered-list",
action: SimpleMDE.toggleOrderedList,
className: "icon-list-ol",
title: "Numbered List",
},
'|',
{
name: "link",
action: (editor) => {
/*if(!mdeModalOpenState) {
if ($('#mk-editor').length === 1) {
let mdeModalOpenState = false
let mdeCurrentEditor = null
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v))
})
// =include editor-image.js
// =include editor-file.js
// =include editor-video.js
// =include editor-codeblock.js
var mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $('#mk-editor').get(0),
placeholder: 'Enter Markdown formatted content here...',
spellChecker: false,
status: false,
toolbar: [{
name: 'bold',
action: SimpleMDE.toggleBold,
className: 'icon-bold',
title: 'Bold'
},
{
name: 'italic',
action: SimpleMDE.toggleItalic,
className: 'icon-italic',
title: 'Italic'
},
{
name: 'strikethrough',
action: SimpleMDE.toggleStrikethrough,
className: 'icon-strikethrough',
title: 'Strikethrough'
},
'|',
{
name: 'heading-1',
action: SimpleMDE.toggleHeading1,
className: 'icon-header fa-header-x fa-header-1',
title: 'Big Heading'
},
{
name: 'heading-2',
action: SimpleMDE.toggleHeading2,
className: 'icon-header fa-header-x fa-header-2',
title: 'Medium Heading'
},
{
name: 'heading-3',
action: SimpleMDE.toggleHeading3,
className: 'icon-header fa-header-x fa-header-3',
title: 'Small Heading'
},
{
name: 'quote',
action: SimpleMDE.toggleBlockquote,
className: 'icon-quote-left',
title: 'Quote'
},
'|',
{
name: 'unordered-list',
action: SimpleMDE.toggleUnorderedList,
className: 'icon-th-list',
title: 'Bullet List'
},
{
name: 'ordered-list',
action: SimpleMDE.toggleOrderedList,
className: 'icon-list-ol',
title: 'Numbered List'
},
'|',
{
name: 'link',
action: (editor) => {
/* if(!mdeModalOpenState) {
mdeModalOpenState = true;
$('#modal-editor-link').slideToggle();
}*/
},
className: "icon-link2",
title: "Insert Link",
},
{
name: "image",
action: (editor) => {
if(!mdeModalOpenState) {
vueImage.open();
}
},
className: "icon-image",
title: "Insert Image",
},
{
name: "file",
action: (editor) => {
if(!mdeModalOpenState) {
vueFile.open();
}
},
className: "icon-paper",
title: "Insert File",
},
{
name: "video",
action: (editor) => {
if(!mdeModalOpenState) {
vueVideo.open();
}
},
className: "icon-video-camera2",
title: "Insert Video Player",
},
'|',
{
name: "inline-code",
action: (editor) => {
if(!editor.codemirror.doc.somethingSelected()) {
return alerts.pushError('Invalid selection','You must select at least 1 character first.');
}
let curSel = editor.codemirror.doc.getSelections();
curSel = _.map(curSel, (s) => {
return '`' + s + '`';
});
editor.codemirror.doc.replaceSelections(curSel);
},
className: "icon-terminal",
title: "Inline Code",
},
{
name: "code-block",
action: (editor) => {
if(!mdeModalOpenState) {
mdeModalOpenState = true;
if(mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection();
}
vueCodeBlock.open();
}
},
className: "icon-code",
title: "Code Block",
},
'|',
{
name: "table",
action: (editor) => {
//todo
},
className: "icon-table",
title: "Insert Table",
},
{
name: "horizontal-rule",
action: SimpleMDE.drawHorizontalRule,
className: "icon-minus2",
title: "Horizontal Rule",
}
],
shortcuts: {
"toggleBlockquote": null,
"toggleFullScreen": null
}
});
//-> Save
let saveCurrentDocument = (ev) => {
$.ajax(window.location.href, {
data: {
markdown: mde.value()
},
dataType: 'json',
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if(rData.ok) {
window.location.assign('/' + pageEntryPath);
} else {
alerts.pushError('Something went wrong', rData.error);
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.');
});
};
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
saveCurrentDocument(ev);
});
$(window).bind('keydown', (ev) => {
if (ev.ctrlKey || ev.metaKey) {
switch (String.fromCharCode(ev.which).toLowerCase()) {
case 's':
ev.preventDefault();
saveCurrentDocument(ev);
break;
}
}
});
}
\ No newline at end of file
} */
},
className: 'icon-link2',
title: 'Insert Link'
},
{
name: 'image',
action: (editor) => {
if (!mdeModalOpenState) {
vueImage.open()
}
},
className: 'icon-image',
title: 'Insert Image'
},
{
name: 'file',
action: (editor) => {
if (!mdeModalOpenState) {
vueFile.open()
}
},
className: 'icon-paper',
title: 'Insert File'
},
{
name: 'video',
action: (editor) => {
if (!mdeModalOpenState) {
vueVideo.open()
}
},
className: 'icon-video-camera2',
title: 'Insert Video Player'
},
'|',
{
name: 'inline-code',
action: (editor) => {
if (!editor.codemirror.doc.somethingSelected()) {
return alerts.pushError('Invalid selection', 'You must select at least 1 character first.')
}
let curSel = editor.codemirror.doc.getSelections()
curSel = _.map(curSel, (s) => {
return '`' + s + '`'
})
editor.codemirror.doc.replaceSelections(curSel)
},
className: 'icon-terminal',
title: 'Inline Code'
},
{
name: 'code-block',
action: (editor) => {
if (!mdeModalOpenState) {
mdeModalOpenState = true
if (mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
}
vueCodeBlock.open()
}
},
className: 'icon-code',
title: 'Code Block'
},
'|',
{
name: 'table',
action: (editor) => {
// todo
},
className: 'icon-table',
title: 'Insert Table'
},
{
name: 'horizontal-rule',
action: SimpleMDE.drawHorizontalRule,
className: 'icon-minus2',
title: 'Horizontal Rule'
}
],
shortcuts: {
'toggleBlockquote': null,
'toggleFullScreen': null
}
})
// -> Save
let saveCurrentDocument = (ev) => {
$.ajax(window.location.href, {
data: {
markdown: mde.value()
},
dataType: 'json',
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if (rData.ok) {
window.location.assign('/' + pageEntryPath)
} else {
alerts.pushError('Something went wrong', rData.error)
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.')
})
}
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
saveCurrentDocument(ev)
})
$(window).bind('keydown', (ev) => {
if (ev.ctrlKey || ev.metaKey) {
switch (String.fromCharCode(ev.which).toLowerCase()) {
case 's':
ev.preventDefault()
saveCurrentDocument(ev)
break
}
}
})
}
"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({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0;
if(val.length >= 3) {
vueHeader.searchactive = true;
vueHeader.searchload++;
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match;
vueHeader.searchsuggest = data.suggest;
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest);
if(vueHeader.searchload > 0) { vueHeader.searchload--; }
});
} else {
vueHeader.searchactive = false;
vueHeader.searchres = [];
vueHeader.searchsuggest = [];
vueHeader.searchmovearr = [];
vueHeader.searchload = 0;
}
},
searchmoveidx: (val, oldVal) => {
if(val > 0) {
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ?
var vueHeader = new Vue({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0
if (val.length >= 3) {
vueHeader.searchactive = true
vueHeader.searchload++
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match
vueHeader.searchsuggest = data.suggest
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
})
} else {
vueHeader.searchactive = false
vueHeader.searchres = []
vueHeader.searchsuggest = []
vueHeader.searchmovearr = []
vueHeader.searchload = 0
}
},
searchmoveidx: (val, oldVal) => {
if (val > 0) {
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) ?
'res.' + vueHeader.searchmovearr[val - 1]._id :
'sug.' + vueHeader.searchmovearr[val - 1];
} else {
vueHeader.searchmovekey = '';
}
}
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug;
},
closeSearch: () => {
vueHeader.searchq = '';
},
moveSelectSearch: () => {
if(vueHeader.searchmoveidx < 1) { return; }
let i = vueHeader.searchmoveidx - 1;
if(vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id);
} else {
vueHeader.searchq = vueHeader.searchmovearr[i];
}
},
moveDownSearch: () => {
if(vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++;
}
},
moveUpSearch: () => {
if(vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--;
}
}
}
});
'sug.' + vueHeader.searchmovearr[val - 1]
} else {
vueHeader.searchmovekey = ''
}
}
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug
},
closeSearch: () => {
vueHeader.searchq = ''
},
moveSelectSearch: () => {
if (vueHeader.searchmoveidx < 1) { return }
let i = vueHeader.searchmoveidx - 1
$('main').on('click', vueHeader.closeSearch);
if (vueHeader.searchmovearr[i]) {
window.location.assign('/' + vueHeader.searchmovearr[i]._id)
} else {
vueHeader.searchq = vueHeader.searchmovearr[i]
}
},
moveDownSearch: () => {
if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
vueHeader.searchmoveidx++
}
},
moveUpSearch: () => {
if (vueHeader.searchmoveidx > 0) {
vueHeader.searchmoveidx--
}
}
}
})
}
\ No newline at end of file
$('main').on('click', vueHeader.closeSearch)
}
function setInputSelection(input, startPos, endPos) {
input.focus();
if (typeof input.selectionStart != "undefined") {
input.selectionStart = startPos;
input.selectionEnd = endPos;
} else if (document.selection && document.selection.createRange) {
function setInputSelection (input, startPos, endPos) {
input.focus()
if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos
input.selectionEnd = endPos
} else if (document.selection && document.selection.createRange) {
// IE branch
input.select();
var range = document.selection.createRange();
range.collapse(true);
range.moveEnd("character", endPos);
range.moveStart("character", startPos);
range.select();
}
}
\ No newline at end of file
input.select()
var range = document.selection.createRange()
range.collapse(true)
range.moveEnd('character', endPos)
range.moveStart('character', startPos)
range.select()
}
}
function makeSafePath(rawPath) {
let rawParts = _.split(_.trim(rawPath), '/');
rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r)));
});
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r); }), '/');
}
\ No newline at end of file
function makeSafePath (rawPath) {
let rawParts = _.split(_.trim(rawPath), '/')
rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r)))
})
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
}
"use strict";
'use strict'
jQuery( document ).ready(function( $ ) {
$('#login-user').focus();
});
\ No newline at end of file
jQuery(document).ready(function ($) {
$('#login-user').focus()
})
......@@ -2,29 +2,27 @@
// Vue Create User instance
let vueCreateUser = new Vue({
el: '#modal-admin-users-create',
data: {
email: '',
provider: 'local',
password: '',
name: ''
},
methods: {
open: (ev) => {
$('#modal-admin-users-create').addClass('is-active');
$('#modal-admin-users-create input').first().focus();
},
cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active');
vueCreateUser.email = '';
vueCreateUser.provider = 'local';
},
create: (ev) => {
el: '#modal-admin-users-create',
data: {
email: '',
provider: 'local',
password: '',
name: ''
},
methods: {
open: (ev) => {
$('#modal-admin-users-create').addClass('is-active')
$('#modal-admin-users-create input').first().focus()
},
cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active')
vueCreateUser.email = ''
vueCreateUser.provider = 'local'
},
create: (ev) => {
vueCreateUser.cancel()
}
}
})
vueCreateUser.cancel();
}
}
});
$('.btn-create-prompt').on('click', vueCreateUser.open);
\ No newline at end of file
$('.btn-create-prompt').on('click', vueCreateUser.open)
......@@ -2,21 +2,21 @@
// Vue Delete User instance
let vueDeleteUser = new Vue({
el: '#modal-admin-users-delete',
data: {
el: '#modal-admin-users-delete',
data: {
},
methods: {
open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active');
},
cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active');
},
deleteUser: (ev) => {
vueDeleteUser.cancel();
}
}
});
},
methods: {
open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active')
},
cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active')
},
deleteUser: (ev) => {
vueDeleteUser.cancel()
}
}
})
$('.btn-deluser-prompt').on('click', vueDeleteUser.open);
\ No newline at end of file
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
//-> Create New Document
// -> Create New Document
let suggestedCreatePath = currentBasePath + '/new-page';
let suggestedCreatePath = currentBasePath + '/new-page'
$('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath);
$('#modal-create-prompt').toggleClass('is-active');
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length);
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden');
});
$('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('#txt-create-prompt').on('keypress', (ev) => {
if(ev.which === 13) {
$('.btn-create-go').trigger('click');
}
});
if (ev.which === 13) {
$('.btn-create-go').trigger('click')
}
})
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val());
if(_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden');
} else {
$('#txt-create-prompt').parent().addClass('is-loading');
window.location.assign('/create/' + newDocPath);
}
});
\ No newline at end of file
let newDocPath = makeSafePath($('#txt-create-prompt').val())
if (_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath)
}
})
//-> Move Existing Document
// -> Move Existing Document
if(currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden');
if (currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden')
}
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1;
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
$('.btn-move-prompt').on('click', (ev) => {
$('#txt-move-prompt').val(currentBasePath);
$('#modal-move-prompt').toggleClass('is-active');
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length);
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden');
});
$('#txt-move-prompt').val(currentBasePath)
$('#modal-move-prompt').toggleClass('is-active')
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length)
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('#txt-move-prompt').on('keypress', (ev) => {
if(ev.which === 13) {
$('.btn-move-go').trigger('click');
}
});
if (ev.which === 13) {
$('.btn-move-go').trigger('click')
}
})
$('.btn-move-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-move-prompt').val());
if(_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden');
} else {
$('#txt-move-prompt').parent().addClass('is-loading');
$.ajax(window.location.href, {
data: {
move: newDocPath
},
dataType: 'json',
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if(rData.ok) {
window.location.assign('/' + newDocPath);
} else {
alerts.pushError('Something went wrong', rData.error);
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.');
});
}
});
\ No newline at end of file
let newDocPath = makeSafePath($('#txt-move-prompt').val())
if (_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-move-prompt').parent().addClass('is-loading')
$.ajax(window.location.href, {
data: {
move: newDocPath
},
dataType: 'json',
method: 'PUT'
}).then((rData, rStatus, rXHR) => {
if (rData.ok) {
window.location.assign('/' + newDocPath)
} else {
alerts.pushError('Something went wrong', rData.error)
}
}, (rXHR, rStatus, err) => {
alerts.pushError('Something went wrong', 'Save operation failed.')
})
}
})
if($('#page-type-admin-profile').length) {
if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
password: '********',
passwordVerify: '********',
name: ''
},
methods: {
saveUser: (ev) => {
if (vueProfile.password !== vueProfile.passwordVerify) {
alerts.pushError('Error', "Passwords don't match!")
return
}
$.post(window.location.href, {
password: vueProfile.password,
name: vueProfile.name
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp)
})
}
},
created: function () {
this.name = usrDataName
}
})
} else if ($('#page-type-admin-users').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
password: '********',
passwordVerify: '********',
name: ''
},
methods: {
saveUser: (ev) => {
if(vueProfile.password !== vueProfile.passwordVerify) {
alerts.pushError('Error', "Passwords don't match!");
return;
}
$.post(window.location.href, {
password: vueProfile.password,
name: vueProfile.name
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.');
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp);
})
}
},
created: function() {
this.name = usrDataName;
}
});
// =include ../modals/admin-users-create.js
} else if($('#page-type-admin-users').length) {
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
el: '#page-type-admin-users-edit',
data: {
id: '',
email: '',
password: '********',
name: '',
rights: [],
roleoverride: 'none'
},
methods: {
addRightsRow: (ev) => {
vueEditUser.rights.push({
role: 'write',
path: '/',
exact: false,
deny: false
})
},
removeRightsRow: (idx) => {
_.pullAt(vueEditUser.rights, idx)
vueEditUser.$forceUpdate()
},
saveUser: (ev) => {
let formattedRights = _.cloneDeep(vueEditUser.rights)
switch (vueEditUser.roleoverride) {
case 'admin':
formattedRights.push({
role: 'admin',
path: '/',
exact: false,
deny: false
})
break
}
$.post(window.location.href, {
password: vueEditUser.password,
name: vueEditUser.name,
rights: JSON.stringify(formattedRights)
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp)
})
}
},
created: function () {
this.id = usrData._id
this.email = usrData.email
this.name = usrData.name
//=include ../modals/admin-users-create.js
if (_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin'])
this.roleoverride = 'admin'
} else {
this.rights = usrData.rights
}
}
})
} else if($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
el: '#page-type-admin-users-edit',
data: {
id: '',
email: '',
password: '********',
name: '',
rights: [],
roleoverride: 'none'
},
methods: {
addRightsRow: (ev) => {
vueEditUser.rights.push({
role: 'write',
path: '/',
exact: false,
deny: false
});
},
removeRightsRow: (idx) => {
_.pullAt(vueEditUser.rights, idx)
vueEditUser.$forceUpdate()
},
saveUser: (ev) => {
let formattedRights = _.cloneDeep(vueEditUser.rights)
switch(vueEditUser.roleoverride) {
case 'admin':
formattedRights.push({
role: 'admin',
path: '/',
exact: false,
deny: false
})
break;
}
$.post(window.location.href, {
password: vueEditUser.password,
name: vueEditUser.name,
rights: JSON.stringify(formattedRights)
}).done((resp) => {
alerts.pushSuccess('Saved successfully', 'Changes have been applied.');
}).fail((jqXHR, txtStatus, resp) => {
alerts.pushError('Error', resp);
})
}
},
created: function() {
this.id = usrData._id;
this.email = usrData.email;
this.name = usrData.name;
if(_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin']);
this.roleoverride = 'admin';
} else {
this.rights = usrData.rights;
}
}
});
//=include ../modals/admin-users-delete.js
}
\ No newline at end of file
// =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) => {
$('#modal-create-discard').toggleClass('is-active')
})
$('.btn-create-discard').on('click', (ev) => {
$('#modal-create-discard').toggleClass('is-active');
});
//=include ../components/editor.js
}
\ No newline at end of file
// =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) => {
$('#modal-edit-discard').toggleClass('is-active')
})
$('.btn-edit-discard').on('click', (ev) => {
$('#modal-edit-discard').toggleClass('is-active');
});
//=include ../components/editor.js
}
\ No newline at end of file
// =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");
scEditor.setTheme("ace/theme/tomorrow_night");
scEditor.getSession().setMode("ace/mode/markdown");
scEditor.setOption('fontSize', '14px');
scEditor.setOption('hScrollBarAlwaysVisible', false);
scEditor.setOption('wrap', true);
scEditor.setReadOnly(true);
scEditor.renderer.updateFull();
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : '';
//=include ../modals/create.js
//=include ../modals/move.js
}
\ No newline at end of file
// =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
}
\ No newline at end of file
// =include ../modals/create.js
// =include ../modals/move.js
}
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
const Promise = require('bluebird');
const validator = require('validator');
const _ = require('lodash');
var express = require('express')
var router = express.Router()
const Promise = require('bluebird')
const validator = require('validator')
const _ = require('lodash')
/**
* Admin
*/
router.get('/', (req, res) => {
res.redirect('/admin/profile');
});
res.redirect('/admin/profile')
})
router.get('/profile', (req, res) => {
if (res.locals.isGuest) {
return res.render('error-forbidden')
}
if(res.locals.isGuest) {
return res.render('error-forbidden');
}
res.render('pages/admin/profile', { adminTab: 'profile' });
});
res.render('pages/admin/profile', { adminTab: 'profile' })
})
router.post('/profile', (req, res) => {
if(res.locals.isGuest) {
return res.render('error-forbidden');
}
return db.User.findById(req.user.id).then((usr) => {
usr.name = _.trim(req.body.name);
if(usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password);
if(nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd;
return usr.save();
});
}
} else {
return usr.save();
}
}).then(() => {
return res.json({ msg: 'OK' });
}).catch((err) => {
res.status(400).json({ msg: err.message });
})
});
if (res.locals.isGuest) {
return res.render('error-forbidden')
}
return db.User.findById(req.user.id).then((usr) => {
usr.name = _.trim(req.body.name)
if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password)
if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd
return usr.save()
})
}
} else {
return usr.save()
}
}).then(() => {
return res.json({ msg: 'OK' })
}).catch((err) => {
res.status(400).json({ msg: err.message })
})
})
router.get('/stats', (req, res) => {
if(res.locals.isGuest) {
return res.render('error-forbidden');
}
Promise.all([
db.Entry.count(),
db.UplFile.count(),
db.User.count()
]).spread((totalEntries, totalUploads, totalUsers) => {
return res.render('pages/admin/stats', {
totalEntries, totalUploads, totalUsers,
adminTab: 'stats'
}) || true;
}).catch((err) => {
throw err;
});
});
if (res.locals.isGuest) {
return res.render('error-forbidden')
}
Promise.all([
db.Entry.count(),
db.UplFile.count(),
db.User.count()
]).spread((totalEntries, totalUploads, totalUsers) => {
return res.render('pages/admin/stats', {
totalEntries, totalUploads, totalUsers, adminTab: 'stats'
}) || true
}).catch((err) => {
throw err
})
})
router.get('/users', (req, res) => {
if(!res.locals.rights.manage) {
return res.render('error-forbidden');
}
db.User.find({})
.select('-password -rights')
.sort('name email')
.exec().then((usrs) => {
res.render('pages/admin/users', { adminTab: 'users', usrs });
});
});
if (!res.locals.rights.manage) {
return res.render('error-forbidden')
}
db.User.find({})
.select('-password -rights')
.sort('name email')
.exec().then((usrs) => {
res.render('pages/admin/users', { adminTab: 'users', usrs })
})
})
router.get('/users/:id', (req, res) => {
if(!res.locals.rights.manage) {
return res.render('error-forbidden');
}
if(!validator.isMongoId(req.params.id)) {
return res.render('error-forbidden');
}
db.User.findById(req.params.id)
.select('-password -providerId')
.exec().then((usr) => {
let usrOpts = {
canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin),
canChangeName: (usr.email !== 'guest'),
canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'),
canChangeRole: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin)),
canBeDeleted: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin))
};
res.render('pages/admin/users-edit', { adminTab: 'users', usr, usrOpts });
});
});
if (!res.locals.rights.manage) {
return res.render('error-forbidden')
}
if (!validator.isMongoId(req.params.id)) {
return res.render('error-forbidden')
}
db.User.findById(req.params.id)
.select('-password -providerId')
.exec().then((usr) => {
let usrOpts = {
canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin),
canChangeName: (usr.email !== 'guest'),
canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'),
canChangeRole: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin)),
canBeDeleted: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin))
}
res.render('pages/admin/users-edit', { adminTab: 'users', usr, usrOpts })
})
})
router.post('/users/:id', (req, res) => {
if(!res.locals.rights.manage) {
return res.status(401).json({ msg: 'Unauthorized' });
}
if(!validator.isMongoId(req.params.id)) {
return res.status(400).json({ msg: 'Invalid User ID' });
}
return db.User.findById(req.params.id).then((usr) => {
usr.name = _.trim(req.body.name);
usr.rights = JSON.parse(req.body.rights);
if(usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password);
if(nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd;
return usr.save();
});
}
} else {
return usr.save();
}
}).then(() => {
return res.json({ msg: 'OK' });
}).catch((err) => {
res.status(400).json({ msg: err.message });
})
});
if (!res.locals.rights.manage) {
return res.status(401).json({ msg: 'Unauthorized' })
}
if (!validator.isMongoId(req.params.id)) {
return res.status(400).json({ msg: 'Invalid User ID' })
}
return db.User.findById(req.params.id).then((usr) => {
usr.name = _.trim(req.body.name)
usr.rights = JSON.parse(req.body.rights)
if (usr.provider === 'local' && req.body.password !== '********') {
let nPwd = _.trim(req.body.password)
if (nPwd.length < 6) {
return Promise.reject(new Error('New Password too short!'))
} else {
return db.User.hashPassword(nPwd).then((pwd) => {
usr.password = pwd
return usr.save()
})
}
} else {
return usr.save()
}
}).then(() => {
return res.json({ msg: 'OK' })
}).catch((err) => {
res.status(400).json({ msg: err.message })
})
})
router.get('/settings', (req, res) => {
if (!res.locals.rights.manage) {
return res.render('error-forbidden')
}
if(!res.locals.rights.manage) {
return res.render('error-forbidden');
}
res.render('pages/admin/settings', { adminTab: 'settings' });
});
res.render('pages/admin/settings', { adminTab: 'settings' })
})
module.exports = router;
\ No newline at end of file
module.exports = router
var express = require('express');
var router = express.Router();
var passport = require('passport');
var ExpressBrute = require('express-brute');
var ExpressBruteMongooseStore = require('express-brute-mongoose');
var moment = require('moment');
'use strict'
const express = require('express')
const router = express.Router()
const passport = require('passport')
const ExpressBrute = require('express-brute')
const ExpressBruteMongooseStore = require('express-brute-mongoose')
const moment = require('moment')
/**
* Setup Express-Brute
*/
var EBstore = new ExpressBruteMongooseStore(db.Bruteforce);
var bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5,
minWait: 60 * 1000,
maxWait: 5 * 60 * 1000,
refreshTimeoutOnRequest: false,
failCallback(req, res, next, nextValidRequestDate) {
req.flash('alert', {
class: 'error',
title: 'Too many attempts!',
message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.',
iconClass: 'fa-times'
});
res.redirect('/login');
}
});
const EBstore = new ExpressBruteMongooseStore(db.Bruteforce)
const bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5,
minWait: 60 * 1000,
maxWait: 5 * 60 * 1000,
refreshTimeoutOnRequest: false,
failCallback (req, res, next, nextValidRequestDate) {
req.flash('alert', {
class: 'error',
title: 'Too many attempts!',
message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.',
iconClass: 'fa-times'
})
res.redirect('/login')
}
})
/**
* Login form
*/
router.get('/login', function(req, res, next) {
res.render('auth/login', {
usr: res.locals.usr
});
});
router.post('/login', bruteforce.prevent, function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
router.get('/login', function (req, res, next) {
res.render('auth/login', {
usr: res.locals.usr
})
})
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) {
req.flash('alert', {
title: 'Invalid login',
message: "The email or password is invalid."
});
return res.redirect('/login');
}
if (!user) {
req.flash('alert', {
title: 'Invalid login',
message: 'The email or password is invalid.'
})
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
req.logIn(user, function (err) {
if (err) { return next(err) }
req.brute.reset(function () {
return res.redirect('/');
});
});
})(req, res, next);
});
return res.redirect('/')
})
})
})(req, res, next)
})
/**
* Social Login
*/
router.get('/login/ms', passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] }));
router.get('/login/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/login/facebook', passport.authenticate('facebook', { scope: ['public_profile', 'email'] }));
router.get('/login/ms', passport.authenticate('windowslive', { scope: ['wl.signin', 'wl.basic', 'wl.emails'] }))
router.get('/login/google', passport.authenticate('google', { scope: ['profile', 'email'] }))
router.get('/login/facebook', passport.authenticate('facebook', { scope: ['public_profile', 'email'] }))
router.get('/login/ms/callback', passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/google/callback', passport.authenticate('google', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }));
router.get('/login/ms/callback', passport.authenticate('windowslive', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/google/callback', passport.authenticate('google', { failureRedirect: '/login', successRedirect: '/' }))
router.get('/login/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login', successRedirect: '/' }))
/**
* Logout
*/
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
router.get('/logout', function (req, res) {
req.logout()
res.redirect('/')
})
module.exports = router;
\ No newline at end of file
module.exports = router
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
var _ = require('lodash');
const express = require('express')
const router = express.Router()
const _ = require('lodash')
// ==========================================
// EDIT MODE
......@@ -12,132 +12,123 @@ var _ = require('lodash');
* Edit document in Markdown
*/
router.get('/edit/*', (req, res, next) => {
if(!res.locals.rights.write) {
return res.render('error-forbidden');
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
entries.fetchOriginal(safePath, {
parseMarkdown: false,
parseMeta: true,
parseTree: false,
includeMarkdown: true,
includeParentInfo: false,
cache: false
}).then((pageData) => {
if(pageData) {
res.render('pages/edit', { pageData });
} else {
throw new Error('Invalid page path.');
}
return true;
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
if (!res.locals.rights.write) {
return res.render('error-forbidden')
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''))
entries.fetchOriginal(safePath, {
parseMarkdown: false,
parseMeta: true,
parseTree: false,
includeMarkdown: true,
includeParentInfo: false,
cache: false
}).then((pageData) => {
if (pageData) {
res.render('pages/edit', { pageData })
} else {
throw new Error('Invalid page path.')
}
return true
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
})
})
})
router.put('/edit/*', (req, res, next) => {
if(!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
entries.update(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true;
}).catch((err) => {
res.json({
ok: false,
error: err.message
});
});
});
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
})
}
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''))
entries.update(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true
}).catch((err) => {
res.json({
ok: false,
error: err.message
})
})
})
// ==========================================
// CREATE MODE
// ==========================================
router.get('/create/*', (req, res, next) => {
if(!res.locals.rights.write) {
return res.render('error-forbidden');
}
if(_.some(['create','edit','account','source','history','mk'], (e) => { return _.startsWith(req.path, '/create/' + e); })) {
return res.render('error', {
message: 'You cannot create a document with this name as it is reserved by the system.',
error: {}
});
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
entries.exists(safePath).then((docExists) => {
if(!docExists) {
return entries.getStarter(safePath).then((contents) => {
let pageData = {
markdown: contents,
meta: {
title: _.startCase(safePath),
path: safePath
}
};
res.render('pages/create', { pageData });
return true;
}).catch((err) => {
throw new Error('Could not load starter content!');
});
} else {
throw new Error('This entry already exists!');
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
if (!res.locals.rights.write) {
return res.render('error-forbidden')
}
if (_.some(['create', 'edit', 'account', 'source', 'history', 'mk'], (e) => { return _.startsWith(req.path, '/create/' + e) })) {
return res.render('error', {
message: 'You cannot create a document with this name as it is reserved by the system.',
error: {}
})
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''))
entries.exists(safePath).then((docExists) => {
if (!docExists) {
return entries.getStarter(safePath).then((contents) => {
let pageData = {
markdown: contents,
meta: {
title: _.startCase(safePath),
path: safePath
}
}
res.render('pages/create', { pageData })
return true
}).catch((err) => {
winston.warn(err)
throw new Error('Could not load starter content!')
})
} else {
throw new Error('This entry already exists!')
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
})
})
})
router.put('/create/*', (req, res, next) => {
if(!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
entries.create(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true;
}).catch((err) => {
return res.json({
ok: false,
error: err.message
});
});
});
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
})
}
let safePath = entries.parsePath(_.replace(req.path, '/create', ''))
entries.create(safePath, req.body.markdown).then(() => {
return res.json({
ok: true
}) || true
}).catch((err) => {
return res.json({
ok: false,
error: err.message
})
})
})
// ==========================================
// VIEW MODE
......@@ -147,102 +138,94 @@ router.put('/create/*', (req, res, next) => {
* View source of a document
*/
router.get('/source/*', (req, res, next) => {
let safePath = entries.parsePath(_.replace(req.path, '/source', ''));
entries.fetchOriginal(safePath, {
parseMarkdown: false,
parseMeta: true,
parseTree: false,
includeMarkdown: true,
includeParentInfo: false,
cache: false
}).then((pageData) => {
if(pageData) {
res.render('pages/source', { pageData });
} else {
throw new Error('Invalid page path.');
}
return true;
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
let safePath = entries.parsePath(_.replace(req.path, '/source', ''))
entries.fetchOriginal(safePath, {
parseMarkdown: false,
parseMeta: true,
parseTree: false,
includeMarkdown: true,
includeParentInfo: false,
cache: false
}).then((pageData) => {
if (pageData) {
res.render('pages/source', { pageData })
} else {
throw new Error('Invalid page path.')
}
return true
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
})
})
})
/**
* View document
*/
router.get('/*', (req, res, next) => {
let safePath = entries.parsePath(req.path);
entries.fetch(safePath).then((pageData) => {
if(pageData) {
res.render('pages/view', { pageData });
} else {
res.render('error-notexist', {
newpath: safePath
});
}
return true;
}).error((err) => {
if(safePath === 'home') {
res.render('pages/welcome');
} else {
res.render('error-notexist', {
message: err.message,
newpath: safePath
});
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
});
});
});
let safePath = entries.parsePath(req.path)
entries.fetch(safePath).then((pageData) => {
if (pageData) {
res.render('pages/view', { pageData })
} else {
res.render('error-notexist', {
newpath: safePath
})
}
return true
}).error((err) => {
if (safePath === 'home') {
res.render('pages/welcome')
} else {
res.render('error-notexist', {
message: err.message,
newpath: safePath
})
}
}).catch((err) => {
res.render('error', {
message: err.message,
error: {}
})
})
})
/**
* Move document
*/
router.put('/*', (req, res, next) => {
if(!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
});
}
let safePath = entries.parsePath(req.path);
if(_.isEmpty(req.body.move)) {
return res.json({
ok: false,
error: 'Invalid document action call.'
});
}
let safeNewPath = entries.parsePath(req.body.move);
entries.move(safePath, safeNewPath).then(() => {
res.json({
ok: true
});
}).catch((err) => {
res.json({
ok: false,
error: err.message
});
});
});
module.exports = router;
\ No newline at end of file
if (!res.locals.rights.write) {
return res.json({
ok: false,
error: 'Forbidden'
})
}
let safePath = entries.parsePath(req.path)
if (_.isEmpty(req.body.move)) {
return res.json({
ok: false,
error: 'Invalid document action call.'
})
}
let safeNewPath = entries.parsePath(req.body.move)
entries.move(safePath, safeNewPath).then(() => {
res.json({
ok: true
})
}).catch((err) => {
res.json({
ok: false,
error: err.message
})
})
})
module.exports = router
"use strict";
'use strict'
var express = require('express');
var router = express.Router();
const express = require('express')
const router = express.Router()
var readChunk = require('read-chunk'),
fileType = require('file-type'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
path = require('path'),
_ = require('lodash');
const readChunk = require('read-chunk')
const fileType = require('file-type')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const path = require('path')
const _ = require('lodash')
var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$");
var validPathThumbsRe = new RegExp("^([0-9]+\\.png)$");
const validPathRe = new RegExp('^([a-z0-9\\/-]+\\.[a-z0-9]+)$')
const validPathThumbsRe = new RegExp('^([0-9]+\\.png)$')
// ==========================================
// SERVE UPLOADS FILES
// ==========================================
router.get('/t/*', (req, res, next) => {
let fileName = req.params[0];
if(!validPathThumbsRe.test(fileName)) {
return res.sendStatus(404).end();
}
//todo: Authentication-based access
res.sendFile(fileName, {
root: lcdata.getThumbsPath(),
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end();
}
});
});
let fileName = req.params[0]
if (!validPathThumbsRe.test(fileName)) {
return res.sendStatus(404).end()
}
// todo: Authentication-based access
res.sendFile(fileName, {
root: lcdata.getThumbsPath(),
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end()
}
})
})
router.post('/img', lcdata.uploadImgHandler, (req, res, next) => {
let destFolder = _.chain(req.body.folder).trim().toLower().value();
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if(!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' });
return true;
}
Promise.map(req.files, (f) => {
let destFilename = '';
let destFilePath = '';
return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
destFilename = fname;
destFilePath = path.resolve(destFolderPath, destFilename);
return readChunk(f.path, 0, 262);
}).then((buf) => {
//-> Check MIME type by magic number
let mimeInfo = fileType(buf);
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return Promise.reject(new Error('Invalid file type.'));
}
return true;
}).then(() => {
//-> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false });
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
};
}).reflect();
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if(r.isFulfilled()) {
return r.value();
} else {
return {
ok: false,
msg: r.reason().message
};
}
});
res.json({ ok: true, results: uplResults });
return true;
}).catch((err) => {
res.json({ ok: false, msg: err.message });
return true;
});
});
});
let destFolder = _.chain(req.body.folder).trim().toLower().value()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' })
return true
}
Promise.map(req.files, (f) => {
let destFilename = ''
let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
return readChunk(f.path, 0, 262)
}).then((buf) => {
// -> Check MIME type by magic number
let mimeInfo = fileType(buf)
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return Promise.reject(new Error('Invalid file type.'))
}
return true
}).then(() => {
// -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false })
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
}
}).reflect()
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if (r.isFulfilled()) {
return r.value()
} else {
return {
ok: false,
msg: r.reason().message
}
}
})
res.json({ ok: true, results: uplResults })
return true
}).catch((err) => {
res.json({ ok: false, msg: err.message })
return true
})
})
})
router.post('/file', lcdata.uploadFileHandler, (req, res, next) => {
let destFolder = _.chain(req.body.folder).trim().toLower().value();
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if(!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' });
return true;
}
Promise.map(req.files, (f) => {
let destFilename = '';
let destFilePath = '';
return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
destFilename = fname;
destFilePath = path.resolve(destFolderPath, destFilename);
//-> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false });
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
};
}).reflect();
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if(r.isFulfilled()) {
return r.value();
} else {
return {
ok: false,
msg: r.reason().message
};
}
});
res.json({ ok: true, results: uplResults });
return true;
}).catch((err) => {
res.json({ ok: false, msg: err.message });
return true;
});
});
});
let destFolder = _.chain(req.body.folder).trim().toLower().value()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
res.json({ ok: false, msg: 'Invalid Folder' })
return true
}
Promise.map(req.files, (f) => {
let destFilename = ''
let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
destFilename = fname
destFilePath = path.resolve(destFolderPath, destFilename)
// -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false })
}).then(() => {
return {
ok: true,
filename: destFilename,
filesize: f.size
}
}).reflect()
}, {concurrency: 3}).then((results) => {
let uplResults = _.map(results, (r) => {
if (r.isFulfilled()) {
return r.value()
} else {
return {
ok: false,
msg: r.reason().message
}
}
})
res.json({ ok: true, results: uplResults })
return true
}).catch((err) => {
res.json({ ok: false, msg: err.message })
return true
})
})
})
router.get('/*', (req, res, next) => {
let fileName = req.params[0];
if(!validPathRe.test(fileName)) {
return res.sendStatus(404).end();
}
//todo: Authentication-based access
res.sendFile(fileName, {
root: git.getRepoPath() + '/uploads/',
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end();
}
});
});
module.exports = router;
\ No newline at end of file
let fileName = req.params[0]
if (!validPathRe.test(fileName)) {
return res.sendStatus(404).end()
}
// todo: Authentication-based access
res.sendFile(fileName, {
root: git.getRepoPath() + '/uploads/',
dotfiles: 'deny'
}, (err) => {
if (err) {
res.status(err.status).end()
}
})
})
module.exports = router
"use strict";
'use strict'
const _ = require('lodash')
module.exports = (socket) => {
if(!socket.request.user.logged_in) {
return;
if (!socket.request.user.logged_in) {
return
}
//-----------------------------------------
// -----------------------------------------
// SEARCH
//-----------------------------------------
// -----------------------------------------
socket.on('search', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
entries.search(data.terms).then((results) => {
return cb(results) || true;
});
});
return cb(results) || true
})
})
//-----------------------------------------
// -----------------------------------------
// UPLOADS
//-----------------------------------------
// -----------------------------------------
socket.on('uploadsGetFolders', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFolders().then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsCreateFolder', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.createUploadsFolder(data.foldername).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsGetImages', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFiles('image', data.folder).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsGetFiles', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.getUploadsFiles('binary', data.folder).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsDeleteFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.deleteUploadsFile(data.uid).then((f) => {
return cb(f) || true;
});
});
return cb(f) || true
})
})
socket.on('uploadsFetchFileFromURL', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.downloadFromUrl(data.folder, data.fetchUrl).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
return cb({
ok: false,
msg: err.message
}) || true
})
})
socket.on('uploadsRenameFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder, data.filename).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
return cb({
ok: false,
msg: err.message
}) || true
})
})
socket.on('uploadsMoveFile', (data, cb) => {
cb = cb || _.noop;
cb = cb || _.noop
upl.moveUploadsFile(data.uid, data.folder).then((f) => {
return cb({ ok: true }) || true;
return cb({ ok: true }) || true
}).catch((err) => {
return cb({
ok: false,
msg: err.message
}) || true;
});
});
};
\ No newline at end of file
return cb({
ok: false,
msg: err.message
}) || true
})
})
}
"use strict";
'use strict'
const crypto = require('crypto');
const crypto = require('crypto')
/**
* Internal Authentication
*/
module.exports = {
_curKey: false,
_curKey: false,
init(inKey) {
init (inKey) {
this._curKey = inKey
this._curKey = inKey;
return this
},
return this;
generateKey () {
return crypto.randomBytes(20).toString('hex')
},
},
validateKey (inKey) {
return inKey === this._curKey
}
generateKey() {
return crypto.randomBytes(20).toString('hex');
},
validateKey(inKey) {
return inKey === this._curKey;
}
};
\ No newline at end of file
}
"use strict";
'use strict'
var path = require('path'),
Promise = require('bluebird'),
fs = Promise.promisifyAll(require('fs-extra')),
multer = require('multer'),
os = require('os'),
_ = require('lodash');
const path = require('path')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs-extra'))
const multer = require('multer')
const os = require('os')
const _ = require('lodash')
/**
* Local Data Storage
*/
module.exports = {
_uploadsPath: './repo/uploads',
_uploadsThumbsPath: './data/thumbs',
uploadImgHandler: null,
/**
* Initialize Local Data Storage model
*
* @return {Object} Local Data Storage model instance
*/
init() {
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads');
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs');
this.createBaseDirectories(appconfig);
this.initMulter(appconfig);
return this;
},
/**
* Init Multer upload handlers
*
* @param {Object} appconfig The application config
* @return {boolean} Void
*/
initMulter(appconfig) {
let maxFileSizes = {
img: appconfig.uploads.maxImageFileSize * 1024 * 1024,
file: appconfig.uploads.maxOtherFileSize * 1024 * 1024
};
//-> IMAGES
this.uploadImgHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'));
}
}),
fileFilter: (req, f, cb) => {
//-> Check filesize
if(f.size > maxFileSizes.img) {
return cb(null, false);
}
//-> Check MIME type (quick check only)
if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
return cb(null, false);
}
cb(null, true);
}
}).array('imgfile', 20);
//-> FILES
this.uploadFileHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'));
}
}),
fileFilter: (req, f, cb) => {
//-> Check filesize
if(f.size > maxFileSizes.file) {
return cb(null, false);
}
cb(null, true);
}
}).array('binfile', 20);
return true;
},
/**
* Creates a base directories (Synchronous).
*
* @param {Object} appconfig The application config
* @return {Void} Void
*/
createBaseDirectories(appconfig) {
winston.info('[SERVER] Checking data directories...');
try {
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'));
if(os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'), '644');
}
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo));
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'));
if(os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.repo, './upload'), '644');
}
} catch (err) {
winston.error(err);
}
winston.info('[SERVER] Data and Repository directories are OK.');
return;
},
/**
* Gets the uploads path.
*
* @return {String} The uploads path.
*/
getUploadsPath() {
return this._uploadsPath;
},
/**
* Gets the thumbnails folder path.
*
* @return {String} The thumbs path.
*/
getThumbsPath() {
return this._uploadsThumbsPath;
},
/**
* Check if filename is valid and unique
*
* @param {String} f The filename
* @param {String} fld The containing folder
* @param {boolean} isImage Indicates if image
* @return {Promise<String>} Promise of the accepted filename
*/
validateUploadsFilename(f, fld, isImage) {
let fObj = path.parse(f);
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9\-]+/g, '');
let fext = _.toLower(fObj.ext);
if(isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
fext = '.png';
}
f = fname + fext;
let fpath = path.resolve(this._uploadsPath, fld, f);
return fs.statAsync(fpath).then((s) => {
throw new Error('File ' + f + ' already exists.');
}).catch((err) => {
if(err.code === 'ENOENT') {
return f;
}
throw err;
});
},
};
\ No newline at end of file
_uploadsPath: './repo/uploads',
_uploadsThumbsPath: './data/thumbs',
uploadImgHandler: null,
/**
* Initialize Local Data Storage model
*
* @return {Object} Local Data Storage model instance
*/
init () {
this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
this.createBaseDirectories(appconfig)
this.initMulter(appconfig)
return this
},
/**
* Init Multer upload handlers
*
* @param {Object} appconfig The application config
* @return {boolean} Void
*/
initMulter (appconfig) {
let maxFileSizes = {
img: appconfig.uploads.maxImageFileSize * 1024 * 1024,
file: appconfig.uploads.maxOtherFileSize * 1024 * 1024
}
// -> IMAGES
this.uploadImgHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
}
}),
fileFilter: (req, f, cb) => {
// -> Check filesize
if (f.size > maxFileSizes.img) {
return cb(null, false)
}
// -> Check MIME type (quick check only)
if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
return cb(null, false)
}
cb(null, true)
}
}).array('imgfile', 20)
// -> FILES
this.uploadFileHandler = multer({
storage: multer.diskStorage({
destination: (req, f, cb) => {
cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
}
}),
fileFilter: (req, f, cb) => {
// -> Check filesize
if (f.size > maxFileSizes.file) {
return cb(null, false)
}
cb(null, true)
}
}).array('binfile', 20)
return true
},
/**
* Creates a base directories (Synchronous).
*
* @param {Object} appconfig The application config
* @return {Void} Void
*/
createBaseDirectories (appconfig) {
winston.info('[SERVER] Checking data directories...')
try {
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'))
if (os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'), '644')
}
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo))
fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'))
if (os.type() !== 'Windows_NT') {
fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.repo, './upload'), '644')
}
} catch (err) {
winston.error(err)
}
winston.info('[SERVER] Data and Repository directories are OK.')
return
},
/**
* Gets the uploads path.
*
* @return {String} The uploads path.
*/
getUploadsPath () {
return this._uploadsPath
},
/**
* Gets the thumbnails folder path.
*
* @return {String} The thumbs path.
*/
getThumbsPath () {
return this._uploadsThumbsPath
},
/**
* Check if filename is valid and unique
*
* @param {String} f The filename
* @param {String} fld The containing folder
* @param {boolean} isImage Indicates if image
* @return {Promise<String>} Promise of the accepted filename
*/
validateUploadsFilename (f, fld, isImage) {
let fObj = path.parse(f)
let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(/[^a-z0-9-]+/g, '')
let fext = _.toLower(fObj.ext)
if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
fext = '.png'
}
f = fname + fext
let fpath = path.resolve(this._uploadsPath, fld, f)
return fs.statAsync(fpath).then((s) => {
throw new Error('File ' + f + ' already exists.')
}).catch((err) => {
if (err.code === 'ENOENT') {
return f
}
throw err
})
}
}
"use strict";
'use strict'
var Promise = require('bluebird'),
moment = require('moment-timezone');
const moment = require('moment-timezone')
/**
* Authentication middleware
......@@ -12,29 +11,27 @@ var Promise = require('bluebird'),
* @return {any} void
*/
module.exports = (req, res, next) => {
// Is user authenticated ?
// Is user authenticated ?
if (!req.isAuthenticated()) {
return res.redirect('/login')
}
if (!req.isAuthenticated()) {
return res.redirect('/login');
}
// Check permissions
// Check permissions
if (!rights.check(req, 'read')) {
return res.render('error-forbidden')
}
if(!rights.check(req, 'read')) {
return res.render('error-forbidden');
}
// Set i18n locale
// Set i18n locale
req.i18n.changeLanguage(req.user.lang)
res.locals.userMoment = moment
res.locals.userMoment.locale(req.user.lang)
req.i18n.changeLanguage(req.user.lang);
res.locals.userMoment = moment;
res.locals.userMoment.locale(req.user.lang);
// Expose user data
// Expose user data
res.locals.user = req.user
res.locals.user = req.user;
return next();
};
\ No newline at end of file
return next()
}
"use strict";
'use strict'
/**
* Flash middleware
......@@ -9,9 +9,7 @@
* @return {any} void
*/
module.exports = (req, res, next) => {
res.locals.appflash = req.flash('alert')
res.locals.appflash = req.flash('alert');
next();
};
\ No newline at end of file
next()
}
'use strict'
/**
* Security Middleware
*
......@@ -6,23 +8,21 @@
* @param {Function} next next callback function
* @return {any} void
*/
module.exports = function(req, res, next) {
//-> Disable X-Powered-By
app.disable('x-powered-by');
//-> Disable Frame Embedding
res.set('X-Frame-Options', 'deny');
module.exports = function (req, res, next) {
// -> Disable X-Powered-By
app.disable('x-powered-by')
//-> Re-enable XSS Fitler if disabled
res.set('X-XSS-Protection', '1; mode=block');
// -> Disable Frame Embedding
res.set('X-Frame-Options', 'deny')
//-> Disable MIME-sniffing
res.set('X-Content-Type-Options', 'nosniff');
// -> Re-enable XSS Fitler if disabled
res.set('X-XSS-Protection', '1; mode=block')
//-> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge');
// -> Disable MIME-sniffing
res.set('X-Content-Type-Options', 'nosniff')
return next();
// -> Disable IE Compatibility Mode
res.set('X-UA-Compatible', 'IE=edge')
};
\ No newline at end of file
return next()
}
"use strict";
'use strict'
/**
* BruteForce schema
......@@ -6,13 +6,13 @@
* @type {<Mongoose.Schema>}
*/
var bruteForceSchema = Mongoose.Schema({
_id: { type: String, index: 1 },
data: {
count: Number,
lastRequest: Date,
firstRequest: Date
},
expires: { type: Date, index: { expires: '1d' } }
});
_id: { type: String, index: 1 },
data: {
count: Number,
lastRequest: Date,
firstRequest: Date
},
expires: { type: Date, index: { expires: '1d' } }
})
module.exports = Mongoose.model('Bruteforce', bruteForceSchema);
\ No newline at end of file
module.exports = Mongoose.model('Bruteforce', bruteForceSchema)
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Entry schema
......@@ -10,7 +7,7 @@ const Promise = require('bluebird'),
*/
var entrySchema = Mongoose.Schema({
_id: String,
_id: String,
title: {
type: String,
......@@ -31,9 +28,9 @@ var entrySchema = Mongoose.Schema({
}
},
{
timestamps: {}
});
{
timestamps: {}
})
entrySchema.index({
_id: 'text',
......@@ -48,6 +45,6 @@ entrySchema.index({
content: 1
},
name: 'EntriesTextIndex'
});
})
module.exports = Mongoose.model('Entry', entrySchema);
\ No newline at end of file
module.exports = Mongoose.model('Entry', entrySchema)
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Upload File schema
......@@ -10,7 +7,7 @@ const Promise = require('bluebird'),
*/
var uplFileSchema = Mongoose.Schema({
_id: String,
_id: String,
category: {
type: String,
......@@ -42,9 +39,6 @@ var uplFileSchema = Mongoose.Schema({
required: true
}
},
{
timestamps: {}
});
}, { timestamps: {} })
module.exports = Mongoose.model('UplFile', uplFileSchema);
\ No newline at end of file
module.exports = Mongoose.model('UplFile', uplFileSchema)
"use strict";
const Promise = require('bluebird'),
_ = require('lodash');
'use strict'
/**
* Upload Folder schema
......@@ -10,16 +7,13 @@ const Promise = require('bluebird'),
*/
var uplFolderSchema = Mongoose.Schema({
_id: String,
_id: String,
name: {
type: String,
index: true
}
},
{
timestamps: {}
});
}, { timestamps: {} })
module.exports = Mongoose.model('UplFolder', uplFolderSchema);
\ No newline at end of file
module.exports = Mongoose.model('UplFolder', uplFolderSchema)
"use strict";
'use strict'
const Promise = require('bluebird'),
bcrypt = require('bcryptjs-then'),
_ = require('lodash');
const Promise = require('bluebird')
const bcrypt = require('bcryptjs-then')
const _ = require('lodash')
/**
* Region schema
......@@ -11,78 +11,73 @@ const Promise = require('bluebird'),
*/
var userSchema = Mongoose.Schema({
email: {
type: String,
required: true,
index: true
},
email: {
type: String,
required: true,
index: true
},
provider: {
type: String,
required: true
},
provider: {
type: String,
required: true
},
providerId: {
type: String
},
providerId: {
type: String
},
password: {
type: String
},
password: {
type: String
},
name: {
type: String
},
name: {
type: String
},
rights: [{
role: String,
path: String,
exact: Boolean,
deny: Boolean
}]
rights: [{
role: String,
path: String,
exact: Boolean,
deny: Boolean
}]
},
{
timestamps: {}
});
}, { timestamps: {} })
userSchema.statics.processProfile = (profile) => {
let primaryEmail = ''
if (_.isArray(profile.emails)) {
let e = _.find(profile.emails, ['primary', true])
primaryEmail = (e) ? e.value : _.first(profile.emails).value
} else if (_.isString(profile.email) && profile.email.length > 5) {
primaryEmail = profile.email
} else {
return Promise.reject(new Error('Invalid User Email'))
}
let primaryEmail = '';
if(_.isArray(profile.emails)) {
let e = _.find(profile.emails, ['primary', true]);
primaryEmail = (e) ? e.value : _.first(profile.emails).value;
} else if(_.isString(profile.email) && profile.email.length > 5) {
primaryEmail = profile.email;
} else {
return Promise.reject(new Error('Invalid User Email'));
}
return db.User.findOneAndUpdate({
email: primaryEmail,
provider: profile.provider
}, {
email: primaryEmail,
provider: profile.provider,
providerId: profile.id,
name: profile.displayName || _.split(primaryEmail, '@')[0]
}, {
new: true,
upsert: true
}).then((user) => {
return (user) ? user : Promise.reject(new Error('User Upsert failed.'));
});
};
return db.User.findOneAndUpdate({
email: primaryEmail,
provider: profile.provider
}, {
email: primaryEmail,
provider: profile.provider,
providerId: profile.id,
name: profile.displayName || _.split(primaryEmail, '@')[0]
}, {
new: true,
upsert: true
}).then((user) => {
return user || Promise.reject(new Error('User Upsert failed.'))
})
}
userSchema.statics.hashPassword = (rawPwd) => {
return bcrypt.hash(rawPwd);
};
return bcrypt.hash(rawPwd)
}
userSchema.methods.validatePassword = function(rawPwd) {
return bcrypt.compare(rawPwd, this.password).then((isValid) => {
return (isValid) ? true : Promise.reject(new Error('Invalid Login'));
});
};
userSchema.methods.validatePassword = function (rawPwd) {
return bcrypt.compare(rawPwd, this.password).then((isValid) => {
return (isValid) ? true : Promise.reject(new Error('Invalid Login'))
})
}
module.exports = Mongoose.model('User', userSchema);
\ No newline at end of file
module.exports = Mongoose.model('User', userSchema)
......@@ -129,5 +129,35 @@
"twemoji-awesome": "^1.0.4",
"vue": "^2.1.10"
},
"standard": {
"globals": [
"app",
"appconfig",
"appdata",
"bgAgent",
"db",
"entries",
"git",
"mark",
"lang",
"lcdata",
"rights",
"upl",
"winston",
"ws",
"Mongoose",
"CORE_PATH",
"ROOTPATH",
"IS_DEBUG",
"PROCNAME",
"WSInternalKey"
],
"ignore": [
"assets/**/*",
"data/**/*",
"node_modules/**/*",
"repo/**/*"
]
},
"snyk": true
}
'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
// CSS
link(type='text/css', rel='stylesheet', href='/css/libs.css')
link(type='text/css', rel='stylesheet', href='/css/app.css')
link(type='text/css', rel='stylesheet', href='/css/error.css')
body(class='server-error')
section.hero.is-warning.is-fullheight
.hero-body
.container
a(href='/'): img(src='/favicons/android-icon-96x96.png')
h1.title(style={ 'margin-top': '30px'})= message
h2.subtitle(style={ 'margin-bottom': '50px'}) Oops, something went wrong
a.button.is-warning.is-inverted(href='/') Go Home
body(class='is-error')
.container
a(href='/'): img(src='/favicons/android-icon-96x96.png')
h1= message
h2 Oops, something went wrong
a.button.is-amber.is-inverted.is-featured(href='/') Go Home
if error.stack
section.section
.container.is-fluid
.content
h3 Detailed debug trail:
pre: code #{error.stack}
\ No newline at end of file
if error.stack
h3 Detailed debug trail:
pre: code #{error.stack}
\ No newline at end of file
......@@ -53,6 +53,11 @@ block content
a(href='/admin')
i.icon-head
span Account
else
li
a(href='/login')
i.icon-unlock
span Login
aside.stickyscroll(data-margin-top=40)
.sidebar-label
i.icon-th-list
......
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