2018-12-17 16:37:19 +01:00
'use strict' ;
exports = module . exports = {
NotificationsError : NotificationsError ,
get : get ,
ack : ack ,
2019-01-04 17:13:52 +01:00
getAllPaged : getAllPaged ,
2018-12-17 17:35:19 +01:00
2019-02-27 16:10:54 -08:00
onEvent : onEvent ,
2019-02-28 14:52:14 -08:00
// NOTE: if you add an alert, be sure to add title below
ALERT _BACKUP _CONFIG : 'backupConfig' ,
ALERT _DISK _SPACE : 'diskSpace' ,
ALERT _MAIL _STATUS : 'mailStatus' ,
ALERT _REBOOT : 'reboot' ,
2019-03-07 13:34:46 -08:00
ALERT _BOX _UPDATE : 'boxUpdate' ,
2019-02-28 14:52:14 -08:00
2019-03-02 19:23:39 -08:00
alert : alert ,
// exported for testing
_add : add
2018-12-17 16:37:19 +01:00
} ;
2019-02-28 14:52:14 -08:00
let assert = require ( 'assert' ) ,
2018-12-17 17:35:19 +01:00
async = require ( 'async' ) ,
2019-05-07 11:30:12 -07:00
custom = require ( './custom.js' ) ,
2018-12-17 16:37:19 +01:00
DatabaseError = require ( './databaseerror.js' ) ,
debug = require ( 'debug' ) ( 'box:notifications' ) ,
2019-02-27 16:10:54 -08:00
eventlog = require ( './eventlog.js' ) ,
2018-12-17 17:35:19 +01:00
mailer = require ( './mailer.js' ) ,
2018-12-17 16:37:19 +01:00
notificationdb = require ( './notificationdb.js' ) ,
2019-07-26 10:49:29 -07:00
settings = require ( './settings.js' ) ,
2018-12-17 17:35:19 +01:00
users = require ( './users.js' ) ,
2018-12-17 16:37:19 +01:00
util = require ( 'util' ) ;
function NotificationsError ( reason , errorOrMessage ) {
assert . strictEqual ( typeof reason , 'string' ) ;
assert ( errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined' ) ;
Error . call ( this ) ;
Error . captureStackTrace ( this , this . constructor ) ;
this . name = this . constructor . name ;
this . reason = reason ;
if ( typeof errorOrMessage === 'undefined' ) {
this . message = reason ;
} else if ( typeof errorOrMessage === 'string' ) {
this . message = errorOrMessage ;
} else {
this . message = 'Internal error' ;
this . nestedError = errorOrMessage ;
}
}
util . inherits ( NotificationsError , Error ) ;
NotificationsError . INTERNAL _ERROR = 'Internal Error' ;
NotificationsError . NOT _FOUND = 'Not Found' ;
2019-02-28 14:59:33 -08:00
function add ( userId , eventId , title , message , callback ) {
2018-12-17 16:37:19 +01:00
assert . strictEqual ( typeof userId , 'string' ) ;
2019-02-06 16:18:12 +01:00
assert ( typeof eventId === 'string' || eventId === null ) ;
2018-12-17 16:37:19 +01:00
assert . strictEqual ( typeof title , 'string' ) ;
assert . strictEqual ( typeof message , 'string' ) ;
2018-12-17 17:40:53 +01:00
assert . strictEqual ( typeof callback , 'function' ) ;
2018-12-17 16:37:19 +01:00
2019-02-28 14:59:33 -08:00
debug ( 'add: ' , userId , title ) ;
2018-12-17 16:37:19 +01:00
notificationdb . add ( {
userId : userId ,
2019-01-19 13:22:29 +01:00
eventId : eventId ,
2018-12-17 16:37:19 +01:00
title : title ,
2019-03-04 20:44:41 -08:00
message : message ,
acknowledged : false
2018-12-17 16:37:19 +01:00
} , function ( error , result ) {
2019-01-21 08:51:04 +01:00
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( new NotificationsError ( NotificationsError . NOT _FOUND , error . message ) ) ;
2018-12-17 16:37:19 +01:00
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
callback ( null , { id : result } ) ;
} ) ;
}
function get ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
notificationdb . get ( id , function ( error , result ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( new NotificationsError ( NotificationsError . NOT _FOUND ) ) ;
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
callback ( null , result ) ;
} ) ;
}
function ack ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
notificationdb . update ( id , { acknowledged : true } , function ( error ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( new NotificationsError ( NotificationsError . NOT _FOUND ) ) ;
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
}
// if acknowledged === null we return all, otherwise yes or no based on acknowledged as a boolean
2019-01-04 17:13:52 +01:00
function getAllPaged ( userId , acknowledged , page , perPage , callback ) {
2018-12-17 16:37:19 +01:00
assert . strictEqual ( typeof userId , 'string' ) ;
assert ( acknowledged === null || typeof acknowledged === 'boolean' ) ;
assert . strictEqual ( typeof page , 'number' ) ;
assert . strictEqual ( typeof perPage , 'number' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
notificationdb . listByUserIdPaged ( userId , page , perPage , function ( error , result ) {
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
if ( acknowledged === null ) return callback ( null , result ) ;
callback ( null , result . filter ( function ( r ) { return r . acknowledged === acknowledged ; } ) ) ;
} ) ;
}
2018-12-17 17:35:19 +01:00
2019-01-07 14:56:43 +01:00
// Calls iterator with (admin, callback)
2019-01-17 13:36:54 +01:00
function actionForAllAdmins ( skippingUserIds , iterator , callback ) {
assert ( Array . isArray ( skippingUserIds ) ) ;
2019-01-07 14:56:43 +01:00
assert . strictEqual ( typeof iterator , 'function' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
users . getAllAdmins ( function ( error , result ) {
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
2019-01-17 13:36:54 +01:00
// filter out users we want to skip (like the user who did the action or the user the action was performed on)
result = result . filter ( function ( r ) { return skippingUserIds . indexOf ( r . id ) === - 1 ; } ) ;
2019-01-07 14:56:43 +01:00
async . each ( result , iterator , callback ) ;
} ) ;
}
2019-03-01 15:14:35 -08:00
function userAdded ( performedBy , eventId , user , callback ) {
2019-01-17 13:36:54 +01:00
assert . strictEqual ( typeof performedBy , 'string' ) ;
2019-01-19 13:22:29 +01:00
assert . strictEqual ( typeof eventId , 'string' ) ;
2018-12-17 17:35:19 +01:00
assert . strictEqual ( typeof user , 'object' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2018-12-17 17:35:19 +01:00
2019-03-01 15:14:35 -08:00
actionForAllAdmins ( [ performedBy , user . id ] , function ( admin , done ) {
2019-01-07 14:56:43 +01:00
mailer . userAdded ( admin . email , user ) ;
2019-08-03 10:17:07 -07:00
add ( admin . id , eventId , ` User ' ${ user . displayName } ' added ` , ` User ${ user . username || user . email || user . fallbackEmail } was added ` , done ) ;
2019-03-01 15:14:35 -08:00
} , callback ) ;
2018-12-17 17:35:19 +01:00
}
2019-01-07 12:57:57 +01:00
2019-03-01 15:14:35 -08:00
function userRemoved ( performedBy , eventId , user , callback ) {
2019-01-17 13:36:54 +01:00
assert . strictEqual ( typeof performedBy , 'string' ) ;
2019-01-19 13:22:29 +01:00
assert . strictEqual ( typeof eventId , 'string' ) ;
2019-01-10 12:00:04 +01:00
assert . strictEqual ( typeof user , 'object' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-01-10 12:00:04 +01:00
2019-03-01 15:14:35 -08:00
actionForAllAdmins ( [ performedBy , user . id ] , function ( admin , done ) {
2019-01-10 12:00:04 +01:00
mailer . userRemoved ( admin . email , user ) ;
2019-08-03 10:17:07 -07:00
add ( admin . id , eventId , ` User ' ${ user . displayName } ' removed ` , ` User ${ user . username || user . email || user . fallbackEmail } was removed ` , done ) ;
2019-03-01 15:14:35 -08:00
} , callback ) ;
2019-01-10 12:00:04 +01:00
}
2019-03-01 15:14:35 -08:00
function adminChanged ( performedBy , eventId , user , callback ) {
2019-01-17 13:36:54 +01:00
assert . strictEqual ( typeof performedBy , 'string' ) ;
2019-01-10 12:00:04 +01:00
assert . strictEqual ( typeof user , 'object' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-01-10 12:00:04 +01:00
2019-03-01 15:14:35 -08:00
actionForAllAdmins ( [ performedBy , user . id ] , function ( admin , done ) {
2019-01-17 13:12:26 +01:00
mailer . adminChanged ( admin . email , user , user . admin ) ;
2019-08-03 10:17:07 -07:00
add ( admin . id , eventId , ` User ' ${ user . displayName } ' ${ user . admin ? 'is now an admin' : 'is no more an admin' } ` , ` User ${ user . username || user . email || user . fallbackEmail } ${ user . admin ? 'is now an admin' : 'is no more an admin' } ` , done ) ;
2019-03-01 15:14:35 -08:00
} , callback ) ;
2019-01-10 12:00:04 +01:00
}
2019-03-06 11:54:37 -08:00
function oomEvent ( eventId , app , addon , containerId , event , callback ) {
2019-01-19 13:22:29 +01:00
assert . strictEqual ( typeof eventId , 'string' ) ;
2019-03-06 11:54:37 -08:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof addon , 'object' ) ;
assert . strictEqual ( typeof containerId , 'string' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-01-07 12:57:57 +01:00
2019-03-06 11:54:37 -08:00
let title , message , program ;
if ( app ) {
2019-03-06 16:08:12 -08:00
program = ` App ${ app . fqdn } ` ;
2019-03-06 11:54:37 -08:00
title = ` The application ${ app . fqdn } ( ${ app . manifest . title } ) ran out of memory. ` ;
message = 'The application has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://cloudron.io/documentation/apps/#increasing-the-memory-limit-of-an-app)' ;
} else if ( addon ) {
2019-03-06 16:08:12 -08:00
program = ` ${ addon . name } service ` ;
2019-03-06 11:54:37 -08:00
title = ` The ${ addon . name } service ran out of memory ` ;
message = 'The service has been restarted automatically. If you see this notification often, consider increasing the [memory limit](https://cloudron.io/documentation/troubleshooting/#services)' ;
2019-03-06 15:59:35 -08:00
} else { // this never happens currently
2019-03-06 16:08:12 -08:00
program = ` Container ${ containerId } ` ;
2019-03-06 11:54:37 -08:00
title = ` The container ${ containerId } ran out of memory ` ;
message = 'The container has been restarted automatically. Consider increasing the [memory limit](https://docs.docker.com/v17.09/edge/engine/reference/commandline/update/#update-a-containers-kernel-memory-constraints)' ;
}
2019-05-10 15:53:34 -07:00
if ( custom . spec ( ) . alerts . email ) mailer . oomEvent ( custom . spec ( ) . alerts . email , program , event ) ;
if ( ! custom . spec ( ) . alerts . notifyCloudronAdmins ) return callback ( ) ;
2019-01-10 12:00:04 +01:00
2019-03-01 15:14:35 -08:00
actionForAllAdmins ( [ ] , function ( admin , done ) {
2019-03-06 11:54:37 -08:00
mailer . oomEvent ( admin . email , program , event ) ;
2019-01-08 14:20:08 +01:00
2019-03-06 11:54:37 -08:00
add ( admin . id , eventId , title , message , done ) ;
2019-03-01 15:14:35 -08:00
} , callback ) ;
2019-01-07 12:57:57 +01:00
}
2019-01-07 13:01:27 +01:00
2019-03-01 15:14:35 -08:00
function appUp ( eventId , app , callback ) {
2019-02-11 12:32:02 -08:00
assert . strictEqual ( typeof eventId , 'string' ) ;
assert . strictEqual ( typeof app , 'object' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-02-11 12:32:02 -08:00
2019-05-10 15:53:34 -07:00
if ( custom . spec ( ) . alerts . email ) mailer . appUp ( custom . spec ( ) . alerts . email , app ) ;
if ( ! custom . spec ( ) . alerts . notifyCloudronAdmins ) return callback ( ) ;
2019-02-11 12:32:02 -08:00
2019-03-01 15:14:35 -08:00
actionForAllAdmins ( [ ] , function ( admin , done ) {
2019-02-11 12:32:02 -08:00
mailer . appUp ( admin . email , app ) ;
2019-03-01 15:14:35 -08:00
add ( admin . id , eventId , ` App ${ app . fqdn } is back online ` , ` The application ${ app . manifest . title } installed at ${ app . fqdn } is back online. ` , done ) ;
} , callback ) ;
2019-02-11 12:32:02 -08:00
}
2019-03-01 15:14:35 -08:00
function appDied ( eventId , app , callback ) {
2019-01-19 13:22:29 +01:00
assert . strictEqual ( typeof eventId , 'string' ) ;
2019-01-07 13:01:27 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
2019-03-01 15:14:35 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-01-07 13:01:27 +01:00
2019-05-10 15:53:34 -07:00
if ( custom . spec ( ) . alerts . email ) mailer . appDied ( custom . spec ( ) . alerts . email , app ) ;
if ( ! custom . spec ( ) . alerts . notifyCloudronAdmins ) return callback ( ) ;
2019-01-10 12:00:04 +01:00
2019-01-17 13:36:54 +01:00
actionForAllAdmins ( [ ] , function ( admin , callback ) {
2019-01-10 12:00:04 +01:00
mailer . appDied ( admin . email , app ) ;
2019-02-28 14:59:33 -08:00
add ( admin . id , eventId , ` App ${ app . fqdn } is down ` , ` The application ${ app . manifest . title } installed at ${ app . fqdn } is not responding. ` , callback ) ;
2019-03-01 15:14:35 -08:00
} , callback ) ;
2019-01-07 13:01:27 +01:00
}
2019-01-10 13:57:47 +01:00
2019-05-07 12:04:43 +02:00
function appUpdated ( eventId , app , callback ) {
assert . strictEqual ( typeof eventId , 'string' ) ;
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2019-08-02 20:56:25 -07:00
const tmp = app . manifest . description . match ( /<upstream>(.*)<\/upstream>/i ) ;
const upstreamVersion = ( tmp && tmp [ 1 ] ) ? tmp [ 1 ] : '' ;
const title = upstreamVersion ? ` ${ app . manifest . title } at ${ app . fqdn } updated to ${ upstreamVersion } (package version ${ app . manifest . version } ) `
: ` ${ app . manifest . title } at ${ app . fqdn } updated to package version ${ app . manifest . version } ` ;
2019-05-07 12:04:43 +02:00
actionForAllAdmins ( [ ] , function ( admin , done ) {
2019-08-03 09:41:16 -07:00
add ( admin . id , eventId , title , ` The application ${ app . manifest . title } installed at https:// ${ app . fqdn } was updated. \n \n Changelog: \n ${ app . manifest . changelog } \n ` , function ( error ) {
2019-05-07 14:22:51 +02:00
if ( error ) return callback ( error ) ;
mailer . appUpdated ( admin . email , app , function ( error ) {
if ( error ) console . error ( 'Failed to send app updated email' , error ) ; // non fatal
done ( ) ;
} ) ;
} ) ;
2019-05-07 12:04:43 +02:00
} , callback ) ;
}
2019-03-04 14:50:56 -08:00
function certificateRenewalError ( eventId , vhost , errorMessage , callback ) {
assert . strictEqual ( typeof eventId , 'string' ) ;
assert . strictEqual ( typeof vhost , 'string' ) ;
assert . strictEqual ( typeof errorMessage , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2019-05-10 15:53:34 -07:00
if ( custom . spec ( ) . alerts . email ) mailer . certificateRenewalError ( custom . spec ( ) . alerts . email , vhost , errorMessage ) ;
if ( ! custom . spec ( ) . alerts . notifyCloudronAdmins ) return callback ( ) ;
2019-03-10 15:53:20 -07:00
2019-03-04 14:50:56 -08:00
actionForAllAdmins ( [ ] , function ( admin , callback ) {
2019-03-10 15:53:20 -07:00
mailer . certificateRenewalError ( admin . email , vhost , errorMessage ) ;
2019-03-04 14:50:56 -08:00
add ( admin . id , eventId , ` Certificate renewal of ${ vhost } failed ` , ` Failed to new certs of ${ vhost } : ${ errorMessage } . Renewal will be retried in 12 hours ` , callback ) ;
} , callback ) ;
}
2019-03-04 17:52:31 -08:00
function backupFailed ( eventId , taskId , errorMessage , callback ) {
2019-03-04 15:00:23 -08:00
assert . strictEqual ( typeof eventId , 'string' ) ;
2019-03-04 17:52:31 -08:00
assert . strictEqual ( typeof taskId , 'string' ) ;
2019-03-04 15:00:23 -08:00
assert . strictEqual ( typeof errorMessage , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2019-07-26 10:49:29 -07:00
if ( custom . spec ( ) . alerts . email ) mailer . backupFailed ( custom . spec ( ) . alerts . email , errorMessage , ` ${ settings . adminOrigin ( ) } /logs.html?taskId= ${ taskId } ` ) ;
2019-05-10 15:53:34 -07:00
if ( ! custom . spec ( ) . alerts . notifyCloudronAdmins ) return callback ( ) ;
2019-03-10 15:53:20 -07:00
2019-03-04 15:00:23 -08:00
actionForAllAdmins ( [ ] , function ( admin , callback ) {
2019-07-26 10:49:29 -07:00
mailer . backupFailed ( admin . email , errorMessage , ` ${ settings . adminOrigin ( ) } /logs.html?taskId= ${ taskId } ` ) ;
2019-03-04 17:52:31 -08:00
add ( admin . id , eventId , 'Failed to backup' , ` Backup failed: ${ errorMessage } . Logs are available [here](/logs.html?taskId= ${ taskId } ). Will be retried in 4 hours ` , callback ) ;
2019-03-04 15:00:23 -08:00
} , callback ) ;
}
2019-04-30 16:26:16 +02:00
function alert ( id , title , message , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
2019-02-28 14:52:14 -08:00
assert . strictEqual ( typeof title , 'string' ) ;
2019-02-06 15:47:31 +01:00
assert . strictEqual ( typeof message , 'string' ) ;
2019-02-28 14:52:14 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-02-06 15:47:31 +01:00
2019-04-30 16:26:16 +02:00
debug ( ` alert: id= ${ id } title= ${ title } message= ${ message } ` ) ;
2019-03-04 20:58:27 -08:00
const acknowledged = ! message ;
2019-02-06 15:47:31 +01:00
2019-04-30 16:26:16 +02:00
actionForAllAdmins ( [ ] , function ( admin , callback ) {
const data = {
userId : admin . id ,
eventId : null ,
title : title ,
message : message ,
acknowledged : acknowledged ,
creationTime : new Date ( )
} ;
2019-02-06 14:58:45 -08:00
2019-04-30 16:26:16 +02:00
notificationdb . getByUserIdAndTitle ( admin . id , title , function ( error , result ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
2019-03-04 20:58:27 -08:00
2019-04-30 16:26:16 +02:00
if ( ! result && acknowledged ) return callback ( ) ; // do not add acked alerts
2019-03-04 20:58:27 -08:00
2019-04-30 16:26:16 +02:00
let updateFunc = ! result ? notificationdb . add . bind ( null , data ) : notificationdb . update . bind ( null , result . id , data ) ;
2019-03-04 20:58:27 -08:00
2019-04-30 16:26:16 +02:00
updateFunc ( function ( error ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( new NotificationsError ( NotificationsError . NOT _FOUND , error . message ) ) ;
if ( error ) return callback ( new NotificationsError ( NotificationsError . INTERNAL _ERROR , error ) ) ;
2019-03-04 20:58:27 -08:00
2019-04-30 16:26:16 +02:00
callback ( null ) ;
} ) ;
2019-03-04 20:58:27 -08:00
} ) ;
2019-02-06 15:47:31 +01:00
} , function ( error ) {
if ( error ) console . error ( error ) ;
2019-02-28 14:52:14 -08:00
2019-03-04 20:21:41 -08:00
callback ( ) ;
} ) ;
2019-02-06 15:47:31 +01:00
}
2019-02-27 16:10:54 -08:00
function onEvent ( id , action , source , data , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof action , 'string' ) ;
assert . strictEqual ( typeof source , 'object' ) ;
assert . strictEqual ( typeof data , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2019-03-01 15:14:35 -08:00
switch ( action ) {
2019-03-25 14:02:23 -07:00
case eventlog . ACTION _USER _ADD :
return userAdded ( source . userId , id , data . user , callback ) ;
case eventlog . ACTION _USER _REMOVE :
return userRemoved ( source . userId , id , data . user , callback ) ;
case eventlog . ACTION _USER _UPDATE :
if ( ! data . adminStatusChanged ) return callback ( ) ;
return adminChanged ( source . userId , id , data . user , callback ) ;
case eventlog . ACTION _APP _OOM :
return oomEvent ( id , data . app , data . addon , data . containerId , data . event , callback ) ;
case eventlog . ACTION _APP _DOWN :
return appDied ( id , data . app , callback ) ;
case eventlog . ACTION _APP _UP :
return appUp ( id , data . app , callback ) ;
2019-05-07 12:04:43 +02:00
case eventlog . ACTION _APP _UPDATE _FINISH :
return appUpdated ( id , data . app , callback ) ;
2019-03-04 14:50:56 -08:00
case eventlog . ACTION _CERTIFICATE _RENEWAL :
case eventlog . ACTION _CERTIFICATE _NEW :
2019-03-25 14:02:23 -07:00
if ( ! data . errorMessage ) return callback ( ) ;
return certificateRenewalError ( id , data . domain , data . errorMessage , callback ) ;
2019-03-04 14:50:56 -08:00
2019-03-25 14:02:23 -07:00
case eventlog . ACTION _BACKUP _FINISH :
2019-03-25 14:53:12 -07:00
if ( ! data . errorMessage || source . username !== 'cron' ) return callback ( ) ;
return backupFailed ( id , data . taskId , data . errorMessage , callback ) ; // only notify for automated backups
2019-03-04 15:00:23 -08:00
2019-03-25 14:02:23 -07:00
default :
return callback ( ) ;
2019-02-27 16:10:54 -08:00
}
}