2018-03-09 22:59:12 +02:00
const { Logger } = require ( 'lib/logger.js' ) ;
const Setting = require ( 'lib/models/Setting.js' ) ;
const { shim } = require ( 'lib/shim.js' ) ;
const SyncTargetRegistry = require ( 'lib/SyncTargetRegistry.js' ) ;
const { _ } = require ( 'lib/locale.js' ) ;
2017-07-06 21:48:17 +02:00
const reg = { } ;
2017-11-24 01:10:55 +02:00
reg . syncTargets _ = { } ;
2017-07-31 20:32:51 +02:00
2017-07-06 21:48:17 +02:00
reg . logger = ( ) => {
2017-07-15 01:12:32 +02:00
if ( ! reg . logger _ ) {
2019-10-09 21:35:13 +02:00
// console.warn('Calling logger before it is initialized');
2017-07-15 01:12:32 +02:00
return new Logger ( ) ;
}
2017-07-06 21:48:17 +02:00
return reg . logger _ ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-06 21:48:17 +02:00
2019-07-29 15:43:53 +02:00
reg . setLogger = l => {
2017-07-09 17:47:05 +02:00
reg . logger _ = l ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-09 17:47:05 +02:00
2019-07-29 15:43:53 +02:00
reg . setShowErrorMessageBoxHandler = v => {
2017-12-01 19:47:18 +02:00
reg . showErrorMessageBoxHandler _ = v ;
2019-07-29 15:43:53 +02:00
} ;
2017-12-01 19:47:18 +02:00
2019-07-29 15:43:53 +02:00
reg . showErrorMessageBox = message => {
2017-12-01 19:47:18 +02:00
if ( ! reg . showErrorMessageBoxHandler _ ) return ;
reg . showErrorMessageBoxHandler _ ( message ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-12-01 19:47:18 +02:00
2018-05-21 17:26:01 +02:00
reg . resetSyncTarget = ( syncTargetId = null ) => {
if ( syncTargetId === null ) syncTargetId = Setting . value ( 'sync.target' ) ;
delete reg . syncTargets _ [ syncTargetId ] ;
2019-07-29 15:43:53 +02:00
} ;
2018-05-21 17:26:01 +02:00
2017-11-24 01:10:55 +02:00
reg . syncTarget = ( syncTargetId = null ) => {
2018-03-09 22:59:12 +02:00
if ( syncTargetId === null ) syncTargetId = Setting . value ( 'sync.target' ) ;
2017-11-24 01:10:55 +02:00
if ( reg . syncTargets _ [ syncTargetId ] ) return reg . syncTargets _ [ syncTargetId ] ;
2017-07-06 21:48:17 +02:00
2017-11-24 20:37:40 +02:00
const SyncTargetClass = SyncTargetRegistry . classById ( syncTargetId ) ;
2018-03-09 22:59:12 +02:00
if ( ! reg . db ( ) ) throw new Error ( 'Cannot initialize sync without a db' ) ;
2017-07-06 21:48:17 +02:00
2017-11-24 01:10:55 +02:00
const target = new SyncTargetClass ( reg . db ( ) ) ;
target . setLogger ( reg . logger ( ) ) ;
reg . syncTargets _ [ syncTargetId ] = target ;
return target ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-06 21:48:17 +02:00
2018-03-09 21:07:38 +02:00
reg . scheduleSync = async ( delay = null , syncOptions = null ) => {
2018-02-27 23:19:11 +02:00
if ( delay === null ) delay = 1000 * 10 ;
2018-03-09 21:07:38 +02:00
if ( syncOptions === null ) syncOptions = { } ;
2017-07-19 21:15:55 +02:00
2017-11-28 02:22:38 +02:00
let promiseResolve = null ;
2019-09-13 00:16:42 +02:00
const promise = new Promise ( ( resolve ) => {
2017-11-28 02:22:38 +02:00
promiseResolve = resolve ;
} ) ;
2017-07-17 22:22:05 +02:00
if ( reg . scheduleSyncId _ ) {
clearTimeout ( reg . scheduleSyncId _ ) ;
reg . scheduleSyncId _ = null ;
}
2017-07-16 23:17:22 +02:00
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Scheduling sync operation...' ) ;
2017-07-16 23:17:22 +02:00
2019-07-29 15:43:53 +02:00
if ( Setting . value ( 'env' ) === 'dev' && delay !== 0 ) {
reg . logger ( ) . info ( 'Schedule sync DISABLED!!!' ) ;
2019-02-03 22:19:17 +02:00
return ;
}
2017-11-03 20:51:13 +02:00
2017-07-24 22:36:49 +02:00
const timeoutCallback = async ( ) => {
2017-07-16 23:17:22 +02:00
reg . scheduleSyncId _ = null ;
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Preparing scheduled sync' ) ;
2017-07-16 23:17:22 +02:00
2018-03-09 22:59:12 +02:00
const syncTargetId = Setting . value ( 'sync.target' ) ;
2017-07-26 19:49:01 +02:00
2019-07-29 15:43:53 +02:00
if ( ! ( await reg . syncTarget ( syncTargetId ) . isAuthenticated ( ) ) ) {
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Synchroniser is missing credentials - manual sync required to authenticate.' ) ;
2017-11-28 02:22:38 +02:00
promiseResolve ( ) ;
2017-07-16 23:17:22 +02:00
return ;
}
2017-07-24 21:47:01 +02:00
try {
2017-11-24 01:10:55 +02:00
const sync = await reg . syncTarget ( syncTargetId ) . synchronizer ( ) ;
2017-07-30 22:22:57 +02:00
2019-09-19 23:51:18 +02:00
const contextKey = ` sync. ${ syncTargetId } .context ` ;
2017-08-19 22:56:28 +02:00
let context = Setting . value ( contextKey ) ;
2018-01-07 21:20:10 +02:00
try {
context = context ? JSON . parse ( context ) : { } ;
} catch ( error ) {
// Clearing the context is inefficient since it means all items are going to be re-downloaded
// however it won't result in duplicate items since the synchroniser is going to compare each
// item to the current state.
2019-09-19 23:51:18 +02:00
reg . logger ( ) . warn ( ` Could not parse JSON sync context ${ contextKey } : ` , context ) ;
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Clearing context and starting from scratch' ) ;
2018-01-07 21:20:10 +02:00
context = null ;
}
2017-07-30 22:22:57 +02:00
try {
2019-07-29 15:43:53 +02:00
reg . logger ( ) . info ( 'Starting scheduled sync' ) ;
2018-03-09 21:07:38 +02:00
const options = Object . assign ( { } , syncOptions , { context : context } ) ;
2018-06-11 00:16:27 +02:00
if ( ! options . saveContextHandler ) {
2019-07-29 15:43:53 +02:00
options . saveContextHandler = newContext => {
2018-06-11 00:16:27 +02:00
Setting . setValue ( contextKey , JSON . stringify ( newContext ) ) ;
2019-07-29 15:43:53 +02:00
} ;
2018-06-11 00:16:27 +02:00
}
2018-03-09 21:07:38 +02:00
const newContext = await sync . start ( options ) ;
2017-08-19 22:56:28 +02:00
Setting . setValue ( contextKey , JSON . stringify ( newContext ) ) ;
2017-07-30 22:22:57 +02:00
} catch ( error ) {
2018-03-09 22:59:12 +02:00
if ( error . code == 'alreadyStarted' ) {
2017-07-30 22:22:57 +02:00
reg . logger ( ) . info ( error . message ) ;
} else {
2017-11-28 02:22:38 +02:00
promiseResolve ( ) ;
2017-07-30 22:22:57 +02:00
throw error ;
}
2017-07-24 21:47:01 +02:00
}
2017-07-30 22:22:57 +02:00
} catch ( error ) {
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Could not run background sync:' ) ;
2017-07-30 22:22:57 +02:00
reg . logger ( ) . info ( error ) ;
2017-12-01 19:47:18 +02:00
// Special case to display OneDrive Business error. This is the full error that's received when trying to use a OneDrive Business account:
//
// {"error":"invalid_client","error_description":"AADSTS50011: The reply address 'http://localhost:1917' does not match the reply addresses configured for
// the application: 'cbabb902-d276-4ea4-aa88-062a5889d6dc'. More details: not specified\r\nTrace ID: 6e63dac6-8b37-47e2-bd1b-4768f8713400\r\nCorrelation
// ID: acfd6503-8d97-4349-ae2e-e7a19dd7b6bc\r\nTimestamp: 2017-12-01 13:35:55Z","error_codes":[50011],"timestamp":"2017-12-01 13:35:55Z","trace_id":
// "6e63dac6-8b37-47e2-bd1b-4768f8713400","correlation_id":"acfd6503-8d97-4349-ae2e-e7a19dd7b6bc"}: TOKEN: null Error: {"error":"invalid_client",
// "error_description":"AADSTS50011: The reply address 'http://localhost:1917' does not match the reply addresses configured for the application:
// 'cbabb902-d276-4ea4-aa88-062a5889d6dc'. More details: not specified\r\nTrace ID: 6e63dac6-8b37-47e2-bd1b-4768f8713400\r\nCorrelation ID
// acfd6503-8d97-4349-ae2e-e7a19dd7b6bc\r\nTimestamp: 2017-12-01 13:35:55Z","error_codes":[50011],"timestamp":"2017-12-01 13:35:55Z","trace_id":
// "6e63dac6-8b37-47e2-bd1b-4768f8713400","correlation_id":"acfd6503-8d97-4349-ae2e-e7a19dd7b6bc"}
if ( error && error . message && error . message . indexOf ( '"invalid_client"' ) >= 0 ) {
2018-03-09 22:59:12 +02:00
reg . showErrorMessageBox ( _ ( 'Could not synchronize with OneDrive.\n\nThis error often happens when using OneDrive for Business, which unfortunately cannot be supported.\n\nPlease consider using a regular OneDrive account.' ) ) ;
2017-12-01 19:47:18 +02:00
}
2017-07-24 21:47:01 +02:00
}
2017-07-26 19:49:01 +02:00
reg . setupRecurrentSync ( ) ;
2017-11-28 02:22:38 +02:00
promiseResolve ( ) ;
2017-07-24 22:36:49 +02:00
} ;
if ( delay === 0 ) {
timeoutCallback ( ) ;
} else {
reg . scheduleSyncId _ = setTimeout ( timeoutCallback , delay ) ;
}
2017-11-28 02:22:38 +02:00
return promise ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-16 23:17:22 +02:00
2017-07-26 19:49:01 +02:00
reg . setupRecurrentSync = ( ) => {
2017-07-27 19:25:42 +02:00
if ( reg . recurrentSyncId _ ) {
2017-10-19 00:13:53 +02:00
shim . clearInterval ( reg . recurrentSyncId _ ) ;
2017-07-27 19:25:42 +02:00
reg . recurrentSyncId _ = null ;
2017-07-26 19:49:01 +02:00
}
2018-03-09 22:59:12 +02:00
if ( ! Setting . value ( 'sync.interval' ) ) {
reg . logger ( ) . debug ( 'Recurrent sync is disabled' ) ;
2017-08-20 16:29:18 +02:00
} else {
2019-09-19 23:51:18 +02:00
reg . logger ( ) . debug ( ` Setting up recurrent sync with interval ${ Setting . value ( 'sync.interval' ) } ` ) ;
2017-07-26 19:49:01 +02:00
2018-03-09 22:59:12 +02:00
if ( Setting . value ( 'env' ) === 'dev' ) {
reg . logger ( ) . info ( 'Recurrent sync operation DISABLED!!!' ) ;
2018-02-21 21:58:28 +02:00
return ;
}
2017-10-19 00:13:53 +02:00
reg . recurrentSyncId _ = shim . setInterval ( ( ) => {
2018-03-09 22:59:12 +02:00
reg . logger ( ) . info ( 'Running background sync on timer...' ) ;
2017-08-20 16:29:18 +02:00
reg . scheduleSync ( 0 ) ;
2018-03-09 22:59:12 +02:00
} , 1000 * Setting . value ( 'sync.interval' ) ) ;
2017-08-20 16:29:18 +02:00
}
2019-07-29 15:43:53 +02:00
} ;
2017-07-26 19:49:01 +02:00
2019-07-29 15:43:53 +02:00
reg . setDb = v => {
2017-07-06 21:48:17 +02:00
reg . db _ = v ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-06 21:48:17 +02:00
reg . db = ( ) => {
return reg . db _ ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-06 21:48:17 +02:00
2019-07-29 15:43:53 +02:00
module . exports = { reg } ;