diff --git a/brain/api/v1/settings.js b/brain/api/v1/settings.js index ee31887..825d517 100644 --- a/brain/api/v1/settings.js +++ b/brain/api/v1/settings.js @@ -4,6 +4,8 @@ import Render from '../../data/Render'; import Settings, { SETTINGS_FILE, SETTINGS_FOLKS } from '../../data/Settings'; import Navigation from '../../data/Navigation'; import Book from '../../data/Book'; +import Utils from '../../data/Utils'; +import { util } from 'prettier'; const express = require('express'); const router = express.Router(); const multer = require('multer'); @@ -15,6 +17,7 @@ const render = new Render(); const book = new Book(); const settings = new Settings(); const nav = new Navigation(); +const utils = new Utils(); const uploadPath = './public/assets/images/user/' + moment().format('YYYY') + '/' + moment().format('MM'); @@ -204,6 +207,56 @@ router.post('/add-feature-background', background_upload, (req, res) => { }); } }); + +/*** + CREATE BACK UP +*/ +router.post('/create-backup', (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 + }); + }); +}); + +router.get('/download-backup', (req, res) => { + 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); + //Make secure + /* + auth.authCheck(req) + .then(() => { + + }) + .catch(err => { + res.json({ + type: err.type, + message: err.message + }); + }); + */ +}); + module.exports = router; function getBookData() { diff --git a/brain/data/Utils.js b/brain/data/Utils.js index e58ee6e..d70e2cb 100644 --- a/brain/data/Utils.js +++ b/brain/data/Utils.js @@ -7,6 +7,7 @@ const render = new Render(); const stringUtils = new StringUtils(); const moment = require('moment'); const fs = require('fs-extra'); +const AdmZip = require('adm-zip'); export default class Utils { constructor() {} @@ -130,4 +131,28 @@ 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', 'public/'); + zip.addLocalFolder('content/pages', 'pages/'); + zip.addLocalFile('site/folks.json', 'settings/'); + zip.addLocalFile('site/settings.json', 'settings/'); + zip.addLocalFile('site/tags.json', 'settings/'); + zip.writeZip('public/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); + }); + } } diff --git a/brain/routes/dash/settings.js b/brain/routes/dash/settings.js index e4fe38c..afcbd58 100644 --- a/brain/routes/dash/settings.js +++ b/brain/routes/dash/settings.js @@ -4,6 +4,7 @@ const router = express.Router(); const FileHound = require('filehound'); const fs = require('fs-extra'); const settings = new Settings(); +const moment = require('moment'); var config = []; //-------------------------- // SETTINGS @@ -39,6 +40,7 @@ router.get('/', function (req, res) { status: true, themes: themes, settings: config, + last_backup: moment(config.global.last_backup).fromNow(), member: memberInfo[0] }); } else { diff --git a/brain/views/settings.pug b/brain/views/settings.pug index 69acc7f..7b49fb9 100644 --- a/brain/views/settings.pug +++ b/brain/views/settings.pug @@ -38,7 +38,22 @@ 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 MEMBER UTILS + br + button#create-backup CREATE BACKUP + br + -if(settings.global.last_backup != null) + br + | The last back up was + a(href='/api/v1/settings/download-backup')= last_backup + -else + br + span No back ups. Frowny face. + #util-2.column + SOMETHING #option-settings.columns #theme-settings.column label THEMES diff --git a/package-lock.json b/package-lock.json index 16fcdb2..3702ace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1590,6 +1590,11 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz", "integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==" }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" + }, "agent-base": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", diff --git a/package.json b/package.json index 9cf0572..9eb6439 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "node": ">=10.16.0" }, "dependencies": { + "adm-zip": "^0.4.16", "bcrypt": "^5.0.0", "bluebird": "^3.7.2", "body-parser": "latest", diff --git a/src/com/controllers/SettingsIndex.js b/src/com/controllers/SettingsIndex.js index 1e8e7fd..74842a3 100644 --- a/src/com/controllers/SettingsIndex.js +++ b/src/com/controllers/SettingsIndex.js @@ -77,6 +77,10 @@ export default class SettingsIndex { for (i = 0, length = mailBtn.length; i < length; i++) { mailBtn[i].addEventListener('click', e => this.handleMailOptions(e)); } + //handle backup + document + .getElementById('create-backup') + .addEventListener('click', e => this.handleBackup(e)); } //-------------------------- // event handlers @@ -169,4 +173,17 @@ export default class SettingsIndex { notify.alert(err, false); }); } + handleBackup(e) { + e.preventDefault(); + e.stopPropagation(); + let task = { task: 'create_backup' }; + admin + .createBackup(task) + .then(r => { + notify.alert(r.message, true); + }) + .catch(err => { + notify.alert(err, false); + }); + } } diff --git a/src/com/events/DataEvent.js b/src/com/events/DataEvent.js index 77b1fc0..1b86762 100644 --- a/src/com/events/DataEvent.js +++ b/src/com/events/DataEvent.js @@ -27,6 +27,7 @@ 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_BACKUP_CREATE = 'createBackup'; export const API_IMAGES_UPLOAD = 'uploadProfileImages'; export const API_RENDER_PAGES = 'renderPages'; export const API_INIT = 'blogInit'; diff --git a/src/libraries/FipamoAdminAPI.js b/src/libraries/FipamoAdminAPI.js index fedcfe4..80f33a4 100644 --- a/src/libraries/FipamoAdminAPI.js +++ b/src/libraries/FipamoAdminAPI.js @@ -18,6 +18,8 @@ export const API_UPLOAD_AVATAR = '/api/v1/settings/add-avatar'; export const API_UPLOAD_BACKGROUND = '/api/v1/settings/add-feature-background'; export const API_PUBLISH_PAGES = '/api/v1/settings/publish-pages'; export const API_NAV_SYNC = '/api/v1/settings/nav-sync'; +export const API_CREATE_BACKUP = '/api/v1/settings/create-backup'; +export const API_DOWNLOAD_BACKUP = '/api/v1/settings/download-backup'; export const API_SEND_MAIL = '/api/v1/mailer'; import * as DataEvent from '../com/events/DataEvent'; export default class APIUtils { @@ -188,6 +190,23 @@ export default class APIUtils { }); }); } + createBackup(data) { + return new Promise((resolve, reject) => { + this._request( + API_CREATE_BACKUP, + DataEvent.API_BACKUP_CREATE, + REQUEST_TYPE_POST, + CONTENT_TYPE_JSON, + data + ) + .then(result => { + resolve(result); + }) + .catch(err => { + reject(err); + }); + }); + } //-------------------------- // private //-------------------------- @@ -218,7 +237,8 @@ export default class APIUtils { eventType === DataEvent.API_IMAGES_UPLOAD || eventType === DataEvent.API_SETTINGS_WRITE || eventType === DataEvent.API_PAGE_DELETE || - eventType === DataEvent.API_RENDER_PAGES + eventType === DataEvent.API_RENDER_PAGES || + eventType === DataEvent.API_BACKUP_CREATE ) request.setRequestHeader('x-access-token', self.token); diff --git a/src/styles/main/_settings.styl b/src/styles/main/_settings.styl index 28e8481..cefd5e4 100644 --- a/src/styles/main/_settings.styl +++ b/src/styles/main/_settings.styl @@ -58,7 +58,7 @@ width 100% height 45px - #member-settings, #site-settings, #option-settings + #member-settings, #site-settings, #option-settings, #member-utils background $primary padding 5px border-radius 5px 0 5px 0 @@ -66,6 +66,8 @@ label font-family $monoType color $white + span + color $secondary input width 95%