2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
2021-01-21 11:31:35 -08:00
get ,
2021-07-14 11:07:19 -07:00
getByIdentifierAndStatePaged ,
getByTypePaged ,
add ,
update ,
2022-04-04 21:23:59 -07:00
setState ,
2021-07-14 11:07:19 -07:00
list ,
del ,
2015-09-21 14:14:21 -07:00
2021-01-21 11:31:35 -08:00
startBackupTask ,
2016-04-10 19:17:44 -07:00
2021-01-21 11:31:35 -08:00
startCleanupTask ,
cleanupCacheFilesSync ,
2017-09-20 09:57:16 -07:00
2021-01-21 11:31:35 -08:00
injectPrivateFields ,
removePrivateFields ,
2019-02-09 18:08:10 -08:00
2021-01-21 11:31:35 -08:00
generateEncryptionKeysSync ,
2021-07-14 11:07:19 -07:00
getSnapshotInfo ,
setSnapshotInfo ,
testConfig ,
testProviderConfig ,
2020-05-12 14:00:05 -07:00
2021-10-11 17:45:35 +02:00
remount ,
2020-06-14 12:19:51 -07:00
BACKUP _IDENTIFIER _BOX : 'box' ,
2021-09-26 18:37:04 -07:00
BACKUP _IDENTIFIER _MAIL : 'mail' ,
2020-06-14 12:19:51 -07:00
BACKUP _TYPE _APP : 'app' ,
BACKUP _TYPE _BOX : 'box' ,
2021-09-26 18:37:04 -07:00
BACKUP _TYPE _MAIL : 'mail' ,
2020-06-14 12:19:51 -07:00
BACKUP _STATE _NORMAL : 'normal' , // should rename to created to avoid listing in UI?
BACKUP _STATE _CREATING : 'creating' ,
BACKUP _STATE _ERROR : 'error' ,
2015-07-20 00:09:47 -07:00
} ;
2021-07-14 11:07:19 -07:00
const assert = require ( 'assert' ) ,
2019-10-22 20:36:20 -07:00
BoxError = require ( './boxerror.js' ) ,
2019-07-25 14:40:52 -07:00
constants = require ( './constants.js' ) ,
2020-07-28 16:19:44 -07:00
CronJob = require ( 'cron' ) . CronJob ,
2017-09-20 09:57:16 -07:00
crypto = require ( 'crypto' ) ,
2017-11-22 10:58:07 -08:00
database = require ( './database.js' ) ,
2021-09-10 12:10:10 -07:00
debug = require ( 'debug' ) ( 'box:backups' ) ,
2018-12-09 03:20:00 -08:00
eventlog = require ( './eventlog.js' ) ,
2022-04-08 16:23:27 -07:00
hat = require ( './hat.js' ) ,
2018-12-09 03:20:00 -08:00
locker = require ( './locker.js' ) ,
2016-04-10 19:17:44 -07:00
path = require ( 'path' ) ,
paths = require ( './paths.js' ) ,
2017-04-21 14:07:10 -07:00
safe = require ( 'safetydance' ) ,
2015-11-07 22:06:09 -08:00
settings = require ( './settings.js' ) ,
2021-07-14 11:07:19 -07:00
storage = require ( './storage.js' ) ,
2022-04-14 07:59:50 -05:00
tasks = require ( './tasks.js' ) ;
2021-07-14 11:07:19 -07:00
2022-04-04 14:13:27 -07:00
const BACKUPS _FIELDS = [ 'id' , 'remotePath' , 'label' , 'identifier' , 'creationTime' , 'packageVersion' , 'type' , 'dependsOnJson' , 'state' , 'manifestJson' , 'format' , 'preserveSecs' , 'encryptionVersion' ] ;
2016-04-10 19:17:44 -07:00
2021-07-14 11:07:19 -07:00
function postProcess ( result ) {
assert . strictEqual ( typeof result , 'object' ) ;
2016-04-10 19:17:44 -07:00
2022-04-04 14:13:27 -07:00
result . dependsOn = result . dependsOnJson ? safe . JSON . parse ( result . dependsOnJson ) : [ ] ;
delete result . dependsOnJson ;
2021-07-14 11:07:19 -07:00
result . manifest = result . manifestJson ? safe . JSON . parse ( result . manifestJson ) : null ;
delete result . manifestJson ;
2015-11-06 18:14:59 -08:00
2021-07-14 11:07:19 -07:00
return result ;
2021-05-26 23:01:05 -07:00
}
2019-02-09 18:08:10 -08:00
function injectPrivateFields ( newConfig , currentConfig ) {
2020-05-14 11:18:41 -07:00
if ( 'password' in newConfig ) {
2020-05-14 23:01:44 +02:00
if ( newConfig . password === constants . SECRET _PLACEHOLDER ) {
2020-05-14 11:18:41 -07:00
delete newConfig . password ;
}
2020-05-14 23:35:03 +02:00
newConfig . encryption = currentConfig . encryption || null ;
} else {
newConfig . encryption = null ;
2020-05-12 14:00:05 -07:00
}
2021-07-14 11:07:19 -07:00
if ( newConfig . provider === currentConfig . provider ) storage . api ( newConfig . provider ) . injectPrivateFields ( newConfig , currentConfig ) ;
2019-02-09 18:08:10 -08:00
}
function removePrivateFields ( backupConfig ) {
assert . strictEqual ( typeof backupConfig , 'object' ) ;
2020-05-12 14:00:05 -07:00
if ( backupConfig . encryption ) {
delete backupConfig . encryption ;
2020-05-14 23:01:44 +02:00
backupConfig . password = constants . SECRET _PLACEHOLDER ;
2020-05-12 14:00:05 -07:00
}
2021-07-14 11:07:19 -07:00
return storage . api ( backupConfig . provider ) . removePrivateFields ( backupConfig ) ;
2020-05-12 15:49:43 -07:00
}
2021-10-06 13:09:04 -07:00
// this function is used in migrations - 20200512172301-settings-backup-encryption.js
2020-05-12 14:00:05 -07:00
function generateEncryptionKeysSync ( password ) {
assert . strictEqual ( typeof password , 'string' ) ;
const aesKeys = crypto . scryptSync ( password , Buffer . from ( 'CLOUDRONSCRYPTSALT' , 'utf8' ) , 128 ) ;
return {
dataKey : aesKeys . slice ( 0 , 32 ) . toString ( 'hex' ) ,
dataHmacKey : aesKeys . slice ( 32 , 64 ) . toString ( 'hex' ) ,
filenameKey : aesKeys . slice ( 64 , 96 ) . toString ( 'hex' ) ,
filenameHmacKey : aesKeys . slice ( 96 ) . toString ( 'hex' )
} ;
}
2019-12-05 11:55:51 -08:00
2022-04-04 14:13:27 -07:00
async function add ( data ) {
2021-07-14 11:07:19 -07:00
assert ( data && typeof data === 'object' ) ;
2022-04-04 14:13:27 -07:00
assert . strictEqual ( typeof data . remotePath , 'string' ) ;
2021-07-14 11:07:19 -07:00
assert ( data . encryptionVersion === null || typeof data . encryptionVersion === 'number' ) ;
assert . strictEqual ( typeof data . packageVersion , 'string' ) ;
assert . strictEqual ( typeof data . type , 'string' ) ;
assert . strictEqual ( typeof data . identifier , 'string' ) ;
assert . strictEqual ( typeof data . state , 'string' ) ;
assert ( Array . isArray ( data . dependsOn ) ) ;
assert . strictEqual ( typeof data . manifest , 'object' ) ;
assert . strictEqual ( typeof data . format , 'string' ) ;
2022-04-04 21:23:59 -07:00
assert . strictEqual ( typeof data . preserveSecs , 'number' ) ;
2021-07-14 11:07:19 -07:00
const creationTime = data . creationTime || new Date ( ) ; // allow tests to set the time
const manifestJson = JSON . stringify ( data . manifest ) ;
2022-09-29 18:01:16 +02:00
const prefixId = data . type === exports . BACKUP _TYPE _APP ? ` ${ data . type } _ ${ data . identifier } ` : data . type ; // type and identifier are same for other types
const id = ` ${ prefixId } _v ${ data . packageVersion } _ ${ hat ( 256 ) } ` ; // id is used by the UI to derive dependent packages. making this a UUID will require a lot of db querying
2021-07-14 11:07:19 -07:00
2022-04-04 21:23:59 -07:00
const [ error ] = await safe ( database . query ( 'INSERT INTO backups (id, remotePath, identifier, encryptionVersion, packageVersion, type, creationTime, state, dependsOnJson, manifestJson, format, preserveSecs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' ,
[ id , data . remotePath , data . identifier , data . encryptionVersion , data . packageVersion , data . type , creationTime , data . state , JSON . stringify ( data . dependsOn ) , manifestJson , data . format , data . preserveSecs ] ) ) ;
2021-07-14 11:07:19 -07:00
if ( error && error . code === 'ER_DUP_ENTRY' ) throw new BoxError ( BoxError . ALREADY _EXISTS , 'Backup already exists' ) ;
if ( error ) throw error ;
2022-04-04 14:13:27 -07:00
return id ;
2021-07-14 11:07:19 -07:00
}
async function getByIdentifierAndStatePaged ( identifier , state , page , perPage ) {
2020-06-14 12:19:51 -07:00
assert . strictEqual ( typeof identifier , 'string' ) ;
2017-05-30 14:09:55 -07:00
assert . strictEqual ( typeof state , 'string' ) ;
2016-03-08 08:52:20 -08:00
assert ( typeof page === 'number' && page > 0 ) ;
assert ( typeof perPage === 'number' && perPage > 0 ) ;
2015-07-20 00:09:47 -07:00
2021-07-14 11:07:19 -07:00
const results = await database . query ( ` SELECT ${ BACKUPS _FIELDS } FROM backups WHERE identifier = ? AND state = ? ORDER BY creationTime DESC LIMIT ?,? ` , [ identifier , state , ( page - 1 ) * perPage , perPage ] ) ;
2016-06-13 13:42:25 -07:00
2021-07-14 11:07:19 -07:00
results . forEach ( function ( result ) { postProcess ( result ) ; } ) ;
2016-06-13 13:42:25 -07:00
2021-07-14 11:07:19 -07:00
return results ;
2016-06-13 13:42:25 -07:00
}
2021-07-14 11:07:19 -07:00
async function get ( id ) {
assert . strictEqual ( typeof id , 'string' ) ;
2017-09-19 20:40:38 -07:00
2021-07-14 11:07:19 -07:00
const result = await database . query ( 'SELECT ' + BACKUPS _FIELDS + ' FROM backups WHERE id = ? ORDER BY creationTime DESC' , [ id ] ) ;
if ( result . length === 0 ) return null ;
2020-06-05 13:27:18 +02:00
2021-07-14 11:07:19 -07:00
return postProcess ( result [ 0 ] ) ;
2017-09-19 20:40:38 -07:00
}
2021-07-14 11:07:19 -07:00
async function getByTypePaged ( type , page , perPage ) {
assert . strictEqual ( typeof type , 'string' ) ;
assert ( typeof page === 'number' && page > 0 ) ;
assert ( typeof perPage === 'number' && perPage > 0 ) ;
2018-07-27 06:55:54 -07:00
2021-07-14 11:07:19 -07:00
const results = await database . query ( ` SELECT ${ BACKUPS _FIELDS } FROM backups WHERE type = ? ORDER BY creationTime DESC LIMIT ?,? ` , [ type , ( page - 1 ) * perPage , perPage ] ) ;
2018-07-27 06:55:54 -07:00
2021-07-14 11:07:19 -07:00
results . forEach ( function ( result ) { postProcess ( result ) ; } ) ;
2018-07-27 11:46:42 -07:00
2021-07-14 11:07:19 -07:00
return results ;
2018-07-27 11:46:42 -07:00
}
2022-04-02 17:09:08 -07:00
function validateLabel ( label ) {
assert . strictEqual ( typeof label , 'string' ) ;
if ( label . length >= 200 ) return new BoxError ( BoxError . BAD _FIELD , 'label too long' ) ;
2022-06-24 09:18:51 -07:00
if ( /[^a-zA-Z0-9._() -]/ . test ( label ) ) return new BoxError ( BoxError . BAD _FIELD , 'label can only contain alphanumerals, space, dot, hyphen, brackets or underscore' ) ;
2022-04-02 17:09:08 -07:00
return null ;
}
2022-04-04 21:23:59 -07:00
// this is called by REST API
2022-04-02 17:09:08 -07:00
async function update ( id , data ) {
2021-07-14 11:07:19 -07:00
assert . strictEqual ( typeof id , 'string' ) ;
2022-04-02 17:09:08 -07:00
assert . strictEqual ( typeof data , 'object' ) ;
2020-05-15 16:05:12 -07:00
2022-04-02 17:09:08 -07:00
let error ;
if ( 'label' in data ) {
error = validateLabel ( data . label ) ;
if ( error ) throw error ;
}
const fields = [ ] , values = [ ] ;
for ( const p in data ) {
2022-04-04 21:23:59 -07:00
if ( p === 'label' || p === 'preserveSecs' ) {
2022-04-02 17:09:08 -07:00
fields . push ( p + ' = ?' ) ;
values . push ( data [ p ] ) ;
}
2020-05-10 21:40:25 -07:00
}
2021-07-14 11:07:19 -07:00
values . push ( id ) ;
2020-05-10 21:40:25 -07:00
2022-04-04 21:23:59 -07:00
const backup = await get ( id ) ;
if ( backup === null ) throw new BoxError ( BoxError . NOT _FOUND , 'Backup not found' ) ;
2021-07-14 11:07:19 -07:00
const result = await database . query ( 'UPDATE backups SET ' + fields . join ( ', ' ) + ' WHERE id = ?' , values ) ;
if ( result . affectedRows !== 1 ) throw new BoxError ( BoxError . NOT _FOUND , 'Backup not found' ) ;
2022-04-04 21:23:59 -07:00
if ( 'preserveSecs' in data ) {
// update the dependancies
for ( const depId of backup . dependsOn ) {
await database . query ( 'UPDATE backups SET preserveSecs=? WHERE id = ?' , [ data . preserveSecs , depId ] ) ;
}
}
}
async function setState ( id , state ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof state , 'string' ) ;
const result = await database . query ( 'UPDATE backups SET state = ? WHERE id = ?' , [ state , id ] ) ;
if ( result . affectedRows !== 1 ) throw new BoxError ( BoxError . NOT _FOUND , 'Backup not found' ) ;
2020-05-10 21:40:25 -07:00
}
2021-09-10 12:10:10 -07:00
async function startBackupTask ( auditSource ) {
2021-07-14 11:07:19 -07:00
let error = locker . lock ( locker . OP _FULL _BACKUP ) ;
2021-09-10 12:10:10 -07:00
if ( error ) throw new BoxError ( BoxError . BAD _STATE , ` Cannot backup now: ${ error . message } ` ) ;
2020-05-10 21:40:25 -07:00
2021-09-10 12:10:10 -07:00
const backupConfig = await settings . getBackupConfig ( ) ;
2018-07-27 06:55:54 -07:00
2021-10-26 09:17:02 -07:00
const memoryLimit = 'memoryLimit' in backupConfig ? Math . max ( backupConfig . memoryLimit / 1024 / 1024 , 800 ) : 800 ;
2018-07-27 06:55:54 -07:00
2021-09-10 12:10:10 -07:00
const taskId = await tasks . add ( tasks . TASK _BACKUP , [ { /* options */ } ] ) ;
2018-07-27 06:55:54 -07:00
2021-09-10 12:10:10 -07:00
await eventlog . add ( eventlog . ACTION _BACKUP _START , auditSource , { taskId } ) ;
2020-09-30 20:03:46 -07:00
2021-09-10 12:10:10 -07:00
tasks . startTask ( taskId , { timeout : 24 * 60 * 60 * 1000 /* 24 hours */ , nice : 15 , memoryLimit } , async function ( error , backupId ) {
locker . unlock ( locker . OP _FULL _BACKUP ) ;
2020-05-10 21:40:25 -07:00
2021-09-10 12:10:10 -07:00
const errorMessage = error ? error . message : '' ;
const timedOut = error ? error . code === tasks . ETIMEOUT : false ;
2020-05-10 21:40:25 -07:00
2022-08-01 14:13:09 +02:00
const backup = backupId ? await get ( backupId ) : null ;
2022-04-05 09:28:30 -07:00
await safe ( eventlog . add ( eventlog . ACTION _BACKUP _FINISH , auditSource , { taskId , errorMessage , timedOut , backupId , remotePath : backup ? . remotePath } ) , { debug } ) ;
2019-12-05 14:51:15 -08:00
} ) ;
2021-09-10 12:10:10 -07:00
return taskId ;
2021-07-14 11:07:19 -07:00
}
2018-07-27 11:46:42 -07:00
2021-07-14 11:07:19 -07:00
async function list ( page , perPage ) {
assert ( typeof page === 'number' && page > 0 ) ;
assert ( typeof perPage === 'number' && perPage > 0 ) ;
2020-05-15 15:24:12 -07:00
2021-07-14 11:07:19 -07:00
const results = await database . query ( 'SELECT ' + BACKUPS _FIELDS + ' FROM backups ORDER BY creationTime DESC LIMIT ?,?' , [ ( page - 1 ) * perPage , perPage ] ) ;
2020-05-10 21:40:25 -07:00
2021-07-14 11:07:19 -07:00
results . forEach ( function ( result ) { postProcess ( result ) ; } ) ;
2019-12-05 14:51:15 -08:00
2021-07-14 11:07:19 -07:00
return results ;
2018-07-27 11:46:42 -07:00
}
2021-07-14 11:07:19 -07:00
async function del ( id ) {
assert . strictEqual ( typeof id , 'string' ) ;
2017-09-20 09:57:16 -07:00
2021-07-14 11:07:19 -07:00
const result = await database . query ( 'DELETE FROM backups WHERE id=?' , [ id ] ) ;
if ( result . affectedRows !== 1 ) throw new BoxError ( BoxError . NOT _FOUND , 'Backup not found' ) ;
}
2017-09-20 09:57:16 -07:00
2021-10-06 13:09:04 -07:00
// this function is used in migrations - 20200512172301-settings-backup-encryption.js
2021-07-14 11:07:19 -07:00
function cleanupCacheFilesSync ( ) {
2021-09-10 12:10:10 -07:00
const files = safe . fs . readdirSync ( path . join ( paths . BACKUP _INFO _DIR ) ) ;
2021-07-14 11:07:19 -07:00
if ( ! files ) return ;
2017-09-20 09:57:16 -07:00
2021-09-10 12:10:10 -07:00
files
. filter ( function ( f ) { return f . endsWith ( '.sync.cache' ) ; } )
. forEach ( function ( f ) {
safe . fs . unlinkSync ( path . join ( paths . BACKUP _INFO _DIR , f ) ) ;
} ) ;
2020-05-10 21:40:25 -07:00
}
2021-07-14 11:07:19 -07:00
function getSnapshotInfo ( id ) {
assert . strictEqual ( typeof id , 'string' ) ;
2018-02-22 10:58:56 -08:00
2021-07-14 11:07:19 -07:00
const contents = safe . fs . readFileSync ( paths . SNAPSHOT _INFO _FILE , 'utf8' ) ;
const info = safe . JSON . parse ( contents ) ;
if ( ! info ) return { } ;
return info [ id ] || { } ;
2017-09-22 14:40:37 -07:00
}
2021-09-26 18:37:04 -07:00
// keeps track of contents of the snapshot directory. this provides a way to clean up backups of uninstalled apps
2021-09-16 13:59:03 -07:00
async function setSnapshotInfo ( id , info ) {
2021-07-14 11:07:19 -07:00
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof info , 'object' ) ;
2017-09-22 14:40:37 -07:00
2021-07-14 11:07:19 -07:00
const contents = safe . fs . readFileSync ( paths . SNAPSHOT _INFO _FILE , 'utf8' ) ;
const data = safe . JSON . parse ( contents ) || { } ;
if ( info ) data [ id ] = info ; else delete data [ id ] ;
if ( ! safe . fs . writeFileSync ( paths . SNAPSHOT _INFO _FILE , JSON . stringify ( data , null , 4 ) , 'utf8' ) ) {
2021-09-16 13:59:03 -07:00
throw new BoxError ( BoxError . FS _ERROR , safe . error . message ) ;
2019-01-17 09:53:51 -08:00
}
2017-09-22 14:40:37 -07:00
}
2021-07-14 11:07:19 -07:00
async function startCleanupTask ( auditSource ) {
assert . strictEqual ( typeof auditSource , 'object' ) ;
2017-09-19 20:27:36 -07:00
2021-07-14 11:07:19 -07:00
const taskId = await tasks . add ( tasks . TASK _CLEAN _BACKUPS , [ ] ) ;
2017-09-19 20:27:36 -07:00
2022-04-04 14:13:27 -07:00
tasks . startTask ( taskId , { } , async ( error , result ) => { // result is { removedBoxBackupPaths, removedAppBackupPaths, removedMailBackupPaths, missingBackupPaths }
2022-02-24 20:04:46 -08:00
await safe ( eventlog . add ( eventlog . ACTION _BACKUP _CLEANUP _FINISH , auditSource , {
2021-07-14 11:07:19 -07:00
taskId ,
errorMessage : error ? error . message : null ,
2022-04-04 14:13:27 -07:00
removedBoxBackupPaths : result ? result . removedBoxBackupPaths : [ ] ,
removedMailBackupPaths : result ? result . removedMailBackupPaths : [ ] ,
removedAppBackupPaths : result ? result . removedAppBackupPaths : [ ] ,
missingBackupPaths : result ? result . missingBackupPaths : [ ]
2022-02-24 20:04:46 -08:00
} ) , { debug } ) ;
2017-09-20 09:57:16 -07:00
} ) ;
2018-12-20 11:41:38 -08:00
2021-07-14 11:07:19 -07:00
return taskId ;
2017-09-23 14:27:35 -07:00
}
2021-09-17 10:11:28 -07:00
async function testConfig ( backupConfig ) {
2017-11-22 10:29:40 -08:00
assert . strictEqual ( typeof backupConfig , 'object' ) ;
2017-09-20 09:57:16 -07:00
2021-07-14 11:07:19 -07:00
const func = storage . api ( backupConfig . provider ) ;
2022-02-07 13:19:59 -08:00
if ( ! func ) return new BoxError ( BoxError . BAD _FIELD , 'unknown storage provider' ) ;
2017-09-20 09:57:16 -07:00
2022-02-07 13:19:59 -08:00
if ( backupConfig . format !== 'tgz' && backupConfig . format !== 'rsync' ) return new BoxError ( BoxError . BAD _FIELD , 'unknown format' ) ;
2019-01-14 11:36:11 -08:00
2021-07-14 11:07:19 -07:00
const job = safe . safeCall ( function ( ) { return new CronJob ( backupConfig . schedulePattern ) ; } ) ;
2022-02-07 13:19:59 -08:00
if ( ! job ) return new BoxError ( BoxError . BAD _FIELD , 'Invalid schedule pattern' ) ;
2017-09-20 09:57:16 -07:00
2021-07-14 11:07:19 -07:00
if ( 'password' in backupConfig ) {
2022-02-07 13:19:59 -08:00
if ( typeof backupConfig . password !== 'string' ) return new BoxError ( BoxError . BAD _FIELD , 'password must be a string' ) ;
if ( backupConfig . password . length < 8 ) return new BoxError ( BoxError . BAD _FIELD , 'password must be atleast 8 characters' ) ;
2021-07-14 11:07:19 -07:00
}
2018-12-20 11:41:38 -08:00
2021-07-14 11:07:19 -07:00
const policy = backupConfig . retentionPolicy ;
2022-02-07 13:19:59 -08:00
if ( ! policy ) return new BoxError ( BoxError . BAD _FIELD , 'retentionPolicy is required' ) ;
if ( ! [ 'keepWithinSecs' , 'keepDaily' , 'keepWeekly' , 'keepMonthly' , 'keepYearly' ] . find ( k => ! ! policy [ k ] ) ) return new BoxError ( BoxError . BAD _FIELD , 'properties missing' ) ;
if ( 'keepWithinSecs' in policy && typeof policy . keepWithinSecs !== 'number' ) return new BoxError ( BoxError . BAD _FIELD , 'keepWithinSecs must be a number' ) ;
if ( 'keepDaily' in policy && typeof policy . keepDaily !== 'number' ) return new BoxError ( BoxError . BAD _FIELD , 'keepDaily must be a number' ) ;
if ( 'keepWeekly' in policy && typeof policy . keepWeekly !== 'number' ) return new BoxError ( BoxError . BAD _FIELD , 'keepWeekly must be a number' ) ;
if ( 'keepMonthly' in policy && typeof policy . keepMonthly !== 'number' ) return new BoxError ( BoxError . BAD _FIELD , 'keepMonthly must be a number' ) ;
if ( 'keepYearly' in policy && typeof policy . keepYearly !== 'number' ) return new BoxError ( BoxError . BAD _FIELD , 'keepYearly must be a number' ) ;
2021-09-17 10:11:28 -07:00
2022-04-14 07:59:50 -05:00
await storage . api ( backupConfig . provider ) . testConfig ( backupConfig ) ;
2017-09-19 20:27:36 -07:00
}
2017-10-10 20:23:04 -07:00
2021-07-14 11:07:19 -07:00
// this skips password check since that policy is only at creation time
2021-09-17 10:11:28 -07:00
async function testProviderConfig ( backupConfig ) {
2017-11-22 10:58:07 -08:00
assert . strictEqual ( typeof backupConfig , 'object' ) ;
2021-07-14 11:07:19 -07:00
const func = storage . api ( backupConfig . provider ) ;
2022-02-07 13:19:59 -08:00
if ( ! func ) return new BoxError ( BoxError . BAD _FIELD , 'unknown storage provider' ) ;
2018-09-26 12:39:33 -07:00
2022-04-14 07:59:50 -05:00
await storage . api ( backupConfig . provider ) . testConfig ( backupConfig ) ;
2020-02-26 09:08:30 -08:00
}
2021-10-11 17:45:35 +02:00
async function remount ( auditSource ) {
assert . strictEqual ( typeof auditSource , 'object' ) ;
const backupConfig = await settings . getBackupConfig ( ) ;
const func = storage . api ( backupConfig . provider ) ;
2022-02-07 13:19:59 -08:00
if ( ! func ) throw new BoxError ( BoxError . BAD _FIELD , 'unknown storage provider' ) ;
2021-10-11 17:45:35 +02:00
2022-04-14 07:43:43 -05:00
await storage . api ( backupConfig . provider ) . remount ( backupConfig ) ;
2021-10-11 17:45:35 +02:00
}