From 580be32923cb810b3ddfc918d28b62096f8a3a1c Mon Sep 17 00:00:00 2001 From: Ro Date: Mon, 24 Aug 2020 13:02:50 -0700 Subject: [PATCH] backup functionality patch --- brain/api/v1/backup.js | 107 ++++++++ brain/api/v1/settings.js | 2 + brain/app.js | 2 + brain/data/Auth.js | 27 ++ brain/data/Utils.js | 115 +++++++++ brain/views/init.pug | 72 ++++-- brain/views/settings.pug | 20 +- init.js | 9 +- package.json | 2 +- public/assets/scripts/dash.min.js | 399 ++++++++++++++++++++---------- 10 files changed, 601 insertions(+), 154 deletions(-) create mode 100644 brain/api/v1/backup.js diff --git a/brain/api/v1/backup.js b/brain/api/v1/backup.js new file mode 100644 index 0000000..4f92bb5 --- /dev/null +++ b/brain/api/v1/backup.js @@ -0,0 +1,107 @@ +import * as DataEvent from '../../../src/com/events/DataEvent'; +import Auth from '../../data/Auth'; +import Utils from '../../data/Utils'; +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const auth = new Auth(); +const utils = new Utils(); + +var backup_upload = multer().array('backup_upload'); +var backup_restore = multer().any(); + +/*** + CREATE BACK UP +*/ +router.post('/create', (req, res) => { + auth.authCheck(req) + .then(() => { + utils + .createBackup() + .then(() => { + res.json({ + type: DataEvent.API_BACKUP_CREATE, + message: "You're backed up. Hi fives" + }); + }) + .catch(err => { + res.json({ + type: err.type, + message: err.message + }); + }); + }) + .catch(err => { + res.json({ + type: err.type, + message: err.message + }); + }); +}); + +/*** + RETRIEVE BACKUP +*/ +router.get('/download', (req, res) => { + if (req.session.user) { + var filePath = 'content/backup.zip'; // Or format the path using the `id` rest param + var fileName = 'backup.zip'; // The default name the browser will use + + res.download(filePath, fileName); + } else { + res.json({ + type: DataEvent.REQUEST_LAME, + message: "You're not logged in, champ" + }); + } + + //Move to route? +}); + +/*** + RESTORE BACKUP +*/ + +router.post('/restore', backup_upload, (req, res) => { + auth.authCheck(req) + .then(() => { + utils + .restoreBackup(req.files[0]) + .then(() => { + res.json({ + type: DataEvent.API_BACKUP_RESTORE, + message: 'Settings, files and pages restored. Nice work.' + }); + }) + .catch(err => { + res.json({ + type: err.type, + message: 'Backup not restored. Uh oh.' + }); + }); + }) + .catch(err => { + res.json({ + type: err.type, + message: err.message + }); + }); +}); + +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/api/v1/settings.js b/brain/api/v1/settings.js index ee31887..080c726 100644 --- a/brain/api/v1/settings.js +++ b/brain/api/v1/settings.js @@ -51,6 +51,7 @@ router.post('/sync', (req, res) => { .catch(err => { res.json({ type: DataEvent.REQUEST_LAME, + error: err.message, message: "Uh oh. Settings didn't take, sport" }); }); @@ -204,6 +205,7 @@ router.post('/add-feature-background', background_upload, (req, res) => { }); } }); + module.exports = router; function getBookData() { diff --git a/brain/app.js b/brain/app.js index b4260b1..0d56a48 100644 --- a/brain/app.js +++ b/brain/app.js @@ -51,12 +51,14 @@ var pages = require('./api/v1/pages'); var setting = require('./api/v1/settings'); var mailer = require('./api/v1/mailer'); var auth = require('./api/v1/auth'); +var backup = require('./api/v1/backup'); // API PATHS app.use('/api/v1/page', pages); app.use('/api/v1/settings', setting); app.use('/api/v1/auth', auth); app.use('/api/v1/mailer', mailer); +app.use('/api/v1/backup', backup); // PAGES app.use('/@/dashboard', dash); app.use('/@/dashboard/page', page); 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 498ff20..f4d0d1f 100644 --- a/brain/data/Utils.js +++ b/brain/data/Utils.js @@ -2,11 +2,14 @@ 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'; 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() {} @@ -94,6 +97,9 @@ export default class Utils { } render.publishArchive(archive); } + reindexPages(pages) { + //let byDate = _.sortBy(pages, ['created']); + } moveAssets() { settings .load(SETTINGS_FILE) @@ -127,4 +133,113 @@ export default class Utils { //console.log('ERROR', err); }); } + createBackup() { + //let self = this; + var response; + return new Promise(resolve => { + var zip = new AdmZip(); + zip.addLocalFolder('public/assets/images/blog', 'public/assets/images/blog'); + zip.addLocalFolder('content/pages', 'content/pages/'); + zip.addLocalFile('site/folks.json', 'settings/'); + zip.addLocalFile('site/settings.json', 'settings/'); + zip.addLocalFile('site/tags.json', 'settings/'); + zip.writeZip('content/backup.zip'); + fs.readJSON('site/settings.json').then(settings => { + settings.global.last_backup = moment(Date.now()).format(); + fs.writeJSON('site/settings.json', settings); + }); + + response = { + type: '', + message: 'BACKUP CREATED' + }; + + resolve(response); + }); + } + + restoreBackup(file) { + var response; + return new Promise((resolve, reject) => { + var zip = new AdmZip(file.buffer); + 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 + ); + } + zip.extractEntryTo('content/pages/index.md', 'content/pages', false, true); + } + }); + + 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(() => { + //resolve(r); + self.restoreBackup(file) + .then(() => { + response = { + type: '', + message: 'RESTORE COMPLETE' + }; + resolve(response); + }) + .catch(err => { + response = { + type: err, + 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/brain/views/settings.pug b/brain/views/settings.pug index 69acc7f..9b7c3e6 100644 --- a/brain/views/settings.pug +++ b/brain/views/settings.pug @@ -38,7 +38,25 @@ block main-content input(type='text', name='base-url' id='settings-url', placeholder='url', value=settings.global.base_url, autofocus) input(type='text', name='base-title' id='settings-title', placeholder='site title', value=settings.global.title, autofocus) textarea(id="settings-desc" type='text', name='settings_desc' class='settings-dec', placeholder='description stuff', autofocus) - =settings.global.descriptions + =settings.global.descriptions + #member-utils.columns + #util-1.column + label BACKUP TOOLS + br + button#create-backup CREATE BACKUP + br + -if(settings.global.last_backup != null) + .backup-meta + | The last back up was created + a(href='/api/v1/backup/download')= last_backup + br + -else + br + span No back ups. Frowny face. + button#restore-backup(for='backup-upload') RESTORE BACKUP + input(id="backup-upload" type="file" name="backup-upload") + #util-2.column + label MAINTENANCE #option-settings.columns #theme-settings.column label THEMES 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/package.json b/package.json index 5a9eabe..ce2a856 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "start": "pm2 --node-args='-r esm' start init.js", "stop": "pm2 stop init.js", - "debug": "nodemon inspect -r esm init.js --ignore node_modules/ -e js" + "test": "nodemon -r esm init.js --ignore node_modules/ -e js" }, "engines": { "node": ">=10.16.0" diff --git a/public/assets/scripts/dash.min.js b/public/assets/scripts/dash.min.js index e193900..ee42b0a 100644 --- a/public/assets/scripts/dash.min.js +++ b/public/assets/scripts/dash.min.js @@ -123,7 +123,7 @@ parcelRequire = (function (modules, cache, entry, globalName) { Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = exports.SEND_MAIL = exports.API_INIT_LAME = exports.API_INIT_GOOD = exports.API_INIT = exports.API_RENDER_PAGES = exports.API_IMAGES_UPLOAD = exports.API_SETTINGS_WRITE = exports.API_PAGE_DELETE = exports.API_PAGE_CREATE = exports.API_PAGE_WRITE = exports.UPLOAD_PROGRESS = exports.SITE_BACKGROUND_UPLOADED = exports.AVATAR_UPLOADED = exports.MENU_UPDATED = exports.MENU_DELETE_ITEM = exports.MENU_ADD_ITEM = exports.SETTINGS_NOT_UPDATED = exports.SETTINGS_UPDATED = exports.TAG_PAGES_NOT_RENDERED = exports.TAG_PAGES_RENDERED = exports.PAGES_NOT_RENDERED = exports.PAGES_RENDERED = exports.PAGE_DELETED = exports.PAGE_UPDATED = exports.PAGE_ADDED = exports.PAGE_ERROR = exports.FEATURE_IMAGE_ADDED = exports.POST_IMAGE_ADDED = exports.SETTINGS_LOADED = exports.IMG_REQUEST_LAME = exports.IMG_REQUEST_GOOD = exports.API_REQUEST_LAME = exports.API_REQUEST_GOOD = exports.REQUEST_LAME = exports.REQUEST_GOOD = void 0; +exports.default = exports.SEND_MAIL = exports.API_INIT_LAME = exports.API_INIT_GOOD = exports.API_INIT = exports.API_RENDER_PAGES = exports.API_IMAGES_UPLOAD = exports.API_BACKUP_RESTORE = exports.API_BACKUP_DOWNLOAD = exports.API_BACKUP_CREATE = exports.API_SETTINGS_WRITE = exports.API_PAGE_DELETE = exports.API_PAGE_CREATE = exports.API_PAGE_WRITE = exports.UPLOAD_PROGRESS = exports.SITE_BACKGROUND_UPLOADED = exports.AVATAR_UPLOADED = exports.MENU_UPDATED = exports.MENU_DELETE_ITEM = exports.MENU_ADD_ITEM = exports.SETTINGS_NOT_UPDATED = exports.SETTINGS_UPDATED = exports.TAG_PAGES_NOT_RENDERED = exports.TAG_PAGES_RENDERED = exports.PAGES_NOT_RENDERED = exports.PAGES_RENDERED = exports.PAGE_DELETED = exports.PAGE_UPDATED = exports.PAGE_ADDED = exports.PAGE_ERROR = exports.FEATURE_IMAGE_ADDED = exports.POST_IMAGE_ADDED = exports.SETTINGS_LOADED = exports.IMG_REQUEST_LAME = exports.IMG_REQUEST_GOOD = exports.API_REQUEST_LAME = exports.API_REQUEST_GOOD = exports.REQUEST_LAME = exports.REQUEST_GOOD = void 0; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -185,6 +185,12 @@ var API_PAGE_DELETE = 'erasingPage'; exports.API_PAGE_DELETE = API_PAGE_DELETE; var API_SETTINGS_WRITE = 'savingSettings'; exports.API_SETTINGS_WRITE = API_SETTINGS_WRITE; +var API_BACKUP_CREATE = 'createBackup'; +exports.API_BACKUP_CREATE = API_BACKUP_CREATE; +var API_BACKUP_DOWNLOAD = 'downloadBackup'; +exports.API_BACKUP_DOWNLOAD = API_BACKUP_DOWNLOAD; +var API_BACKUP_RESTORE = 'downloadBackup'; +exports.API_BACKUP_RESTORE = API_BACKUP_RESTORE; var API_IMAGES_UPLOAD = 'uploadProfileImages'; exports.API_IMAGES_UPLOAD = API_IMAGES_UPLOAD; var API_RENDER_PAGES = 'renderPages'; @@ -368,130 +374,13 @@ var APIUtils = /*#__PURE__*/function () { }(); exports.default = APIUtils; -},{"../com/events/DataEvent":"events/DataEvent.js"}],"utils/DataUtils.js":[function(require,module,exports) { +},{"../com/events/DataEvent":"events/DataEvent.js"}],"../libraries/FipamoAdminAPI.js":[function(require,module,exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = void 0; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } - -var DataUtils = /*#__PURE__*/function () { - //-------------------------- - // constructor - //-------------------------- - function DataUtils() { - _classCallCheck(this, DataUtils); - } //-------------------------- - // methods - //-------------------------- - - - _createClass(DataUtils, [{ - key: "imgLoad", - value: function imgLoad(url) { - 'use strict'; // Create new promise with the Promise() constructor; - // This has as its argument a function with two parameters, resolve and reject - - return new Promise(function (resolve, reject) { - // Standard XHR to load an image - var request = new XMLHttpRequest(); - request.open('GET', url); - request.responseType = 'blob'; // When the request loads, check whether it was successful - - request.onload = function () { - if (request.status === 200) { - // If successful, resolve the promise by passing back the request response - resolve(request.response); - } else { - // If it fails, reject the promise with a error message - reject(new Error("Image didn't load successfully; error code:" + request.statusText)); - } - }; - - request.onerror = function () { - // Also deal with the case when the entire request fails to begin with - // This is probably a network error, so reject the promise with an appropriate message - reject(new Error('There was a network error.')); - }; // Send the request - - - request.send(); - }); - } - }, { - key: "loadImage", - value: function loadImage(src) { - 'use strict'; - - var self = this; - return new Promise(function (resolve, reject) { - // Get a reference to the body element, and create a new image object - var myImage = new Image(); - myImage.crossOrigin = ''; // or "anonymous" - // Call the function with the URL we want to load, but then chain the - // promise then() method on to the end of it. This contains two callbacks - - self.imgLoad(src).then(function (response) { - // The first runs when the promise resolves, with the request.reponse specified within the resolve() method. - var imageURL = window.URL.createObjectURL(response); - resolve(imageURL); //$('background-content').setStyle('background-image', 'url('+imageURL+')') //myImage.src = imageURL; - //console.log(imageURL); - //body.appendChild(myImage); - // The second runs when the promise is rejected, and logs the Error specified with the reject() method. - }, function (Error) { - reject(Error); - }); - }); - } - /** - * Create a function to convert the serialize and convert the form data to JSON - * @param : $('#form_example'); - * @return a JSON Stringify - */ - - }, { - key: "formDataToJSON", - value: function formDataToJSON(form) { - var object = {}; - var formData = new FormData(form); - formData.forEach(function (value, key) { - if (!object.hasOwnProperty(key)) { - object[key] = value; - return; - } - - if (!Array.isArray(object[key])) { - object[key] = [object[key]]; - } - - object[key].push(value); - }); //let json = JSON.stringify(object); - - return object; - } //-------------------------- - // event handlers - //-------------------------- - - }]); - - return DataUtils; -}(); - -exports.default = DataUtils; -},{}],"../libraries/FipamoAdminAPI.js":[function(require,module,exports) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = exports.API_SEND_MAIL = exports.API_NAV_SYNC = exports.API_PUBLISH_PAGES = exports.API_UPLOAD_BACKGROUND = exports.API_UPLOAD_AVATAR = exports.API_SETTINGS_SYNC = exports.API_IMAGE_UPLOAD = exports.API_DELETE_PAGE = exports.API_EDIT_PAGE = exports.API_NEW_PAGE = exports.API_GET_NAV = exports.API_STATUS = exports.CONTENT_TYPE_FORM = exports.CONTENT_TYPE_JSON = exports.TASK_PAGE_DELETE = exports.TASK_PAGE_EDIT = exports.TASK_PAGE_CREATE = exports.REQUEST_TYPE_DELETE = exports.REQUEST_TYPE_PUT = exports.REQUEST_TYPE_GET = exports.REQUEST_TYPE_POST = void 0; +exports.default = exports.API_SEND_MAIL = exports.API_INIT_RESTORE_BACKUP = exports.API_RESTORE_BACKUP = exports.API_DOWNLOAD_BACKUP = exports.API_CREATE_BACKUP = exports.API_NAV_SYNC = exports.API_PUBLISH_PAGES = exports.API_UPLOAD_BACKGROUND = exports.API_UPLOAD_AVATAR = exports.API_SETTINGS_SYNC = exports.API_IMAGE_UPLOAD = exports.API_DELETE_PAGE = exports.API_EDIT_PAGE = exports.API_NEW_PAGE = exports.API_GET_NAV = exports.API_STATUS = exports.CONTENT_TYPE_FORM = exports.CONTENT_TYPE_JSON = exports.TASK_PAGE_DELETE = exports.TASK_PAGE_EDIT = exports.TASK_PAGE_CREATE = exports.REQUEST_TYPE_DELETE = exports.REQUEST_TYPE_PUT = exports.REQUEST_TYPE_GET = exports.REQUEST_TYPE_POST = void 0; var DataEvent = _interopRequireWildcard(require("../com/events/DataEvent")); @@ -545,6 +434,14 @@ var API_PUBLISH_PAGES = '/api/v1/settings/publish-pages'; exports.API_PUBLISH_PAGES = API_PUBLISH_PAGES; var API_NAV_SYNC = '/api/v1/settings/nav-sync'; exports.API_NAV_SYNC = API_NAV_SYNC; +var API_CREATE_BACKUP = '/api/v1/backup/create'; +exports.API_CREATE_BACKUP = API_CREATE_BACKUP; +var API_DOWNLOAD_BACKUP = '/api/v1/backup/download'; +exports.API_DOWNLOAD_BACKUP = API_DOWNLOAD_BACKUP; +var API_RESTORE_BACKUP = '/api/v1/backup/restore'; +exports.API_RESTORE_BACKUP = API_RESTORE_BACKUP; +var API_INIT_RESTORE_BACKUP = '/api/v1/backup/init-restore'; +exports.API_INIT_RESTORE_BACKUP = API_INIT_RESTORE_BACKUP; var API_SEND_MAIL = '/api/v1/mailer'; exports.API_SEND_MAIL = API_SEND_MAIL; @@ -708,6 +605,67 @@ var APIUtils = /*#__PURE__*/function () { reject(err); }); }); + } + }, { + key: "handleBackup", + value: function handleBackup(id, files) { + var _this8 = this; + + return new Promise(function (resolve, reject) { + var url, event, method, type, data; + + if (id === 'create-backup') { + url = API_CREATE_BACKUP; + event = DataEvent.API_BACKUP_CREATE; + method = REQUEST_TYPE_POST; + type = CONTENT_TYPE_JSON; + data = { + task: 'create_backup' + }; + } else { + url = API_RESTORE_BACKUP; + event = DataEvent.API_BACKUP_RESTORE; + method = REQUEST_TYPE_POST; + type = CONTENT_TYPE_FORM; + data = new FormData(); + + for (var i = 0; i < files.length; i++) { + var file = files[i]; // Check the file type. + + if (!file.type.match('application.zip')) { + continue; + } + + data.append('backup_upload', file, file.name); + } + } + + _this8._request(url, event, method, type, data).then(function (result) { + resolve(result); + }).catch(function (err) { + reject(err); + }); + }); + } + }, { + key: "handleInitRestore", + value: function handleInitRestore(form) { + var _this9 = this; + + return new Promise(function (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); + + _this9._request(url, event, method, type, data).then(function (result) { + resolve(result); + }).catch(function (err) { + reject(err); + }); + }); } //-------------------------- // private //-------------------------- @@ -735,7 +693,7 @@ var APIUtils = /*#__PURE__*/function () { }; if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) { - if (eventType === DataEvent.API_PAGE_WRITE || eventType === DataEvent.API_IMAGES_UPLOAD || eventType === DataEvent.API_SETTINGS_WRITE || eventType === DataEvent.API_PAGE_DELETE || eventType === DataEvent.API_RENDER_PAGES) request.setRequestHeader('x-access-token', self.token); + if (eventType === DataEvent.API_PAGE_WRITE || eventType === DataEvent.API_IMAGES_UPLOAD || eventType === DataEvent.API_SETTINGS_WRITE || eventType === DataEvent.API_PAGE_DELETE || eventType === DataEvent.API_RENDER_PAGES || eventType === DataEvent.API_BACKUP_CREATE || eventType === DataEvent.API_BACKUP_RESTORE) request.setRequestHeader('x-access-token', self.token); switch (contentType) { case CONTENT_TYPE_JSON: @@ -766,7 +724,124 @@ var APIUtils = /*#__PURE__*/function () { }(); exports.default = APIUtils; -},{"../com/events/DataEvent":"events/DataEvent.js"}],"utils/StringUtils.js":[function(require,module,exports) { +},{"../com/events/DataEvent":"events/DataEvent.js"}],"utils/DataUtils.js":[function(require,module,exports) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var DataUtils = /*#__PURE__*/function () { + //-------------------------- + // constructor + //-------------------------- + function DataUtils() { + _classCallCheck(this, DataUtils); + } //-------------------------- + // methods + //-------------------------- + + + _createClass(DataUtils, [{ + key: "imgLoad", + value: function imgLoad(url) { + 'use strict'; // Create new promise with the Promise() constructor; + // This has as its argument a function with two parameters, resolve and reject + + return new Promise(function (resolve, reject) { + // Standard XHR to load an image + var request = new XMLHttpRequest(); + request.open('GET', url); + request.responseType = 'blob'; // When the request loads, check whether it was successful + + request.onload = function () { + if (request.status === 200) { + // If successful, resolve the promise by passing back the request response + resolve(request.response); + } else { + // If it fails, reject the promise with a error message + reject(new Error("Image didn't load successfully; error code:" + request.statusText)); + } + }; + + request.onerror = function () { + // Also deal with the case when the entire request fails to begin with + // This is probably a network error, so reject the promise with an appropriate message + reject(new Error('There was a network error.')); + }; // Send the request + + + request.send(); + }); + } + }, { + key: "loadImage", + value: function loadImage(src) { + 'use strict'; + + var self = this; + return new Promise(function (resolve, reject) { + // Get a reference to the body element, and create a new image object + var myImage = new Image(); + myImage.crossOrigin = ''; // or "anonymous" + // Call the function with the URL we want to load, but then chain the + // promise then() method on to the end of it. This contains two callbacks + + self.imgLoad(src).then(function (response) { + // The first runs when the promise resolves, with the request.reponse specified within the resolve() method. + var imageURL = window.URL.createObjectURL(response); + resolve(imageURL); //$('background-content').setStyle('background-image', 'url('+imageURL+')') //myImage.src = imageURL; + //console.log(imageURL); + //body.appendChild(myImage); + // The second runs when the promise is rejected, and logs the Error specified with the reject() method. + }, function (Error) { + reject(Error); + }); + }); + } + /** + * Create a function to convert the serialize and convert the form data to JSON + * @param : $('#form_example'); + * @return a JSON Stringify + */ + + }, { + key: "formDataToJSON", + value: function formDataToJSON(form) { + var object = {}; + var formData = new FormData(form); + formData.forEach(function (value, key) { + if (!object.hasOwnProperty(key)) { + object[key] = value; + return; + } + + if (!Array.isArray(object[key])) { + object[key] = [object[key]]; + } + + object[key].push(value); + }); //let json = JSON.stringify(object); + + return object; + } //-------------------------- + // event handlers + //-------------------------- + + }]); + + return DataUtils; +}(); + +exports.default = DataUtils; +},{}],"utils/StringUtils.js":[function(require,module,exports) { "use strict"; Object.defineProperty(exports, "__esModule", { @@ -4149,11 +4224,17 @@ var SettingsIndex = /*#__PURE__*/function () { document.getElementById('background').addEventListener('click', function () { document.getElementById('background-upload').click(); }); + document.getElementById('restore-backup').addEventListener('click', function () { + document.getElementById('backup-upload').click(); + }); document.getElementById('avatar-upload').addEventListener('change', function (e) { self.handleImageUpload(e.target.id, e.target.files); }, false); document.getElementById('background-upload').addEventListener('change', function (e) { self.handleImageUpload(e.target.id, e.target.files); + }, false); + document.getElementById('backup-upload').addEventListener('change', function (e) { + self.handleBackup(e); }, false); //handle privacy toggle //document //.getElementById('privacy-toggle') @@ -4184,7 +4265,12 @@ var SettingsIndex = /*#__PURE__*/function () { mailBtn[i].addEventListener('click', function (e) { return _this.handleMailOptions(e); }); - } + } //handle backup + + + document.getElementById('create-backup').addEventListener('click', function (e) { + return _this.handleBackup(e); + }); } //-------------------------- // event handlers //-------------------------- @@ -4287,6 +4373,17 @@ var SettingsIndex = /*#__PURE__*/function () { notify.alert(err, false); }); } + }, { + key: "handleBackup", + value: function handleBackup(e) { + e.preventDefault(); + e.stopPropagation(); + admin.handleBackup(e.target.id, e.target.files).then(function (r) { + notify.alert(r.message, true); + }).catch(function (err) { + notify.alert(err, false); + }); + } }]); return SettingsIndex; @@ -8251,6 +8348,8 @@ exports.default = void 0; var _FipamoAPI = _interopRequireDefault(require("../libraries/FipamoAPI")); +var _FipamoAdminAPI = _interopRequireDefault(require("../libraries/FipamoAdminAPI")); + var _DataUtils = _interopRequireDefault(require("./utils/DataUtils")); var DataEvent = _interopRequireWildcard(require("./events/DataEvent")); @@ -8272,6 +8371,7 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var api = new _FipamoAPI.default(); +var admin = new _FipamoAdminAPI.default(); var data = new _DataUtils.default(); var notify = new _Notifications.default(); @@ -8294,6 +8394,14 @@ var Base = /*#__PURE__*/function () { var _this = this; if (document.getElementById('dash-form') || document.getElementById('dash-init')) { + var options = document.getElementsByClassName('init-option'); + + for (var index = 0; index < options.length; index++) { + options[index].addEventListener('click', function (e) { + return _this.handleOptions(e); + }); + } + if (document.getElementById('dash-form')) { document.getElementById('login-btn').addEventListener('click', function (e) { return _this.handleLogin(e); @@ -8302,6 +8410,9 @@ var Base = /*#__PURE__*/function () { document.getElementById('init-blog').addEventListener('click', function (e) { return _this.handleSetup(e); }); + document.getElementById('blog-restore').addEventListener('click', function (e) { + return _this.handleRestore(e); + }); } } else { new _DashManager.default(); @@ -8348,13 +8459,51 @@ var Base = /*#__PURE__*/function () { notify.alert(err, false); }); } + }, { + key: "handleRestore", + value: function handleRestore(e) { + e.stopPropagation(); + e.preventDefault(); + var form = document.getElementById('init-restore'); + admin.handleInitRestore(form).then(function (response) { + if (response.type === DataEvent.REQUEST_LAME) { + notify.alert(response.message, false); + } else { + notify.alert(response.message, true); + setTimeout(function () {//window.location = '/@/dashboard'; + }, 700); + } + }).catch(function (err) { + notify.alert(err, false); + }); + } + }, { + key: "handleOptions", + value: function handleOptions(e) { + e.stopPropagation(); + e.preventDefault(); + var init = document.getElementById('dash-init'); + var 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'; + } + } }]); return Base; }(); exports.default = Base; -},{"../libraries/FipamoAPI":"../libraries/FipamoAPI.js","./utils/DataUtils":"utils/DataUtils.js","./events/DataEvent":"events/DataEvent.js","./controllers/DashManager":"controllers/DashManager.js","./ui/Notifications":"ui/Notifications.js"}],"Start.js":[function(require,module,exports) { +},{"../libraries/FipamoAPI":"../libraries/FipamoAPI.js","../libraries/FipamoAdminAPI":"../libraries/FipamoAdminAPI.js","./utils/DataUtils":"utils/DataUtils.js","./events/DataEvent":"events/DataEvent.js","./controllers/DashManager":"controllers/DashManager.js","./ui/Notifications":"ui/Notifications.js"}],"Start.js":[function(require,module,exports) { "use strict"; var _Base = _interopRequireDefault(require("./Base")); @@ -8392,7 +8541,7 @@ var parent = module.bundle.parent; if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') { var hostname = "" || location.hostname; var protocol = location.protocol === 'https:' ? 'wss' : 'ws'; - var ws = new WebSocket(protocol + '://' + hostname + ':' + "61846" + '/'); + var ws = new WebSocket(protocol + '://' + hostname + ':' + "59699" + '/'); ws.onmessage = function (event) { checkedAssets = {};