2016-06-21 05:44:02 +08:00
|
|
|
const IMAPConnection = require('./imap/connection');
|
2016-06-20 15:19:16 +08:00
|
|
|
const RefreshMailboxesOperation = require('./imap/refresh-mailboxes-operation')
|
2016-06-21 05:44:02 +08:00
|
|
|
const SyncMailboxOperation = require('./imap/sync-mailbox-operation')
|
2016-06-21 08:33:23 +08:00
|
|
|
//
|
|
|
|
// account.syncPolicy = {
|
|
|
|
// afterSync: 'idle',
|
|
|
|
// limit: {
|
|
|
|
// after: Date.now() - 7 * 24 * 60 * 60 * 1000,
|
|
|
|
// count: 10000,
|
|
|
|
// },
|
|
|
|
// folderRecentSync: {
|
|
|
|
// every: 60 * 1000,
|
|
|
|
// },
|
|
|
|
// folderDeepSync: {
|
|
|
|
// every: 5 * 60 * 1000,
|
|
|
|
// },
|
|
|
|
// expiration: Date.now() + 60 * 60 * 1000,
|
|
|
|
// }
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-20 15:19:16 +08:00
|
|
|
class SyncWorker {
|
2016-06-21 08:33:23 +08:00
|
|
|
|
2016-06-20 15:19:16 +08:00
|
|
|
constructor(account, db) {
|
2016-06-21 08:33:23 +08:00
|
|
|
this._db = db;
|
|
|
|
this._conn = null;
|
|
|
|
this._account = account;
|
|
|
|
this._lastFolderRecentSync = null;
|
|
|
|
this._lastFolderDeepSync = null;
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-21 08:33:23 +08:00
|
|
|
this._syncTimer = null;
|
|
|
|
this._expirationTimer = null;
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-21 08:33:23 +08:00
|
|
|
this.syncNow();
|
|
|
|
this.scheduleExpiration();
|
|
|
|
}
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-21 08:33:23 +08:00
|
|
|
// TODO: How does this get called?
|
|
|
|
onAccountChanged() {
|
|
|
|
this.syncNow();
|
|
|
|
this.scheduleExpiration();
|
|
|
|
}
|
|
|
|
|
|
|
|
onExpired() {
|
|
|
|
// Returning syncs to the unclaimed queue every so often is healthy.
|
|
|
|
// TODO: That.
|
|
|
|
}
|
|
|
|
|
|
|
|
onSyncDidComplete() {
|
|
|
|
const {afterSync} = this._account.syncPolicy;
|
|
|
|
|
|
|
|
if (afterSync === 'idle') {
|
|
|
|
this.getInboxCategory().then((inboxCategory) => {
|
|
|
|
this._conn.openBox(inboxCategory.name, true).then(() => {
|
|
|
|
console.log(" - Idling on inbox category");
|
|
|
|
});
|
2016-06-19 18:02:32 +08:00
|
|
|
});
|
2016-06-21 08:33:23 +08:00
|
|
|
} else if (afterSync === 'close') {
|
|
|
|
console.log(" - Closing connection");
|
|
|
|
this._conn.end();
|
|
|
|
this._conn = null;
|
|
|
|
} else {
|
|
|
|
throw new Error(`onSyncDidComplete: Unknown afterSync behavior: ${afterSync}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onConnectionIdleUpdate() {
|
|
|
|
this.getInboxCategory((inboxCategory) => {
|
|
|
|
this._conn.runOperation(new SyncMailboxOperation(inboxCategory, {
|
|
|
|
scanAllUIDs: false,
|
|
|
|
limit: this.account.syncPolicy.options,
|
|
|
|
}));
|
2016-06-19 18:02:32 +08:00
|
|
|
});
|
2016-06-21 08:33:23 +08:00
|
|
|
}
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-21 08:33:23 +08:00
|
|
|
getInboxCategory() {
|
|
|
|
return this._db.Category.find({where: {role: 'inbox'}})
|
|
|
|
}
|
|
|
|
|
|
|
|
getCurrentFolderSyncOptionsForPolicy() {
|
|
|
|
const {folderRecentSync, folderDeepSync, limit} = this._account.syncPolicy;
|
|
|
|
|
|
|
|
if (Date.now() - this._lastFolderDeepSync > folderDeepSync.every) {
|
|
|
|
return {
|
|
|
|
mode: 'deep',
|
|
|
|
options: {
|
|
|
|
scanAllUIDs: true,
|
|
|
|
limit: limit,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (Date.now() - this._lastFolderRecentSync > folderRecentSync.every) {
|
|
|
|
return {
|
|
|
|
mode: 'shallow',
|
|
|
|
options: {
|
|
|
|
scanAllUIDs: false,
|
|
|
|
limit: limit,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
mode: 'none',
|
|
|
|
};
|
2016-06-20 15:19:16 +08:00
|
|
|
}
|
2016-06-19 18:02:32 +08:00
|
|
|
|
2016-06-21 08:33:23 +08:00
|
|
|
ensureConnection() {
|
|
|
|
if (!this._conn) {
|
|
|
|
const conn = new IMAPConnection(this._db, {
|
|
|
|
user: 'inboxapptest1@fastmail.fm',
|
|
|
|
password: 'trar2e',
|
|
|
|
host: 'mail.messagingengine.com',
|
|
|
|
port: 993,
|
|
|
|
tls: true,
|
|
|
|
});
|
|
|
|
conn.on('mail', () => {
|
|
|
|
this.onConnectionIdleUpdate();
|
|
|
|
})
|
|
|
|
conn.on('update', () => {
|
|
|
|
this.onConnectionIdleUpdate();
|
|
|
|
})
|
|
|
|
conn.on('queue-empty', () => {
|
|
|
|
});
|
|
|
|
|
|
|
|
this._conn = conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._conn.connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
queueOperationsForUpdates() {
|
|
|
|
// todo: syncback operations belong here!
|
|
|
|
return this._conn.runOperation(new RefreshMailboxesOperation())
|
|
|
|
}
|
|
|
|
|
|
|
|
queueOperationsForFolderSyncs() {
|
2016-06-20 15:19:16 +08:00
|
|
|
const {Category} = this._db;
|
2016-06-21 08:33:23 +08:00
|
|
|
const {mode, options} = this.getCurrentFolderSyncOptionsForPolicy();
|
|
|
|
|
|
|
|
if (mode === 'none') {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Category.findAll().then((categories) => {
|
2016-06-20 15:19:16 +08:00
|
|
|
const priority = ['inbox', 'drafts', 'sent'];
|
2016-06-21 08:33:23 +08:00
|
|
|
const sorted = categories.sort((a, b) =>
|
|
|
|
priority.indexOf(b.role) - priority.indexOf(a.role)
|
|
|
|
)
|
|
|
|
return Promise.all(sorted.map((cat) =>
|
|
|
|
this._conn.runOperation(new SyncMailboxOperation(cat, options))
|
|
|
|
)).then(() => {
|
|
|
|
if (mode === 'deep') {
|
|
|
|
this._lastFolderDeepSync = Date.now();
|
|
|
|
this._lastFolderRecentSync = Date.now();
|
|
|
|
} else if (mode === 'shallow') {
|
|
|
|
this._lastFolderRecentSync = Date.now();
|
|
|
|
}
|
|
|
|
});
|
2016-06-20 15:19:16 +08:00
|
|
|
});
|
2016-06-19 18:02:32 +08:00
|
|
|
}
|
2016-06-21 08:33:23 +08:00
|
|
|
|
|
|
|
syncNow() {
|
|
|
|
clearTimeout(this._syncTimer);
|
|
|
|
|
|
|
|
this.ensureConnection().then(() =>
|
|
|
|
this.queueOperationsForUpdates().then(() =>
|
|
|
|
this.queueOperationsForFolderSyncs()
|
|
|
|
)
|
|
|
|
).catch((err) => {
|
|
|
|
// Sync has failed for some reason. What do we do?!
|
|
|
|
console.error(err);
|
|
|
|
}).finally(() => {
|
|
|
|
this.onSyncDidComplete();
|
|
|
|
this.scheduleNextSync();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduleExpiration() {
|
|
|
|
const {expiration} = this._account.syncPolicy;
|
|
|
|
|
|
|
|
clearTimeout(this._expirationTimer);
|
|
|
|
this._expirationTimer = setTimeout(() => this.onExpired(), expiration);
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduleNextSync() {
|
|
|
|
const {folderRecentSync, folderDeepSync} = this._account.syncPolicy;
|
|
|
|
|
|
|
|
let target = Number.MAX_SAFE_INTEGER;
|
|
|
|
|
|
|
|
if (folderRecentSync) {
|
|
|
|
target = Math.min(target, this._lastFolderRecentSync + folderRecentSync.every);
|
|
|
|
}
|
|
|
|
if (folderDeepSync) {
|
|
|
|
target = Math.min(target, this._lastFolderDeepSync + folderDeepSync.every);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(`Next sync scheduled for ${new Date(target).toLocaleString()}`);
|
|
|
|
|
|
|
|
this._syncTimer = setTimeout(() => {
|
|
|
|
this.syncNow();
|
|
|
|
}, target - Date.now());
|
|
|
|
}
|
2016-06-19 18:02:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = SyncWorker;
|