diff --git a/.gitignore b/.gitignore index 9352e9d..d26b4fe 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ brain/models/_backup/ /_maintenance/ *.DS_Store /forfipamo -site-settings.json \ No newline at end of file +site-settings.json +.nova/ \ No newline at end of file diff --git a/brain/api/v1/pages.js b/brain/api/v1/pages.js index 02654bb..85d69d0 100644 --- a/brain/api/v1/pages.js +++ b/brain/api/v1/pages.js @@ -31,117 +31,89 @@ var post_upload = multer({ storage: storage }).array('post_image'); -/*** - Retrieve Pages -*/ +/** + * Retrives list of Pages + * @public + */ router.get('/', (req, res) => { book.getPage().then(result => { res.json(result); }); }); -/*** - Update Page -*/ +/** + * Add/Update Page + */ router.post('/write/:task?', feature_upload, (req, res) => { if (req.session.user) { - //Get enctrypted hashed token from header request - let hash = req.headers['x-access-token']; - //Checks if token is a proper hash, if not reject - if (!isTokenValid(req.session.token, hash)) { - res.json({ - type: DataEvent.API_REQUEST_LAME, - message: 'Invalid Token. Auth Blocked' - }); - } else { - //console.log('TOKEN IS GOOD'); - var member = req.session.user; - jwt.verify(req.session.token, member.key, function(err, decoded) { - if (err) { - console('NOPE', err); + authCheck(req) + .then(() => { + let body = _.mapValues(req.body); + let feature = ''; + let task = ''; + req.params.task === 'new' + ? (task = DataEvent.API_PAGE_CREATE) + : (task = DataEvent.API_PAGE_WRITE); + if (req.files.length > 0) { + var path = req.files[0].path; + feature = '/' + path.substring(7, path.length); + } else { + var url = body.feature_image; + url != null || url != undefined || url != '' + ? (feature = url.substring(21, url.length)) + : (feature = ''); } - console.log('YUP', decoded); + body.feature = feature; + body.deleted = false; + book.editPage(body, body.page_uuid, task, req.session.user) + .then(result => { + if (result.type === DataEvent.PAGE_CREATE) { + fs.readJSON('site/settings.json').then(settings => { + settings.library_stats.current_index = ++settings.library_stats + .current_index; + settings.library_stats.total_pages = ++settings.library_stats + .total_pages; + fs.writeJSON('site/settings.json', settings); + }); + } + res.json(result); + }) + .catch(err => { + res.json(err); + }); + }) + .catch(err => { + res.json(err); }); - } } - - var feature = ''; - if (req.files.length > 0) { - var path = req.files[0].path; - feature = '/' + path.substring(7, path.length); - } else { - var url = req.body.feature_image; - url != null || url != undefined || url != '' - ? (feature = url.substring(21, url.length)) - : (feature = ''); - } - - var pageWrite = - '---\n' + - 'id: ' + - req.body.page_id + - '\n' + - 'uuid: ' + - req.body.page_uuid + - '\n' + - 'title: ' + - req.body.title + - '\n' + - 'feature: ' + - feature + - '\n' + - 'layout: ' + - 'page' + - '\n' + - 'tags: ' + - req.body.tags + - '\n' + - 'author: ' + - req.session.user.handle + - '\n' + - 'created: ' + - moment(req.body.created).format() + - '\n' + - 'updated: ' + - moment(Date.now()).format() + - '\n' + - 'menu: ' + - req.body.pinToMenu + - '\n' + - 'featured: ' + - req.body.featureStatus + - '\n' + - 'published: ' + - req.body.publishedStatus + - '\n' + - 'slug: ' + - req.body.slug + - '\n' + - '---\n\n' + - req.body.content; - fs.writeFile('content/pages/' + req.body.slug + '.md', pageWrite, err => { - // throws an error, you could also catch it here - if (err) res.json({ type: DataEvent.PAGE_ERROR, message: err }); - - // success case, the file was saved - if (req.params.task === 'new') { - // if new file, update settings index and page count - fs.readJSON('site/settings.json').then(settings => { - settings.library_stats.current_index = ++settings.library_stats.current_index; - settings.library_stats.total_pages = ++settings.library_stats.total_pages; - fs.writeJSON('site/settings.json', settings); - }); - res.json({ - type: DataEvent.PAGE_ADDED, - message: 'New Page Created', - id: req.body.page_uuid - }); - } else { - res.json({ type: DataEvent.PAGE_UPDATED, message: 'Page saved, boss' }); - } - }); }); +/** + * Soft deletes Page + */ + +router.post('/delete', (req, res) => { + if (req.session.user) { + authCheck(req) + .then(() => { + book.editPage([], req.body.id, DataEvent.API_PAGE_DELETE, req.session.user) + .then(result => { + res.json(result); + }) + .catch(err => { + res.json(err); + }); + }) + .catch(err => { + res.json(err); + }); + } +}); + +/** + * Uploads image from a Page content + */ + router.post('/add-post-image', post_upload, function(req, res) { //console.log(req.body); var image = req.files[0].path; @@ -157,3 +129,39 @@ module.exports = router; function isTokenValid(token, hashedToken) { return bCrypt.compareSync(token, hashedToken); } + +function authCheck(req) { + return new Promise((resolve, reject) => { + let hash = req.headers['x-access-token']; + let response = []; + //Checks if token is a proper hash, if not reject + if (!isTokenValid(req.session.token, hash)) { + response = { + status: false, + type: DataEvent.API_REQUEST_LAME, + message: 'No Token Present. Auth Blocked' + }; + reject(response); + //res.json(); + } else { + var member = req.session.user; + jwt.verify(req.session.token, member.key, function(err, decoded) { + if (err) { + response = { + status: false, + type: DataEvent.API_REQUEST_LAME, + message: 'Invalid Token. Auth Blocked' + }; + reject(response); + } + response = { + status: true, + type: DataEvent.API_REQUEST_GOOD, + message: 'Token Verified', + token: decoded + }; + resolve(response); + }); + } + }); +} diff --git a/brain/data/Book.js b/brain/data/Book.js index 43d9381..ac677c0 100644 --- a/brain/data/Book.js +++ b/brain/data/Book.js @@ -2,6 +2,8 @@ import fh from 'filehound'; import fs from 'fs-extra'; import metadataParser from 'markdown-yaml-metadata-parser'; import _ from 'lodash'; +import * as DataEvent from '../../src/com/events/DataEvent'; +const moment = require('moment'); export default class Pages { //-------------------------- @@ -13,6 +15,10 @@ export default class Pages { //-------------------------- start() {} + /** + * Retrieves single page or pages + * @parameter id: optional id if requesting a single Page + */ getPage(id) { return new Promise((resolve, reject) => { fh.create() @@ -47,6 +53,116 @@ export default class Pages { }); }); } + /** + * Edits single page based on id and task + * @parameter id: id of page being edited + * @parameter task: type of task being performed + */ + editPage(body, id, task, user) { + return new Promise((resolve, reject) => { + let self = this; + let response = []; + switch (task) { + case DataEvent.API_PAGE_CREATE: + case DataEvent.API_PAGE_WRITE: + var pageWrite = + '---\n' + + 'id: ' + + body.id + + '\n' + + 'uuid: ' + + body.uuid + + '\n' + + 'title: ' + + body.title + + '\n' + + 'feature: ' + + body.feature + + '\n' + + 'layout: ' + + 'page' + + '\n' + + 'tags: ' + + body.tags + + '\n' + + 'author: ' + + user.handle + + '\n' + + 'created: ' + + moment(body.created).format() + + '\n' + + 'updated: ' + + moment(Date.now()).format() + + '\n' + + 'deleted: ' + + body.deleted + + '\n' + + 'menu: ' + + body.menu + + '\n' + + 'featured: ' + + body.featured + + '\n' + + 'published: ' + + body.published + + '\n' + + 'slug: ' + + body.slug + + '\n' + + '---\n' + + body.content; + fs.writeFile('content/pages/' + body.slug + '.md', pageWrite, err => { + // throws an error, you could also catch it here + + if (err) { + response = { type: DataEvent.PAGE_ERROR, message: err }; + reject(response); + } + + // success case, the file was saved + if (task === DataEvent.API_PAGE_CREATE) { + // if new file, update settings index and page count + response = { + type: DataEvent.PAGE_ADDED, + message: 'New Page Created', + id: body.page_uuid + }; + } else { + response = { + type: DataEvent.PAGE_UPDATED, + message: 'Page saved, boss' + }; + resolve(response); + } + }); + break; + case DataEvent.API_PAGE_DELETE: + this.getPage(id) + .then(page => { + let body = _.mapValues(page.metadata); + body.content = page.content; + body.deleted = moment(Date.now()).format(); + self.editPage(body, body.uuid, DataEvent.API_PAGE_WRITE, user) + .then(() => { + response = { + type: DataEvent.PAGE_DELETED, + message: 'Page deleted, sport' + }; + resolve(response); + }) + .catch(err => { + response = { type: DataEvent.PAGE_ERROR, message: err }; + reject(response); + }); + }) + .catch(err => { + response = { type: DataEvent.PAGE_ERROR, message: err }; + reject(response); + }); + break; + } + }); + } //-------------------------- // event handlers //-------------------------- diff --git a/brain/routes/dash/pages.js b/brain/routes/dash/pages.js index eca3e34..92e8f5d 100644 --- a/brain/routes/dash/pages.js +++ b/brain/routes/dash/pages.js @@ -18,7 +18,7 @@ router.get('/list/:filter?/:page?', function(req, res) { if (filter == '' || filter == null) filter = 'all'; if (req.session.user) { book.getPage() - .then(function(pages) { + .then(pages => { pages.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id)); let all = []; let deleted = []; @@ -27,7 +27,10 @@ router.get('/list/:filter?/:page?', function(req, res) { let featured = []; for (let index = 0; index < pages.length; index++) { let item = pages[index].metadata; - if (typeof item.deleted == 'undefined' || item.deleted == false) { + if ( + typeof item.deleted === 'undefined' || + (item.deleted === false && item.layout === 'page') + ) { all.push(pages[index].metadata); if (item.published == true) published.push(pages[index].metadata); if (item.menu == true) menu.push(pages[index].metadata); diff --git a/brain/routes/dash/settings.js b/brain/routes/dash/settings.js index 2a2df63..6702c59 100644 --- a/brain/routes/dash/settings.js +++ b/brain/routes/dash/settings.js @@ -14,7 +14,6 @@ router.get('/', function(req, res) { .catch(() => { //console.error(err) }); - loadThemes().then(themes => { if (req.session.user) { let memberInfo = []; diff --git a/package.json b/package.json index 1a29a15..be678d8 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "stop": "pm2 stop init.js", "dev": "nodemon -r esm init.js --ignore node_modules/ -e js", "debug": "nodemon inspect -r esm init.js --ignore node_modules/ -e js", + "prettier-watch": "npx onchange '**/*.js' -- npx prettier --write {{changed}}", "watch-front": "stylus -w -m -o themes/$npm_package_theme/assets/css themes/$npm_package_theme/src/styles/base.styl & parcel watch themes/$npm_package_theme/src/com/Start.js --out-dir themes/$npm_package_theme/assets/js --out-file start.min.js --public-url /$npm_package_theme/assets/js", "build-front-kit": "uglifyjs node_modules/scramble-text/dist/ScrambleText.min.js node_modules/animejs/anime.min.js node_modules/reframe.js/dist/reframe.min.js -c -o themes/$npm_package_theme/assets/js/toolkit.min.js", "watch-back": "stylus -w -m -o public/assets/css src/styles/dash.styl & parcel watch src/com/Start.js --out-dir public/assets/scripts --out-file dash.min.js --public-url /assets/scripts", diff --git a/src/com/actions/PageActions.js b/src/com/actions/PageActions.js index cd413a6..d916445 100644 --- a/src/com/actions/PageActions.js +++ b/src/com/actions/PageActions.js @@ -19,11 +19,11 @@ export default class PostActions { html = html.replace(/<\/?span[^>]*>/g, ''); //removes highightjs styling html = html.replace(/<\/?br[^>]*>/g, '\n'); //convert back to encoded line break for storage pageInfo.append( - 'page_id', + 'id', document.getElementById('post-edit-index').getAttribute('data-index') ); pageInfo.append( - 'page_uuid', + 'uuid', document.getElementById('post-edit-index').getAttribute('data-uuid') ); pageInfo.append('content', html); @@ -38,15 +38,15 @@ export default class PostActions { ); pageInfo.append('tags', document.getElementById('post_tags').value); pageInfo.append( - 'pinToMenu', + 'menu', document.getElementById('option-menu-pin').getAttribute('data-active') ); pageInfo.append( - 'featureStatus', + 'featured', document.getElementById('option-feature').getAttribute('data-active') ); pageInfo.append( - 'publishedStatus', + 'published', document.getElementById('option-published').getAttribute('data-active') ); if (image != null || image != undefined) { @@ -68,19 +68,6 @@ export default class PostActions { } deletePost(id, body) { let self = this; - body.deleted = new Date().toString(); - return new Promise(function(resolve, reject) { - self.dbUtils - .archivePost(id, body) - .then(response => { - //console.log(response); - resolve(response); - }) - .catch(err => { - //console.log(err); - reject(err); - }); - }); } updateNav(add, id, post) { api.request('/api/settings/nav', DataEvent.SETTINGS_LOADED) diff --git a/src/com/controllers/PageEditor.js b/src/com/controllers/PageEditor.js index bd8971e..5a9b94f 100644 --- a/src/com/controllers/PageEditor.js +++ b/src/com/controllers/PageEditor.js @@ -1,5 +1,9 @@ //TOOLS -import ApiUtils, { REQUEST_TYPE_POST, CONTENT_TYPE_FORM } from '../utils/APIUtils'; +import ApiUtils, { + REQUEST_TYPE_POST, + CONTENT_TYPE_FORM, + CONTENT_TYPE_JSON +} from '../utils/APIUtils'; import * as DataEvent from '../events/DataEvent'; import PageActions from '../actions/PageActions'; import * as EditorEvent from '../events/EditorEvent'; @@ -17,9 +21,11 @@ export default class PostEditor { this.urlPieces = document.URL.split('/'); this.post = []; this.postID = null; + this.postUUID = null; api.authStatus(); if (document.getElementById('post-edit-index').getAttribute('data-index')) { this.postID = document.getElementById('post-edit-index').getAttribute('data-index'); + this.postUUID = document.getElementById('post-edit-index').getAttribute('data-uuid'); } if (document.getElementById('edit-post-text')) { this.editor = new TextEditor( @@ -156,14 +162,21 @@ export default class PostEditor { break; case EditorEvent.EDITOR_DELETE: if (confirm("AYE! You know you're deleting this post, right?")) { - new PageActions() - .deletePost(this.postID, this.post) + let id = { id: this.postUUID }; + api.request( + '/api/v1/page/delete', + DataEvent.API_PAGE_DELETE, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + id + ) .then(() => { - setTimeout(() => { - window.location = '/@/dashboard/posts/'; - }, 100); + console.log('DELETED'); + window.location = '/@/dashboard/page/list/'; }) - .catch(() => {}); + .catch(err => { + notify.alert(err, false); + }); } else { // Do nothing! } diff --git a/src/com/events/DataEvent.js b/src/com/events/DataEvent.js index 3faa01c..f2aa2e4 100644 --- a/src/com/events/DataEvent.js +++ b/src/com/events/DataEvent.js @@ -1,7 +1,7 @@ export const REQUEST_GOOD = 'requestGood'; export const REQUEST_LAME = 'requestLame'; -export const API_REQUEST_GOOD = 'apiUseNotAuthorized'; -export const API_REQUEST_LAME = 'apiUseAuthorized'; +export const API_REQUEST_GOOD = 'apiUseAuthorized'; +export const API_REQUEST_LAME = 'apiUseNotAuthorized'; export const IMG_REQUEST_GOOD = 'imgRequestGood'; export const IMG_REQUEST_LAME = 'imgRequestLame'; export const SETTINGS_LOADED = 'settingsLoaded'; @@ -17,6 +17,8 @@ export const AVATAR_UPLOADED = 'avatarUploaded'; export const SITE_BACKGROUND_UPLOADED = 'siteBackgroundUploaded'; export const UPLOAD_PROGRESS = 'uploadProgress'; export const API_PAGE_WRITE = 'writingItDown'; +export const API_PAGE_CREATE = 'writingNewEntry'; +export const API_PAGE_DELETE = 'erasingPage'; export const API_SETTINGS_WRITE = 'savingSettings'; export const API_IMAGES_UPLOAD = 'uploadProfileImages'; class DataEvent { diff --git a/src/com/utils/APIUtils.js b/src/com/utils/APIUtils.js index 2942743..51ca07f 100644 --- a/src/com/utils/APIUtils.js +++ b/src/com/utils/APIUtils.js @@ -55,7 +55,8 @@ export default class APIUtils { if ( eventType === DataEvent.API_PAGE_WRITE || eventType === DataEvent.API_IMAGES_UPLOAD || - eventType === DataEvent.API_SETTINGS_WRITE + eventType === DataEvent.API_SETTINGS_WRITE || + eventType === DataEvent.API_PAGE_DELETE ) request.setRequestHeader('x-access-token', self.token); switch (contentType) {