140 lines
5.9 KiB
JavaScript
140 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
create,
|
|
cleanup,
|
|
remount,
|
|
getMountStatus,
|
|
|
|
getConfig,
|
|
setStorage,
|
|
setLimits,
|
|
|
|
getPolicy,
|
|
setPolicy
|
|
};
|
|
|
|
const assert = require('assert'),
|
|
AuditSource = require('../auditsource.js'),
|
|
backupTargets = require('../backuptargets.js'),
|
|
BoxError = require('../boxerror.js'),
|
|
HttpError = require('@cloudron/connect-lastmile').HttpError,
|
|
HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess,
|
|
safe = require('safetydance');
|
|
|
|
async function create(req, res, next) {
|
|
const [error, taskId] = await safe(backupTargets.startBackupTask(AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, { taskId }));
|
|
}
|
|
|
|
async function cleanup(req, res, next) {
|
|
const [error, taskId] = await safe(backupTargets.startCleanupTask(AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, { taskId }));
|
|
}
|
|
|
|
async function remount(req, res, next) {
|
|
const [error] = await safe(backupTargets.remount());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, {}));
|
|
}
|
|
|
|
async function getMountStatus(req, res, next) {
|
|
const [error, mountStatus] = await safe(backupTargets.getMountStatus());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
next(new HttpSuccess(200, mountStatus));
|
|
}
|
|
|
|
async function getConfig(req, res, next) {
|
|
const [error, backupConfig] = await safe(backupTargets.getConfig());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, backupTargets.removePrivateFields(backupConfig)));
|
|
}
|
|
|
|
async function setLimits(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
const limits = req.body;
|
|
|
|
if ('syncConcurrency' in limits) {
|
|
if (typeof limits.syncConcurrency !== 'number') return next(new HttpError(400, 'syncConcurrency must be a positive integer'));
|
|
if (limits.syncConcurrency < 1) return next(new HttpError(400, 'syncConcurrency must be a positive integer'));
|
|
}
|
|
if ('copyConcurrency' in limits) {
|
|
if (typeof limits.copyConcurrency !== 'number') return next(new HttpError(400, 'copyConcurrency must be a positive integer'));
|
|
if (limits.copyConcurrency < 1) return next(new HttpError(400, 'copyConcurrency must be a positive integer'));
|
|
}
|
|
if ('downloadConcurrency' in limits) {
|
|
if (typeof limits.downloadConcurrency !== 'number') return next(new HttpError(400, 'downloadConcurrency must be a positive integer'));
|
|
if (limits.downloadConcurrency < 1) return next(new HttpError(400, 'downloadConcurrency must be a positive integer'));
|
|
}
|
|
if ('deleteConcurrency' in limits) {
|
|
if (typeof limits.deleteConcurrency !== 'number') return next(new HttpError(400, 'deleteConcurrency must be a positive integer'));
|
|
if (limits.deleteConcurrency < 1) return next(new HttpError(400, 'deleteConcurrency must be a positive integer'));
|
|
}
|
|
if ('uploadPartSize' in limits) {
|
|
if (typeof limits.uploadPartSize !== 'number') return next(new HttpError(400, 'uploadPartSize must be a positive integer'));
|
|
if (limits.uploadPartSize < 1) return next(new HttpError(400, 'uploadPartSize must be a positive integer'));
|
|
}
|
|
|
|
if ('memoryLimit' in limits && typeof limits.memoryLimit !== 'number') return next(new HttpError(400, 'memoryLimit must be a positive integer'));
|
|
|
|
const [error] = await safe(backupTargets.setLimits(req.body));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {}));
|
|
}
|
|
|
|
// storage has three parts. these fields are merged into one top level object
|
|
// 1. format. rsync or tgz
|
|
// 2. config. the 'provider' (see api() function in src/storage.js) differentiates further options
|
|
// s3 providers - accessKeyId, secretAccessKey, bucket, prefix etc . see s3.js
|
|
// gcs - bucket, prefix, projectId, credentials . see gcs.js
|
|
// ext4/xfs/disk (managed providers) - mountOptions (diskPath), prefix, noHardlinks. disk is legacy.
|
|
// nfs/cifs/sshfs (managed providers) - mountOptions (host/username/password/seal/privateKey etc), prefix, noHardlinks
|
|
// filesystem - backupFolder, noHardlinks
|
|
// mountpoint - mountPoint, prefix, noHardlinks
|
|
// 3. encryption. password and encryptedFilenames
|
|
async function setStorage(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
// provider specific options are validated by provider backends
|
|
if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider is required'));
|
|
if (typeof req.body.format !== 'string') return next(new HttpError(400, 'format must be a string'));
|
|
|
|
if ('password' in req.body && typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be a string'));
|
|
if ('encryptedFilenames' in req.body && typeof req.body.encryptedFilenames !== 'boolean') return next(new HttpError(400, 'encryptedFilenames must be a boolean'));
|
|
|
|
// testing the backup using put/del takes a bit of time at times
|
|
req.clearTimeout();
|
|
|
|
const [error] = await safe(backupTargets.setStorage(req.body));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {}));
|
|
}
|
|
|
|
async function getPolicy(req, res, next) {
|
|
const [error, policy] = await safe(backupTargets.getPolicy());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, { policy }));
|
|
}
|
|
|
|
async function setPolicy(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
if (typeof req.body.schedule !== 'string') return next(new HttpError(400, 'schedule is required'));
|
|
if (!req.body.retention || typeof req.body.retention !== 'object') return next(new HttpError(400, 'retention is required'));
|
|
|
|
const [error] = await safe(backupTargets.setPolicy(req.body));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {}));
|
|
}
|