Skip to content
Snippets Groups Projects
Commit ce3a1681 authored by Cédric Moreau's avatar Cédric Moreau
Browse files

Fixing #430 Added backup export functionality

parent 9cc91874
Branches
Tags
No related merge requests found
......@@ -417,6 +417,11 @@ function WebAdmin (dbConf, overConf) {
return {};
});
this.exportData = () => co(function *() {
yield pluggedDALP;
return server.exportAllDataAsZIP();
});
function plugForConf() {
return co(function *() {
yield server.plugFileSystem();
......
......@@ -34,6 +34,7 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) {
httpMethods.httpGET( '/webmin/server/services/stop_all', webminCtrl.stopAllServices, dtos.Boolean);
httpMethods.httpGET( '/webmin/server/reset/data', webminCtrl.resetData, dtos.Boolean);
httpMethods.httpGET( '/webmin/network/interfaces', webminCtrl.listInterfaces, dtos.NetworkInterfaces);
httpMethods.httpGETFile('/webmin/data/duniter_export', webminCtrl.exportData);
}, (httpServer) => {
// Socket for synchronization events
......
......@@ -71,6 +71,10 @@ module.exports = {
return disableLimits ? createObject(NO_LIMIT_STRATEGY) : createObject(HIGH_USAGE_STRATEGY);
},
limitAsUnlimited() {
return createObject(NO_LIMIT_STRATEGY);
},
limitAsTest() {
return disableLimits ? createObject(NO_LIMIT_STRATEGY) : createObject(TEST_STRATEGY);
},
......
......@@ -104,7 +104,8 @@ module.exports = {
routingCallback(app, {
httpGET: (uri, promiseFunc, dtoContract, limiter) => handleRequest(app.get.bind(app), uri, promiseFunc, dtoContract, limiter),
httpPOST: (uri, promiseFunc, dtoContract, limiter) => handleRequest(app.post.bind(app), uri, promiseFunc, dtoContract, limiter)
httpPOST: (uri, promiseFunc, dtoContract, limiter) => handleRequest(app.post.bind(app), uri, promiseFunc, dtoContract, limiter),
httpGETFile: (uri, promiseFunc, dtoContract, limiter) => handleFileRequest(app.get.bind(app), uri, promiseFunc, limiter)
});
if (staticPath) {
......@@ -204,7 +205,8 @@ module.exports = {
})
};
const handleRequest = (method, uri, promiseFunc, dtoContract, limiter) => {
const handleRequest = (method, uri, promiseFunc, dtoContract, theLimiter) => {
const limiter = theLimiter || require('../system/limiter').limitAsUnlimited();
method(uri, function(req, res) {
res.set('Access-Control-Allow-Origin', '*');
res.type('application/json');
......@@ -229,6 +231,29 @@ const handleRequest = (method, uri, promiseFunc, dtoContract, limiter) => {
});
};
const handleFileRequest = (method, uri, promiseFunc, theLimiter) => {
const limiter = theLimiter || require('../system/limiter').limitAsUnlimited();
method(uri, function(req, res) {
res.set('Access-Control-Allow-Origin', '*');
co(function *() {
try {
if (!limiter.canAnswerNow()) {
throw constants.ERRORS.HTTP_LIMITATION;
}
limiter.processRequest();
let fileStream = yield promiseFunc(req);
// HTTP answer
fileStream.pipe(res);
} catch (e) {
let error = getResultingError(e);
// HTTP error
res.status(error.httpCode).send(JSON.stringify(error.uerr, null, " "));
throw e
}
});
});
};
function getResultingError(e) {
// Default is 500 unknown error
let error = constants.ERRORS.UNKNOWN;
......
......@@ -34,6 +34,7 @@
"url": "https://github.com/duniter/duniter/issues"
},
"dependencies": {
"archiver": "1.0.1",
"async": "1.5.2",
"bindings": "1.2.1",
"body-parser": "1.14.2",
......
......@@ -6,6 +6,7 @@ const path = require('path');
const co = require('co');
const _ = require('underscore');
const Q = require('q');
const archiver = require('archiver');
const parsers = require('./app/lib/streams/parsers');
const constants = require('./app/lib/constants');
const fileDAL = require('./app/lib/dal/fileDAL');
......@@ -29,6 +30,7 @@ function Server (dbConf, overrideConf) {
const paramsP = directory.getHomeParams(dbConf && dbConf.memory, home);
const logger = require('./app/lib/logger')('server');
const that = this;
that.home = home;
that.conf = null;
that.dal = null;
that.version = jsonpckg.version;
......@@ -73,6 +75,11 @@ function Server (dbConf, overrideConf) {
that.dal = fileDAL(params);
});
this.unplugFileSystem = () => co(function *() {
logger.debug('Unplugging file system...');
yield that.dal.close();
});
this.softResetData = () => co(function *() {
logger.debug('Soft data reset... [cache]');
yield that.dal.cleanCaches();
......@@ -270,7 +277,7 @@ function Server (dbConf, overrideConf) {
});
this.resetAll = (done) => {
const files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE];
const files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE, 'export.zip', 'import.zip'];
const dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
return resetFiles(files, dirs, done);
};
......@@ -299,6 +306,19 @@ function Server (dbConf, overrideConf) {
return that.dal.resetPeers(done);
};
this.exportAllDataAsZIP = () => co(function *() {
const params = yield paramsP;
const rootPath = params.home;
const archive = archiver('zip');
archive
.directory(rootPath + '/indicators', '/indicators', undefined, { name: 'indicators'})
.file(rootPath + '/duniter.db', { name: 'duniter.db'})
.file(rootPath + '/stats.json', { name: 'stats.json'})
.file(rootPath + '/wotb.bin', { name: 'wotb.bin'})
.finalize();
return archive;
});
this.cleanDBData = () => co(function *() {
yield _.values(that.dal.newDals).map((dal) => dal.cleanData && dal.cleanData());
that.dal.wotb.resetWoT();
......
"use strict";
const should = require('should');
const co = require('co');
const toolbox = require('../integration/tools/toolbox');
const user = require('../integration/tools/user');
const bma = require('../../app/lib/streams/bma');
const serverConfig = {
memory: false,
pair: {
pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
}
};
const s0 = toolbox.server(serverConfig);
const s1 = toolbox.server(serverConfig);
const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
describe('Import/Export', () => {
before(() => co(function *() {
yield s0.resetAll();
yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
yield cat.createIdentity();
yield tac.createIdentity();
yield cat.cert(tac);
yield tac.cert(cat);
yield cat.join();
yield tac.join();
yield s1.commit();
}));
it('should be able to export data', () => co(function *() {
const archive = yield s1.exportAllDataAsZIP();
const output = require('fs').createWriteStream(s1.home + '/export.zip');
archive.pipe(output);
return new Promise((resolve, reject) => {
archive.on('error', reject);
output.on('close', function() {
console.log(archive.pointer() + ' total bytes');
console.log('archiver has been finalized and the output file descriptor has closed.');
resolve();
});
});
}));
});
......@@ -37,7 +37,7 @@ module.exports = {
sigQty: 1
};
const server = duniter({
memory: MEMORY_MODE,
memory: conf.memory !== undefined ? conf.memory : MEMORY_MODE,
name: 'dev_unit_tests'
}, _.extend(conf, commonConf));
......
Subproject commit bd229a872a7346262530a6b6d81e046d9c6a9f72
Subproject commit 233b46694df7ce0b3bd24095c7d6bd98c8d7e6db
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment