From 126361fca86bf48fdf88b21b0b191eff3cd7176a Mon Sep 17 00:00:00 2001 From: Ro Date: Tue, 18 Aug 2020 15:47:04 -0700 Subject: [PATCH] updated init with empty config fix, added site restore by verifying backup file --- brain/api/v1/backup.js | 17 +++++ brain/data/Auth.js | 27 ++++++++ brain/data/Utils.js | 107 +++++++++++++++++++++++--------- brain/views/init.pug | 72 +++++++++++++-------- init.js | 9 ++- src/com/Base.js | 48 ++++++++++++++ src/libraries/FipamoAdminAPI.js | 19 ++++++ src/styles/main/_index.styl | 5 +- 8 files changed, 248 insertions(+), 56 deletions(-) diff --git a/brain/api/v1/backup.js b/brain/api/v1/backup.js index b3d857c..4f92bb5 100644 --- a/brain/api/v1/backup.js +++ b/brain/api/v1/backup.js @@ -8,6 +8,7 @@ const auth = new Auth(); const utils = new Utils(); var backup_upload = multer().array('backup_upload'); +var backup_restore = multer().any(); /*** CREATE BACK UP @@ -87,4 +88,20 @@ router.post('/restore', backup_upload, (req, res) => { }); }); +router.post('/init-restore', backup_restore, (req, res) => { + utils + .verifyBackup(req.files[0], req.body) + .then(response => { + res.json({ + type: response.type, + message: response.message + }); + }) + .catch(err => { + res.json({ + type: err.type, + message: err.message + }); + }); +}); module.exports = router; diff --git a/brain/data/Auth.js b/brain/data/Auth.js index cb734f7..8d4d282 100644 --- a/brain/data/Auth.js +++ b/brain/data/Auth.js @@ -1,6 +1,7 @@ import * as DataEvent from '../../src/com/events/DataEvent'; const bCrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); +const _ = require('lodash'); export default class Auth { //-------------------------- @@ -64,6 +65,32 @@ export default class Auth { }); } + verifyCredentials(config, credentials) { + return new Promise((resolve, reject) => { + var found = _.find(config, { handle: credentials.handle }); + var response; + if (found) { + if (!this.isValidPassword(found, credentials.pass)) { + response = { + type: DataEvent.REQUEST_LAME, + message: 'CHECK YOUR PASSWORD' + }; + reject(response); + } + + response = { type: DataEvent.REQUEST_GOOD, message: 'Backup Verified. Restoring' }; + resolve(response); + } else { + response = { type: DataEvent.REQUEST_LAME, message: 'Handle not found, boss' }; + reject(response); + } + }); + } + + isValidPassword(user, password) { + return bCrypt.compareSync(password, user.password); + } + /** * Checks to make sure received token matches * @parameter token: created token diff --git a/brain/data/Utils.js b/brain/data/Utils.js index 962ea6f..39f2742 100644 --- a/brain/data/Utils.js +++ b/brain/data/Utils.js @@ -2,12 +2,15 @@ import Settings, { SETTINGS_FILE } from './Settings'; import Render from './Render'; import StringUtils from '../../src/com/utils/StringUtils'; import _ from 'lodash'; +import Auth from '../data/Auth'; +import DataEvent from '../../src/com/events/DataEvent'; const settings = new Settings(); const render = new Render(); const stringUtils = new StringUtils(); const moment = require('moment'); const fs = require('fs-extra'); const AdmZip = require('adm-zip'); +const auth = new Auth(); export default class Utils { constructor() {} @@ -157,39 +160,87 @@ export default class Utils { } restoreBackup(file) { - //var response; + var response; return new Promise((resolve, reject) => { var zip = new AdmZip(file.buffer); - zip.extractEntryTo('settings/settings.json', 'site', false, true); - zip.extractEntryTo('settings/folks.json', 'site', false, true); - zip.extractEntryTo('settings/tags.json', 'site', false, true); - zip.getEntries().forEach(function (entry) { - var entryName = entry.entryName; - var list = entryName.split('/'); - if (list[0] === 'public') { - if (list[6]) { - zip.extractEntryTo( - entryName, - 'public/assets/images/blog/' + list[4] + '/' + list[5], - false, - true - ); + try { + zip.extractEntryTo('settings/settings.json', 'site', false, true); + zip.extractEntryTo('settings/folks.json', 'site', false, true); + zip.extractEntryTo('settings/tags.json', 'site', false, true); + zip.getEntries().forEach(function (entry) { + var entryName = entry.entryName; + var list = entryName.split('/'); + if (list[0] === 'public') { + if (list[6]) { + zip.extractEntryTo( + entryName, + 'public/assets/images/blog/' + list[4] + '/' + list[5], + false, + true + ); + } } - } - if (list[0] === 'content') { - if (list[4]) { - zip.extractEntryTo( - entryName, - 'content/pages/' + list[2] + '/' + list[3], - false, - true - ); + if (list[0] === 'content') { + if (list[4]) { + zip.extractEntryTo( + entryName, + 'content/pages/' + list[2] + '/' + list[3], + false, + true + ); + } + zip.extractEntryTo('content/pages/index.md', 'content/pages', false, true); } - zip.extractEntryTo('content/pages/index.md', 'content/pages', false, true); - } - }); + }); - resolve(); + resolve(); + } catch (error) { + response = { + type: error, + message: 'ERROR READING BACKUP' + }; + reject(response); + } + }); + } + + verifyBackup(file, body) { + var response; + var zip = new AdmZip(file.buffer); + var credentials = { handle: body.restore_member_handle, pass: body.restore_member_pass }; + var self = this; + return new Promise((resolve, reject) => { + try { + let folks = JSON.parse(zip.readAsText('settings/folks.json')); + auth.verifyCredentials(folks, credentials) + .then(r => { + //resolve(r); + self.restoreBackup(file) + .then(() => { + response = { + type: '', + message: 'RESTORE COMPLETE' + }; + resolve(response); + }) + .catch(err => { + response = { + type: 'error', + message: 'ERROR RESTORING BACKUP' + }; + }); + }) + .catch(err => { + reject(err); + }); + } catch (error) { + response = { + type: 'error', + message: 'ERROR READING BACKUP FILE' + }; + + reject(response); + } }); } } diff --git a/brain/views/init.pug b/brain/views/init.pug index 9dd7189..02fe5a1 100644 --- a/brain/views/init.pug +++ b/brain/views/init.pug @@ -2,30 +2,52 @@ extends frame block main-content #dash-index #dash-index-wrapper - .dash-init#dash-init + .dash-init#dash-init + br + form#init-form + h1 What up. + p Just fill these in and it'll get you started. + label What's your handle? br - form#init-form - h1 What up. - p Just fill these in and it'll get you started. - label What's your handle? - br - input.large(type='text', name='new_member_handle' id='new_member_handle', placeholder="What\'s your handle?") - br - label Let's get that email - br - input.large(type='text', name='new_member_email' id='new_member_email', placeholder="Email Please") - br - label Let's get a password - br - input.large(type='password', name='new_member_pass' id='new_member_pass', placeholder="Password Please") - br - label And let's confirm that password - br - input.large(type='password', name='new_member_pass2' id='new_member_pass2', placeholder="Email Confirm") - br - label And finally, a title - br - input.large(type='text', name='new_member_title' id='new_member_title', placeholder="Site Title Please") - br - button#init-blog(data-action='blog-init' type='submit') SET IT UP + input.large(type='text', name='new_member_handle' id='new_member_handle', placeholder="What\'s your handle?") + br + label Let's get that email + br + input.large(type='text', name='new_member_email' id='new_member_email', placeholder="Email Please") + br + label Let's get a password + br + input.large(type='password', name='new_member_pass' id='new_member_pass', placeholder="Password Please") + br + label And let's confirm that password + br + input.large(type='password', name='new_member_pass2' id='new_member_pass2', placeholder="Email Confirm") + br + label And finally, a title + br + input.large(type='text', name='new_member_title' id='new_member_title', placeholder="Site Title Please") + br + button#init-blog(data-action='blog-init' type='submit') SET IT UP + .option + button.init-option#init-switch-restore OR RESTORE FROM BACKUP + + .dash-restore#dash-restore + form#init-restore + h1 Restore backup. + p Let's verify your backup + label What's your handle? + br + input.large(type='text', name='restore_member_handle' id='restore_member_handle', placeholder="What\'s your handle?") + br + label Let's get a password + br + input.large(type='password', name='restore_member_pass' id='restore_member_pass', placeholder="Password Please") + br + label Backup File + br + input(id="backup-upload" type="file" name="backup-upload") + br + button#blog-restore(data-action='blog-restore' type='submit') RESTORE + .option + button.init-option#init-switch-fresh OR INSTALL FRESH SITE diff --git a/init.js b/init.js index e040ec7..7f7bc94 100644 --- a/init.js +++ b/init.js @@ -7,13 +7,18 @@ var app = require('./brain/app'); var debug = require('debug')('fipamo:server'); var http = require('http'); -var config = require('./site/settings.json'); /** * Get port from environment and store in Express. */ -var port = normalizePort(process.env.PORT || config.global.port); +try { + var configPort = require('./site/settings.json').global.port; +} catch (err) { + console.log('settings.json not found, assuming this is a first run...'); +} + +var port = normalizePort(configPort || process.env.PORT || 2314); app.set('port', port); /** diff --git a/src/com/Base.js b/src/com/Base.js index 312e208..4c47011 100644 --- a/src/com/Base.js +++ b/src/com/Base.js @@ -1,10 +1,12 @@ import FipamoApi from '../libraries/FipamoAPI'; +import FipamoAdminAPI from '../libraries/FipamoAdminAPI'; import DataUitls from './utils/DataUtils'; import * as DataEvent from './events/DataEvent'; import DashManager from './controllers/DashManager'; import Notfications from './ui/Notifications'; const api = new FipamoApi(); +const admin = new FipamoAdminAPI(); const data = new DataUitls(); const notify = new Notfications(); @@ -21,6 +23,10 @@ export default class Base { //-------------------------- start() { if (document.getElementById('dash-form') || document.getElementById('dash-init')) { + var options = document.getElementsByClassName('init-option'); + for (let index = 0; index < options.length; index++) { + options[index].addEventListener('click', e => this.handleOptions(e)); + } if (document.getElementById('dash-form')) { document .getElementById('login-btn') @@ -29,6 +35,9 @@ export default class Base { document .getElementById('init-blog') .addEventListener('click', e => this.handleSetup(e)); + document + .getElementById('blog-restore') + .addEventListener('click', e => this.handleRestore(e)); } } else { new DashManager(); @@ -76,4 +85,43 @@ export default class Base { notify.alert(err, false); }); } + handleRestore(e) { + e.stopPropagation(); + e.preventDefault(); + var form = document.getElementById('init-restore'); + admin + .handleInitRestore(form) + .then(response => { + if (response.type === DataEvent.REQUEST_LAME) { + notify.alert(response.message, false); + } else { + notify.alert(response.message, true); + setTimeout(() => { + //window.location = '/@/dashboard'; + }, 700); + } + }) + .catch(err => { + notify.alert(err, false); + }); + } + handleOptions(e) { + e.stopPropagation(); + e.preventDefault(); + let init = document.getElementById('dash-init'); + let restore = document.getElementById('dash-restore'); + if (e.target.id === 'init-switch-restore') { + init.style.display = 'none'; + init.style.visibility = 'hidden'; + + restore.style.display = 'block'; + restore.style.visibility = 'visible'; + } else { + init.style.display = 'block'; + init.style.visibility = 'visible'; + + restore.style.display = 'none'; + restore.style.visibility = 'hidden'; + } + } } diff --git a/src/libraries/FipamoAdminAPI.js b/src/libraries/FipamoAdminAPI.js index 694fd36..7436dec 100644 --- a/src/libraries/FipamoAdminAPI.js +++ b/src/libraries/FipamoAdminAPI.js @@ -21,6 +21,7 @@ export const API_NAV_SYNC = '/api/v1/settings/nav-sync'; export const API_CREATE_BACKUP = '/api/v1/backup/create'; export const API_DOWNLOAD_BACKUP = '/api/v1/backup/download'; export const API_RESTORE_BACKUP = '/api/v1/backup/restore'; +export const API_INIT_RESTORE_BACKUP = '/api/v1/backup/init-restore'; export const API_SEND_MAIL = '/api/v1/mailer'; import * as DataEvent from '../com/events/DataEvent'; export default class APIUtils { @@ -225,6 +226,24 @@ export default class APIUtils { }); }); } + handleInitRestore(form) { + return new Promise((resolve, reject) => { + var url, event, method, type, data; + + url = API_INIT_RESTORE_BACKUP; + event = DataEvent.API_BACKUP_RESTORE; + method = REQUEST_TYPE_POST; + type = CONTENT_TYPE_FORM; + data = new FormData(form); + this._request(url, event, method, type, data) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } //-------------------------- // private //-------------------------- diff --git a/src/styles/main/_index.styl b/src/styles/main/_index.styl index e3d6543..03d905f 100644 --- a/src/styles/main/_index.styl +++ b/src/styles/main/_index.styl @@ -14,7 +14,7 @@ height 100% margin 0 auto - .dash-init + .dash-init, .dash-restore width 100% max-width 900px margin 0 auto @@ -40,6 +40,9 @@ width 120px margin 0 auto display block + .dash-restore + display: none; + visibility hidden; #dash-login width 100%