Mailspring/packages/client-app/internal_packages/sync-health-checker/lib/sync-health-checker.es6
Juan Tejada d1b81b6afe [client-app] Prevent from making any requests when NylasID isn't present
Summary:
Sometimes, when logging out of your NylasID and restarting the app, we
would continue making some requests from the worker window that required a
NylasID. This would make the app enter a restart loop and become
completely unresponsive because when we made a request without a
NylasID, we would force the user to log out and restart the app, and
then we would again make the requests without the id, ad infinitum.

To fix this, we make sure we have a NylasID before making any requests
that require it

Test Plan: manual

Reviewers: halla, evan

Reviewed By: halla, evan

Differential Revision: https://phab.nylas.com/D4344
2017-04-04 16:26:32 -07:00

115 lines
3.6 KiB
JavaScript

import {ipcRenderer} from 'electron'
import {IdentityStore, AccountStore, Actions, NylasAPI, NylasAPIRequest} from 'nylas-exports'
const CHECK_HEALTH_INTERVAL = 5 * 60 * 1000;
class SyncHealthChecker {
constructor() {
this._lastSyncActivity = null
this._interval = null
}
start() {
if (this._interval) {
console.warn('SyncHealthChecker has already been started')
} else {
this._interval = setInterval(this._checkSyncHealth, CHECK_HEALTH_INTERVAL)
}
}
stop() {
clearInterval(this._interval)
this._interval = null
}
// This is a separate function so the request can be manipulated in the specs
_buildRequest = () => {
return new NylasAPIRequest({
api: NylasAPI,
options: {
accountId: AccountStore.accounts()[0].id,
path: `/health`,
},
});
}
_checkSyncHealth = async () => {
try {
if (!IdentityStore.identity()) {
return
}
const request = this._buildRequest()
const response = await request.run()
this._lastSyncActivity = response
} catch (err) {
if (/ECONNREFUSED/i.test(err.toString())) {
this._onWorkerWindowUnavailable()
} else {
err.message = `Error checking sync health: ${err.message}`
NylasEnv.reportError(err)
}
}
}
_onWorkerWindowUnavailable() {
let extraData = {};
// Extract data that we want to report. We'll report the entire
// _lastSyncActivity object, but it'll probably be useful if we can segment
// by the data in the oldest or newest entry, so we report those as
// individual values too.
const lastActivityEntries = Object.entries(this._lastSyncActivity || {})
if (lastActivityEntries.length > 0) {
const times = lastActivityEntries.map((entry) => entry[1].time)
const now = Date.now()
const maxTime = Math.max(...times)
const mostRecentEntry = lastActivityEntries.find((entry) => entry[1].time === maxTime)
const [mostRecentActivityAccountId, {
activity: mostRecentActivity,
time: mostRecentActivityTime,
}] = mostRecentEntry;
const mostRecentDuration = now - mostRecentActivityTime
const minTime = Math.min(...times)
const leastRecentEntry = lastActivityEntries.find((entry) => entry[1].time === minTime)
const [leastRecentActivityAccountId, {
activity: leastRecentActivity,
time: leastRecentActivityTime,
}] = leastRecentEntry;
const leastRecentDuration = now - leastRecentActivityTime
extraData = {
mostRecentActivity,
mostRecentActivityTime,
mostRecentActivityAccountId,
mostRecentDuration,
leastRecentActivity,
leastRecentActivityTime,
leastRecentActivityAccountId,
leastRecentDuration,
}
}
NylasEnv.reportError(new Error('Worker window was unavailable'), {
// This information isn't as useful in Sentry, but include it here until
// the data is actually sent to Mixpanel. (See the TODO below)
lastActivityPerAccount: this._lastSyncActivity,
...extraData,
})
// TODO: This doesn't make it to Mixpanel because our analytics process
// lives in the worker window. We should move analytics to the main process.
// https://phab.nylas.com/T8029
Actions.recordUserEvent('Worker Window Unavailable', {
lastActivityPerAccount: this._lastSyncActivity,
...extraData,
})
console.log(`Detected worker window was unavailable. Restarting it.`, this._lastSyncActivity)
ipcRenderer.send('ensure-worker-window')
}
}
export default new SyncHealthChecker()