2017-07-06 21:48:17 +02:00
|
|
|
import { Logger } from 'lib/logger.js';
|
|
|
|
import { Setting } from 'lib/models/setting.js';
|
|
|
|
import { OneDriveApi } from 'lib/onedrive-api.js';
|
2017-07-08 00:25:03 +02:00
|
|
|
import { parameters } from 'lib/parameters.js';
|
2017-07-06 21:48:17 +02:00
|
|
|
import { FileApi } from 'lib/file-api.js';
|
2017-07-24 20:01:40 +02:00
|
|
|
import { Database } from 'lib/database.js';
|
2017-07-06 21:48:17 +02:00
|
|
|
import { Synchronizer } from 'lib/synchronizer.js';
|
|
|
|
import { FileApiDriverOneDrive } from 'lib/file-api-driver-onedrive.js';
|
2017-07-24 20:01:40 +02:00
|
|
|
import { shim } from 'lib/shim.js';
|
2017-08-01 20:14:47 +02:00
|
|
|
import { time } from 'lib/time-utils.js';
|
2017-07-24 20:01:40 +02:00
|
|
|
import { FileApiDriverMemory } from 'lib/file-api-driver-memory.js';
|
2017-08-20 16:29:18 +02:00
|
|
|
import { _ } from 'lib/locale.js';
|
2017-07-06 21:48:17 +02:00
|
|
|
|
|
|
|
const reg = {};
|
|
|
|
|
2017-07-31 20:32:51 +02:00
|
|
|
reg.initSynchronizerStates_ = {};
|
|
|
|
|
2017-07-06 21:48:17 +02:00
|
|
|
reg.logger = () => {
|
2017-07-15 01:12:32 +02:00
|
|
|
if (!reg.logger_) {
|
2017-08-22 19:57:35 +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_;
|
|
|
|
}
|
|
|
|
|
2017-07-09 17:47:05 +02:00
|
|
|
reg.setLogger = (l) => {
|
|
|
|
reg.logger_ = l;
|
|
|
|
}
|
|
|
|
|
2017-07-06 21:48:17 +02:00
|
|
|
reg.oneDriveApi = () => {
|
|
|
|
if (reg.oneDriveApi_) return reg.oneDriveApi_;
|
|
|
|
|
2017-07-07 00:15:31 +02:00
|
|
|
const isPublic = Setting.value('appType') != 'cli';
|
2017-07-06 21:48:17 +02:00
|
|
|
|
2017-07-08 00:25:03 +02:00
|
|
|
reg.oneDriveApi_ = new OneDriveApi(parameters().oneDrive.id, parameters().oneDrive.secret, isPublic);
|
2017-07-09 17:47:05 +02:00
|
|
|
reg.oneDriveApi_.setLogger(reg.logger());
|
2017-07-06 21:48:17 +02:00
|
|
|
|
|
|
|
reg.oneDriveApi_.on('authRefreshed', (a) => {
|
2017-07-06 23:30:45 +02:00
|
|
|
reg.logger().info('Saving updated OneDrive auth.');
|
2017-07-24 20:58:11 +02:00
|
|
|
Setting.setValue('sync.3.auth', a ? JSON.stringify(a) : null);
|
2017-07-06 21:48:17 +02:00
|
|
|
});
|
2017-07-07 00:15:31 +02:00
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
let auth = Setting.value('sync.3.auth');
|
2017-07-07 00:15:31 +02:00
|
|
|
if (auth) {
|
|
|
|
try {
|
|
|
|
auth = JSON.parse(auth);
|
|
|
|
} catch (error) {
|
|
|
|
reg.logger().warn('Could not parse OneDrive auth token');
|
|
|
|
reg.logger().warn(error);
|
|
|
|
auth = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg.oneDriveApi_.setAuth(auth);
|
|
|
|
}
|
2017-07-06 21:48:17 +02:00
|
|
|
|
|
|
|
return reg.oneDriveApi_;
|
|
|
|
}
|
|
|
|
|
2017-07-31 20:32:51 +02:00
|
|
|
reg.initSynchronizer_ = async (syncTargetId) => {
|
2017-07-24 20:01:40 +02:00
|
|
|
if (!reg.db()) throw new Error('Cannot initialize synchronizer: db not initialized');
|
2017-07-06 21:48:17 +02:00
|
|
|
|
2017-07-24 20:01:40 +02:00
|
|
|
let fileApi = null;
|
2017-07-06 21:48:17 +02:00
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) {
|
2017-07-06 21:48:17 +02:00
|
|
|
|
2017-07-24 20:01:40 +02:00
|
|
|
if (!reg.oneDriveApi().auth()) throw new Error('User is not authentified');
|
|
|
|
let appDir = await reg.oneDriveApi().appDirectory();
|
|
|
|
fileApi = new FileApi(appDir, new FileApiDriverOneDrive(reg.oneDriveApi()));
|
2017-07-06 21:48:17 +02:00
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
} else if (syncTargetId == Setting.SYNC_TARGET_MEMORY) {
|
2017-07-24 20:01:40 +02:00
|
|
|
|
|
|
|
fileApi = new FileApi('joplin', new FileApiDriverMemory());
|
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
} else if (syncTargetId == Setting.SYNC_TARGET_FILESYSTEM) {
|
2017-07-24 20:01:40 +02:00
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
let syncDir = Setting.value('sync.2.path');
|
|
|
|
if (!syncDir) throw new Error(_('Please set the "sync.2.path" config value to the desired synchronisation destination.'));
|
2017-07-24 20:01:40 +02:00
|
|
|
await shim.fs.mkdirp(syncDir, 0o755);
|
|
|
|
fileApi = new FileApi(syncDir, new shim.FileApiDriverLocal());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new Error('Unknown sync target: ' + syncTargetId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-07-24 20:58:11 +02:00
|
|
|
fileApi.setSyncTargetId(syncTargetId);
|
2017-07-24 20:01:40 +02:00
|
|
|
fileApi.setLogger(reg.logger());
|
|
|
|
|
|
|
|
let sync = new Synchronizer(reg.db(), fileApi, Setting.value('appType'));
|
|
|
|
sync.setLogger(reg.logger());
|
|
|
|
sync.dispatch = reg.dispatch;
|
|
|
|
|
|
|
|
return sync;
|
2017-07-06 21:48:17 +02:00
|
|
|
}
|
|
|
|
|
2017-07-31 20:32:51 +02:00
|
|
|
reg.synchronizer = async (syncTargetId) => {
|
|
|
|
if (!reg.synchronizers_) reg.synchronizers_ = [];
|
|
|
|
if (reg.synchronizers_[syncTargetId]) return reg.synchronizers_[syncTargetId];
|
|
|
|
if (!reg.db()) throw new Error('Cannot initialize synchronizer: db not initialized');
|
|
|
|
|
|
|
|
if (reg.initSynchronizerStates_[syncTargetId] == 'started') {
|
|
|
|
// Synchronizer is already being initialized, so wait here till it's done.
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const iid = setInterval(() => {
|
|
|
|
if (reg.initSynchronizerStates_[syncTargetId] == 'ready') {
|
|
|
|
clearInterval(iid);
|
|
|
|
resolve(reg.synchronizers_[syncTargetId]);
|
|
|
|
}
|
2017-08-01 20:14:47 +02:00
|
|
|
if (reg.initSynchronizerStates_[syncTargetId] == 'error') {
|
|
|
|
clearInterval(iid);
|
|
|
|
reject(new Error('Could not initialise synchroniser'));
|
|
|
|
}
|
2017-07-31 20:32:51 +02:00
|
|
|
}, 1000);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
reg.initSynchronizerStates_[syncTargetId] = 'started';
|
2017-08-01 20:14:47 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
const sync = await reg.initSynchronizer_(syncTargetId);
|
|
|
|
reg.synchronizers_[syncTargetId] = sync;
|
|
|
|
reg.initSynchronizerStates_[syncTargetId] = 'ready';
|
|
|
|
return sync;
|
|
|
|
} catch (error) {
|
|
|
|
reg.initSynchronizerStates_[syncTargetId] = 'error';
|
|
|
|
throw error;
|
|
|
|
}
|
2017-07-31 20:32:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-26 23:07:27 +02:00
|
|
|
reg.syncHasAuth = (syncTargetId) => {
|
2017-07-26 19:49:01 +02:00
|
|
|
if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE && !reg.oneDriveApi().auth()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-19 21:15:55 +02:00
|
|
|
reg.scheduleSync = async (delay = null) => {
|
|
|
|
if (delay === null) delay = 1000 * 10;
|
|
|
|
|
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
|
|
|
|
|
|
|
reg.logger().info('Scheduling sync operation...');
|
|
|
|
|
2017-07-24 22:36:49 +02:00
|
|
|
const timeoutCallback = async () => {
|
2017-07-16 23:17:22 +02:00
|
|
|
reg.scheduleSyncId_ = null;
|
|
|
|
reg.logger().info('Doing scheduled sync');
|
|
|
|
|
2017-07-26 19:49:01 +02:00
|
|
|
const syncTargetId = Setting.value('sync.target');
|
|
|
|
|
2017-07-26 23:07:27 +02:00
|
|
|
if (!reg.syncHasAuth(syncTargetId)) {
|
2017-07-26 23:27:03 +02:00
|
|
|
reg.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.');
|
2017-07-16 23:17:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-24 21:47:01 +02:00
|
|
|
try {
|
2017-07-30 22:22:57 +02:00
|
|
|
const sync = await reg.synchronizer(syncTargetId);
|
|
|
|
|
2017-08-19 22:56:28 +02:00
|
|
|
const contextKey = 'sync.' + syncTargetId + '.context';
|
|
|
|
let context = Setting.value(contextKey);
|
2017-07-30 22:22:57 +02:00
|
|
|
context = context ? JSON.parse(context) : {};
|
|
|
|
try {
|
|
|
|
let newContext = await sync.start({ context: context });
|
2017-08-19 22:56:28 +02:00
|
|
|
Setting.setValue(contextKey, JSON.stringify(newContext));
|
2017-07-30 22:22:57 +02:00
|
|
|
} catch (error) {
|
|
|
|
if (error.code == 'alreadyStarted') {
|
|
|
|
reg.logger().info(error.message);
|
|
|
|
} else {
|
|
|
|
throw error;
|
|
|
|
}
|
2017-07-24 21:47:01 +02:00
|
|
|
}
|
2017-07-30 22:22:57 +02:00
|
|
|
} catch (error) {
|
|
|
|
reg.logger().info('Could not run background sync: ');
|
|
|
|
reg.logger().info(error);
|
2017-07-24 21:47:01 +02:00
|
|
|
}
|
2017-07-26 19:49:01 +02:00
|
|
|
|
|
|
|
reg.setupRecurrentSync();
|
2017-07-24 22:36:49 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (delay === 0) {
|
|
|
|
timeoutCallback();
|
|
|
|
} else {
|
|
|
|
reg.scheduleSyncId_ = setTimeout(timeoutCallback, delay);
|
|
|
|
}
|
2017-07-16 23:17:22 +02:00
|
|
|
}
|
|
|
|
|
2017-07-26 19:49:01 +02:00
|
|
|
reg.syncStarted = async () => {
|
2017-07-26 23:07:27 +02:00
|
|
|
const syncTarget = Setting.value('sync.target');
|
|
|
|
if (!reg.syncHasAuth(syncTarget)) return false;
|
|
|
|
const sync = await reg.synchronizer(syncTarget);
|
2017-07-26 19:49:01 +02:00
|
|
|
return sync.state() != 'idle';
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-08-20 16:29:18 +02:00
|
|
|
if (!Setting.value('sync.interval')) {
|
|
|
|
reg.logger().debug('Recurrent sync is disabled');
|
|
|
|
} else {
|
|
|
|
reg.logger().debug('Setting up recurrent sync with interval ' + Setting.value('sync.interval'));
|
2017-07-26 19:49:01 +02:00
|
|
|
|
2017-10-19 00:13:53 +02:00
|
|
|
reg.recurrentSyncId_ = shim.setInterval(() => {
|
2017-08-20 16:29:18 +02:00
|
|
|
reg.logger().info('Running background sync on timer...');
|
|
|
|
reg.scheduleSync(0);
|
|
|
|
}, 1000 * Setting.value('sync.interval'));
|
|
|
|
}
|
2017-07-26 19:49:01 +02:00
|
|
|
}
|
|
|
|
|
2017-07-06 21:48:17 +02:00
|
|
|
reg.setDb = (v) => {
|
|
|
|
reg.db_ = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg.db = () => {
|
|
|
|
return reg.db_;
|
|
|
|
}
|
|
|
|
|
|
|
|
export { reg }
|