diff --git a/packages/insomnia-app/app/models/settings.js b/packages/insomnia-app/app/models/settings.js
index 971414ce87..c938f92cea 100644
--- a/packages/insomnia-app/app/models/settings.js
+++ b/packages/insomnia-app/app/models/settings.js
@@ -55,9 +55,6 @@ type BaseSettings = {
useBulkHeaderEditor: boolean,
useBulkParametersEditor: boolean,
validateSSL: boolean,
-
- // Feature flags
- enableSyncBeta: boolean,
};
export type Settings = BaseModel & BaseSettings;
@@ -111,9 +108,6 @@ export function init(): BaseSettings {
useBulkHeaderEditor: false,
useBulkParametersEditor: false,
validateSSL: true,
-
- // Feature flags
- enableSyncBeta: false,
};
}
diff --git a/packages/insomnia-app/app/sync-legacy/__tests__/sync.test.js b/packages/insomnia-app/app/sync-legacy/__tests__/sync.test.js
deleted file mode 100644
index da5e28e5fc..0000000000
--- a/packages/insomnia-app/app/sync-legacy/__tests__/sync.test.js
+++ /dev/null
@@ -1,557 +0,0 @@
-import * as sync from '../index';
-import * as models from '../../models';
-import * as db from '../../common/database';
-import * as syncStorage from '../storage';
-import * as network from '../network';
-import { globalBeforeEach } from '../../__jest__/before-each';
-import * as session from '../../account/session';
-import * as crypt from '../../account/crypt';
-
-describe('Test push/pull behaviour', () => {
- beforeEach(async () => {
- await globalBeforeEach();
-
- // Reset some things
- sync._testReset();
- await _setSessionData();
- await _setupSessionMocks();
-
- // Init sync and storage
- const config = { inMemoryOnly: true, autoload: false, filename: null };
- await syncStorage.initDB(config, true);
-
- // Add some data
- await models.workspace.create({ _id: 'wrk_1', name: 'Workspace 1' });
- await models.workspace.create({ _id: 'wrk_2', name: 'Workspace 2' });
- await models.request.create({
- _id: 'req_1',
- name: 'Request 1',
- parentId: 'wrk_1',
- });
- await models.request.create({
- _id: 'req_2',
- name: 'Request 2',
- parentId: 'wrk_2',
- });
-
- // Create resources, resource groups, and configs
- const workspaces = await models.workspace.all();
- const requests = await models.request.all();
- for (const d of [...workspaces, ...requests]) {
- await sync.getOrCreateResourceForDoc(d);
- }
- });
-
- it('Pushes sync mode with and without resource group id', async () => {
- const request = await models.request.getById('req_1');
- const request2 = await models.request.getById('req_2');
- const resourceRequest = await syncStorage.getResourceByDocId(request._id);
- const resourceRequest2 = await syncStorage.getResourceByDocId(request2._id);
-
- // Set up sync modes
- await sync.createOrUpdateConfig(resourceRequest.resourceGroupId, {
- syncMode: syncStorage.SYNC_MODE_ON,
- });
- await sync.createOrUpdateConfig(resourceRequest2.resourceGroupId, {
- syncMode: syncStorage.SYNC_MODE_UNSET,
- });
-
- await sync.push(); // Push only active configs
- await sync.push(resourceRequest.resourceGroupId); // Force push rg_1
- await sync.push(resourceRequest2.resourceGroupId); // Force push rg_2
-
- expect(network.syncPush.mock.calls.length).toBe(3);
- expect(network.syncPush.mock.calls[0][0].length).toBe(2);
- expect(network.syncPush.mock.calls[0][0][0].id).toBe('wrk_1');
- expect(network.syncPush.mock.calls[0][0][1].id).toBe('req_1');
- expect(network.syncPush.mock.calls[1][0].length).toBe(2);
- expect(network.syncPush.mock.calls[1][0][0].id).toBe('wrk_1');
- expect(network.syncPush.mock.calls[1][0][1].id).toBe('req_1');
- expect(network.syncPush.mock.calls[2][0].length).toBe(2);
- expect(network.syncPush.mock.calls[2][0][0].id).toBe('wrk_2');
- expect(network.syncPush.mock.calls[2][0][1].id).toBe('req_2');
- });
-
- it('Updates dirty flag for push response', async () => {
- const request = await models.request.getById('req_1');
- const resourceRequest = await syncStorage.getResourceByDocId(request._id);
- await sync.createOrUpdateConfig(resourceRequest.resourceGroupId, {
- syncMode: syncStorage.SYNC_MODE_ON,
- });
-
- network.syncPush.mockReturnValueOnce({
- updated: [],
- created: [{ id: request._id, version: 'new-version' }],
- removed: [],
- conflicts: [],
- });
-
- const resourceBefore = await syncStorage.getResourceByDocId(request._id);
- await sync.push(resourceRequest.resourceGroupId);
- const resourceAfter = await syncStorage.getResourceByDocId(request._id);
-
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(2);
- expect(network.syncPush.mock.calls[0][0][0].id).toBe('wrk_1');
- expect(network.syncPush.mock.calls[0][0][1].id).toBe('req_1');
- expect(resourceBefore.dirty).toBe(true);
- expect(resourceAfter.dirty).toBe(false);
- });
-
- it('Updates resources for pull response', async () => {
- const request = await models.request.getById('req_1');
- const request2 = await models.request.getById('req_2');
- const requestNew = Object.assign({}, request, {
- _id: 'req_new',
- name: 'New Request',
- });
- const resourceBefore = await syncStorage.getResourceByDocId(request._id);
- const resource2Before = await syncStorage.getResourceByDocId(requestNew._id);
- await sync.createOrUpdateConfig(resourceBefore.resourceGroupId, {
- syncMode: syncStorage.SYNC_MODE_ON,
- });
- const updatedRequest = Object.assign({}, request, {
- name: 'Request Updated',
- });
- const updatedResource = Object.assign({}, resourceBefore, {
- version: 'ver1',
- encContent: await sync.encryptDoc(resourceBefore.resourceGroupId, updatedRequest),
- });
- const createdResourceNew = Object.assign({}, resourceBefore, {
- id: requestNew._id,
- resourceGroupId: 'rg_1',
- encContent: await sync.encryptDoc(resourceBefore.resourceGroupId, requestNew),
- });
-
- network.syncPull.mockReturnValueOnce({
- updatedResources: [updatedResource],
- createdResources: [createdResourceNew],
- idsToPush: [],
- idsToRemove: ['req_2'],
- });
-
- // Pull and get docs/resources
- await sync.pull(resourceBefore.resourceGroupId);
- const requestAfter = await models.request.getById(request._id);
- const request2After = await models.request.getById(request2._id);
- const requestNewAfter = await models.request.getById('req_new');
- const resourceAfter = await syncStorage.getResourceByDocId(
- request._id,
- resourceBefore.resourceGroupId,
- );
- const resource2After = await syncStorage.getResourceByDocId(request2._id);
- const resourceNewAfter = await syncStorage.getResourceByDocId(requestNewAfter._id);
-
- // Assert
- expect(resourceBefore.version).toBe('__NO_VERSION__');
- expect(resourceAfter.version).toBe(updatedResource.version);
- expect(resourceBefore.dirty).toBe(true);
- expect(resource2Before).toBe(null);
- expect(resourceAfter.dirty).toBe(false);
- expect(resource2After.removed).toBe(true);
- expect(requestAfter.name).toBe('Request Updated');
- expect(request2After).toBe(null);
- expect(resourceNewAfter).not.toBe(null);
- });
-
- it('Conflict: local version wins on modified before', async () => {
- const requestClient = await models.request.getById('req_1');
- const requestServer = Object.assign({}, requestClient, {
- name: 'Server Request',
- });
- const resourceRequest = await syncStorage.getResourceByDocId(requestClient._id);
- const resourceConflict = Object.assign({}, resourceRequest, {
- version: 'ver-2',
- encContent: await sync.encryptDoc(resourceRequest.resourceGroupId, requestServer),
- lastEdited: resourceRequest.lastEdited - 1000, // Same edited time
- });
-
- network.syncPush.mockReturnValueOnce({
- updated: [],
- created: [],
- removed: [],
- conflicts: [resourceConflict],
- });
-
- await sync.push(resourceRequest.resourceGroupId);
- const resourceAfter = await syncStorage.getResourceByDocId(
- requestClient._id,
- resourceRequest.resourceGroupId,
- );
- const requestAfter = await models.request.getById(requestClient._id);
-
- // Assert
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(2);
- // Even when local wins, local resource gets the remove resource version
- expect(resourceAfter.version).toBe(resourceConflict.version);
- // Local resource gets marked as dirty so it's pushed right away
- expect(resourceAfter.dirty).toBe(true);
- // Local db should not be changed since the local won
- expect(requestAfter).toEqual(requestClient);
- });
-
- it('Conflict: local version wins on modified tie', async () => {
- const requestClient = await models.request.getById('req_1');
- const requestServer = Object.assign({}, requestClient, {
- name: 'Server Request',
- });
- const resourceRequest = await syncStorage.getResourceByDocId(requestClient._id);
- const resourceConflict = Object.assign({}, resourceRequest, {
- version: 'ver-2',
- encContent: await sync.encryptDoc(resourceRequest.resourceGroupId, requestServer),
- lastEdited: resourceRequest.lastEdited, // Same edited time
- });
-
- network.syncPush.mockReturnValueOnce({
- updated: [],
- created: [],
- removed: [],
- conflicts: [resourceConflict],
- });
-
- await sync.push(resourceRequest.resourceGroupId);
- const resourceAfter = await syncStorage.getResourceByDocId(
- requestClient._id,
- resourceRequest.resourceGroupId,
- );
- const requestAfter = await models.request.getById(requestClient._id);
-
- // Assert
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(2);
- // Even when local wins, local resource gets the remove resource version
- expect(resourceAfter.version).toBe(resourceConflict.version);
- // Local resource gets marked as dirty so it's pushed right away
- expect(resourceAfter.dirty).toBe(true);
- // Local db should not be changed since the local won
- expect(requestAfter).toEqual(requestClient);
- });
-
- it('Conflict: server version wins if modified after', async () => {
- const requestClient = await models.request.getById('req_1');
- const requestServer = Object.assign({}, requestClient, {
- name: 'Server Request',
- });
- const resourceRequest = await syncStorage.getResourceByDocId(requestClient._id);
- const resourceConflict = Object.assign({}, resourceRequest, {
- version: 'ver-2',
- encContent: await sync.encryptDoc(resourceRequest.resourceGroupId, requestServer),
- lastEdited: resourceRequest.lastEdited + 1000,
- });
-
- network.syncPush.mockReturnValueOnce({
- updated: [],
- created: [],
- removed: [],
- conflicts: [resourceConflict],
- });
-
- await sync.push(resourceRequest.resourceGroupId);
- const resourceAfter = await syncStorage.getResourceByDocId(
- requestClient._id,
- resourceRequest.resourceGroupId,
- );
- const requestAfter = await models.request.getById(requestClient._id);
-
- // Assert
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(2);
- expect(resourceAfter.lastEdited).toBeGreaterThan(resourceRequest.lastEdited);
- expect(resourceAfter.version).toBe(resourceConflict.version);
- // Local resource gets marked as dirty so it's pushed right away
- expect(resourceAfter.dirty).toBe(false);
- expect(requestAfter.name).toBe('Server Request');
- expect(requestAfter.modified).toBe(requestServer.modified);
- });
-});
-
-describe('Integration tests for creating Resources and pushing', () => {
- beforeEach(async () => {
- await globalBeforeEach();
-
- // Reset some things
- await _setSessionData();
- sync._testReset();
-
- // Mock some things
- await _setupSessionMocks();
- jest.useFakeTimers();
-
- // Init storage
- const config = { inMemoryOnly: true, autoload: false, filename: null };
- await syncStorage.initDB(config, true);
-
- // Add some data
- await models.workspace.create({
- _id: 'wrk_empty',
- name: 'Workspace Empty',
- });
- await models.workspace.create({ _id: 'wrk_1', name: 'Workspace 1' });
- await models.request.create({
- _id: 'req_1',
- name: 'Request 1',
- parentId: 'wrk_1',
- });
- await models.request.create({
- _id: 'req_2',
- name: 'Request 2',
- parentId: 'wrk_1',
- });
- await models.request.create({
- _id: 'req_3',
- name: 'Request 3',
- parentId: 'wrk_1',
- });
- await models.environment.create({
- _id: 'env_2',
- name: 'Env Prv',
- parentId: 'wrk_1',
- isPrivate: true,
- });
-
- // Flush changes just to be sure they won't affect our tests
- await db.flushChanges();
- await sync.writePendingChanges();
-
- // Assert that all our new models were created
- expect((await models.workspace.all()).length).toBe(2);
- expect((await models.request.all()).length).toBe(3);
- expect((await models.environment.all()).length).toBe(1);
- expect((await models.cookieJar.all()).length).toBe(0);
-
- // Assert that initializing sync will create the initial resources
- expect((await syncStorage.allConfigs()).length).toBe(0);
- expect((await syncStorage.allResources()).length).toBe(0);
- const promise = sync.init();
- jest.runOnlyPendingTimers();
- await promise;
- expect((await syncStorage.allConfigs()).length).toBe(2);
- expect((await syncStorage.allResources()).length).toBe(5);
-
- // Mark all configs as auto sync
- const configs = await syncStorage.allConfigs();
- for (const config of configs) {
- await syncStorage.updateConfig(config, {
- syncMode: syncStorage.SYNC_MODE_ON,
- });
- }
-
- // Do initial push
- await sync.push();
-
- // Reset mocks once again before tests
- await _setupSessionMocks();
- });
-
- it('Resources created on DB change', async () => {
- // Fetch the workspace and create a new request
- await db.bufferChanges();
- await models.request.create({
- _id: 'req_t',
- url: 'https://google.com',
- parentId: 'wrk_1',
- });
-
- await db.flushChanges();
- await sync.writePendingChanges();
- await sync.push();
-
- // Push changes and get resource
- const resource = await syncStorage.getResourceByDocId('req_t');
-
- // Assert
- expect((await syncStorage.allConfigs()).length).toBe(2);
- expect((await syncStorage.allResources()).length).toBe(6);
- expect(_decryptResource(resource).url).toBe('https://google.com');
- expect(resource.removed).toBe(false);
-
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(6);
-
- expect(network.syncPull.mock.calls).toEqual([]);
- });
-
- it('Resources revived on DB change', async () => {
- // Fetch the workspace and create a new request
- await db.bufferChanges();
- const request = await models.request.create({
- _id: 'req_t',
- name: 'Original Request',
- parentId: 'wrk_1',
- });
- await db.flushChanges();
- await sync.writePendingChanges();
- await sync.push();
-
- // Mark resource as removed
- const originalResource = await syncStorage.getResourceByDocId('req_t');
- const updatedResource = await syncStorage.updateResource(originalResource, {
- removed: true,
- });
-
- // Update it and push it again
- await db.bufferChanges();
- await models.request.update(request, { name: 'New Name' });
- await db.flushChanges();
- await sync.writePendingChanges();
- await sync.push();
- const finalResource = await syncStorage.getResourceByDocId('req_t');
-
- // Assert
- expect(originalResource.removed).toBe(false);
- expect(updatedResource.removed).toBe(true);
- expect(finalResource.removed).toBe(false);
- });
-
- it('Resources update on DB change', async () => {
- // Create, update a request, and fetch it's resource
- const request = await models.request.getById('req_1');
- const resource = await syncStorage.getResourceByDocId(request._id);
- await db.bufferChanges();
- const updatedRequest = await models.request.update(request, {
- name: 'New Name',
- });
-
- // Drain and fetch new resource
- await db.flushChanges();
- await sync.writePendingChanges();
- await sync.push();
- const updatedResource = await syncStorage.getResourceByDocId(request._id);
-
- // Assert
- expect(request.name).toBe('Request 1');
- expect(_decryptResource(resource).name).toBe('Request 1');
- expect(updatedRequest.name).toBe('New Name');
- expect(_decryptResource(updatedResource).name).toBe('New Name');
- expect(resource.removed).toBe(false);
-
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(5);
-
- expect(network.syncPull.mock.calls).toEqual([]);
- });
-
- it('Resources removed on DB change', async () => {
- // Create, update a request, and fetch it's resource
- const request = await models.request.getById('req_1');
- const resource = await syncStorage.getResourceByDocId(request._id);
- await db.bufferChanges();
- await models.request.remove(request);
-
- // Drain and fetch new resource
- await db.flushChanges();
- await sync.writePendingChanges();
- await sync.push();
- const updatedResource = await syncStorage.getResourceByDocId(request._id);
-
- // Assert
- expect(resource.removed).toBe(false);
- expect(updatedResource.removed).toBe(true);
-
- expect(network.syncPush.mock.calls.length).toBe(1);
- expect(network.syncPush.mock.calls[0][0].length).toBe(5);
-
- expect(network.syncPull.mock.calls).toEqual([]);
- });
-});
-
-// ~~~~~~~ //
-// Helpers //
-// ~~~~~~~ //
-
-function _decryptResource(resource) {
- const message = JSON.parse(resource.encContent);
- const fakeKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
- const docJSON = crypt.decryptAES(fakeKey, message);
- return JSON.parse(docJSON);
-}
-
-async function _setSessionData() {
- const symmetricKey = {
- alg: 'A256GCM',
- ext: true,
- k: '3-QU2OcQcpSyFIoL8idgclbImP3M8Y2d0oVAca3Vl4g',
- key_ops: ['encrypt', 'decrypt'],
- kty: 'oct',
- };
-
- const publicKey = {
- alg: 'RSA-OAEP-256',
- e: 'AQAB',
- ext: true,
- key_ops: ['encrypt'],
- kty: 'RSA',
- n: 'aaaa',
- };
-
- const { privateKey } = await crypt.generateKeyPairJWK();
- const encPrivateKey = {
- ad: '',
- d: Buffer.from(JSON.stringify(privateKey)).toString('hex'),
- iv: '968f1d810efdaec58f9e313e',
- t: '0e87a2e57a198ca79cb99585fe9c244a',
- };
-
- // Setup mocks and stuff
- session.setSessionData(
- 'ses_123',
- 'acct_123',
- 'Tammy',
- 'Tester',
- 'greg.schier@konghq.com',
- symmetricKey,
- publicKey,
- encPrivateKey,
- );
-}
-
-async function _setupSessionMocks() {
- const resourceGroups = {};
-
- network.syncCreateResourceGroup = jest.fn((parentId, name, _) => {
- const id = `rg_${Object.keys(resourceGroups).length + 1}`;
-
- // Generate a public key and use a symmetric equal to it's Id for
- // convenience
- const publicKey = session.getPublicKey();
- const symmetricKeyStr = JSON.stringify({ k: id });
- const encSymmetricKey = crypt.encryptRSAWithJWK(publicKey, symmetricKeyStr);
-
- // Store the resource group and return it
- resourceGroups[id] = Object.assign(
- {},
- { id, encSymmetricKey },
- {
- parentResourceId: parentId,
- name: name,
- encSymmetricKey: encSymmetricKey,
- },
- );
- return resourceGroups[id];
- });
-
- network.syncGetResourceGroup = jest.fn(id => {
- if (resourceGroups[id]) {
- return resourceGroups[id];
- }
-
- const err = new Error(`Not Found for ${id}`);
- err.statusCode = 404;
- throw err;
- });
-
- network.syncPull = jest.fn(body => ({
- updatedResources: [],
- createdResources: [],
- idsToPush: [],
- idsToRemove: [],
- }));
-
- network.syncPush = jest.fn(body => ({
- conflicts: [],
- updated: [],
- created: [],
- removed: [],
- }));
-}
diff --git a/packages/insomnia-app/app/sync-legacy/index.js b/packages/insomnia-app/app/sync-legacy/index.js
deleted file mode 100644
index 844be3ce29..0000000000
--- a/packages/insomnia-app/app/sync-legacy/index.js
+++ /dev/null
@@ -1,870 +0,0 @@
-import * as db from '../common/database';
-import * as models from '../models';
-import * as store from './storage';
-import * as misc from '../common/misc';
-import Logger from './logger';
-import * as zlib from 'zlib';
-import {
- syncCreateResourceGroup,
- syncFixDupes,
- syncGetResourceGroup,
- syncPull,
- syncPush,
- syncResetData,
-} from './network';
-import * as crypt from '../account/crypt';
-import * as session from '../account/session';
-
-export const START_DELAY = 1e3;
-export const PULL_PERIOD = 15e3;
-export const WRITE_PERIOD = 1e3;
-
-const WHITE_LIST = {
- [models.workspace.type]: true,
- [models.request.type]: true,
- [models.requestGroup.type]: true,
- [models.environment.type]: true,
- [models.unitTest.type]: true,
- [models.unitTestSuite.type]: true,
-
- // These can be overridden in sync config
- [models.cookieJar.type]: true,
- [models.clientCertificate.type]: true,
-};
-
-export const logger = new Logger();
-
-// TODO: Move this stuff somewhere else
-const NO_VERSION = '__NO_VERSION__';
-const resourceGroupSymmetricKeysCache = {};
-let _pullChangesInterval = null;
-let _writeChangesInterval = null;
-let _pendingDBChanges = {};
-let _isInitialized = false;
-
-// Used to mark whether or not the new sync system is enabled
-let _disabledForSession = false;
-
-export function disableForSession() {
- _disabledForSession = true;
-}
-
-export async function init() {
- if (_disabledForSession) {
- logger.debug('Legacy sync is disabled for current session');
- return;
- }
-
- if (_isInitialized) {
- logger.debug('Already enabled');
- return;
- }
-
- // NOTE: This is at the top to prevent race conditions
- _isInitialized = true;
- db.onChange(async changes => {
- // To help prevent bugs, put Workspaces first
- const sortedChanges = changes.sort(([event, doc, fromSync]) =>
- doc.type === models.workspace.type ? 1 : -1,
- );
-
- for (const [event, doc, fromSync] of sortedChanges) {
- const notOnWhitelist = !WHITE_LIST[doc.type];
- const notLoggedIn = !session.isLoggedIn();
-
- if (doc.isPrivate) {
- logger.debug(`Skip private doc change ${doc._id}`);
- continue;
- }
-
- if (notLoggedIn || notOnWhitelist || fromSync) {
- continue;
- }
-
- const key = `${event}:${doc._id}`;
- _pendingDBChanges[key] = [event, doc, Date.now()];
- }
- });
-
- await misc.delay(START_DELAY);
-
- await push();
- await pull();
-
- let nextSyncTime = 0;
- let isSyncing = false;
- _pullChangesInterval = setInterval(async () => {
- if (isSyncing) {
- return;
- }
-
- if (Date.now() < nextSyncTime) {
- return;
- }
-
- // Mark that we are currently executing a sync op
- isSyncing = true;
-
- const syncStartTime = Date.now();
-
- let extraDelay = 0;
- try {
- await push();
- await pull();
- } catch (err) {
- logger.error('Sync failed with', err);
- extraDelay += PULL_PERIOD;
- }
-
- const totalSyncTime = Date.now() - syncStartTime;
-
- // Add sync duration to give the server some room if it's being slow.
- // Also, multiply it by a random value so everyone doesn't sync up
- extraDelay += totalSyncTime * (Math.random() * 2);
-
- nextSyncTime = Date.now() + PULL_PERIOD + extraDelay;
- isSyncing = false;
- }, PULL_PERIOD / 5);
-
- _writeChangesInterval = setInterval(writePendingChanges, WRITE_PERIOD);
-
- logger.debug('Initialized');
-}
-
-// Used only during tests!
-export function _testReset() {
- _isInitialized = false;
- clearInterval(_pullChangesInterval);
- clearInterval(_writeChangesInterval);
-}
-
-/**
- * Non-blocking function to perform initial sync for an account. This will pull
- * all remote resources (if they exist) before initializing sync.
- */
-export function doInitialSync() {
- process.nextTick(async () => {
- // First, pull down all remote resources, without first creating new ones.
- // This makes sure that the first sync won't create resources locally, when
- // they already exist on the server.
- await pull(null, false);
-
- // Make sure sync is on (start the timers)
- await init();
- });
-}
-
-/**
- * This is a function to clean up Workspaces that might have had more than one
- * ResourceGroup created for them. This function should be called on init (or maybe
- * even periodically) and can be removed once the bug stops persisting.
- */
-export async function fixDuplicateResourceGroups() {
- if (!session.isLoggedIn()) {
- return;
- }
-
- let duplicateCount = 0;
- const workspaces = await models.workspace.all();
- for (const workspace of workspaces) {
- const resources = await store.findResourcesByDocId(workspace._id);
-
- // No duplicates found
- if (resources.length <= 1) {
- continue;
- }
-
- // Fix duplicates
- const ids = resources.map(r => r.resourceGroupId);
- const { deleteResourceGroupIds } = await syncFixDupes(ids);
-
- for (const idToDelete of deleteResourceGroupIds) {
- await store.removeResourceGroup(idToDelete);
- }
-
- duplicateCount++;
- }
-
- if (duplicateCount) {
- logger.debug(`Fixed ${duplicateCount}/${workspaces.length} duplicate synced Workspaces`);
- }
-}
-
-export async function writePendingChanges() {
- // First make a copy and clear pending changes
- const changes = Object.assign({}, _pendingDBChanges);
- _pendingDBChanges = {};
-
- const keys = Object.keys(changes);
-
- if (keys.length === 0) {
- // No changes, just return
- return;
- }
-
- for (const key of Object.keys(changes)) {
- const [event, doc, timestamp] = changes[key];
- await _handleChangeAndPush(event, doc, timestamp);
- }
-}
-
-export async function push(resourceGroupId = null) {
- if (_disabledForSession) {
- logger.debug('Legacy sync is disabled for current session');
- return;
- }
-
- if (!session.isLoggedIn()) {
- return;
- }
-
- let allDirtyResources = [];
- if (resourceGroupId) {
- allDirtyResources = await store.findActiveDirtyResourcesForResourceGroup(resourceGroupId);
- } else {
- allDirtyResources = await store.findActiveDirtyResources();
- }
-
- if (!allDirtyResources.length) {
- // No changes to push
- return;
- }
-
- const dirtyResources = [];
- for (const r of allDirtyResources) {
- // Check if resource type is blacklisted by user
- const config = await store.getConfig(r.resourceGroupId);
-
- if (r.type === models.clientCertificate.type && config.syncDisableClientCertificates) {
- logger.debug(`Skipping pushing blacklisted client certificate ${r.id}`);
- continue;
- }
-
- if (r.type === models.cookieJar.type && config.syncDisableCookieJars) {
- logger.debug(`Skipping pushing blacklisted cookie jar ${r.id}`);
- continue;
- }
-
- dirtyResources.push(r);
- }
-
- let responseBody;
- try {
- responseBody = await syncPush(dirtyResources);
- } catch (e) {
- logger.error('Failed to push changes', e);
- return;
- }
-
- const { updated, created, removed, conflicts } = responseBody;
-
- // Update all resource versions with the ones that were returned
- for (const { id, version } of updated) {
- const resource = await store.getResourceByDocId(id);
- await store.updateResource(resource, { version, dirty: false });
- }
- if (updated.length) {
- logger.debug(`Push updated ${updated.length} resources`);
- }
-
- // Update all resource versions with the ones that were returned
- for (const { id, version } of created) {
- const resource = await store.getResourceByDocId(id);
- await store.updateResource(resource, { version, dirty: false });
- }
- if (created.length) {
- logger.debug(`Push created ${created.length} resources`);
- }
-
- // Update all resource versions with the ones that were returned
- for (const { id, version } of removed) {
- const resource = await store.getResourceByDocId(id);
- await store.updateResource(resource, { version, dirty: false });
- }
- if (removed.length) {
- logger.debug(`Push removed ${removed.length} resources`);
- }
-
- // Resolve conflicts
- await db.bufferChanges();
- for (const serverResource of conflicts) {
- const localResource = await store.getResourceByDocId(
- serverResource.id,
- serverResource.resourceGroupId,
- );
-
- // On conflict, choose last edited one
- const serverIsNewer = serverResource.lastEdited > localResource.lastEdited;
- const winner = serverIsNewer ? serverResource : localResource;
-
- // Update local resource
- // NOTE: using localResource as the base to make sure we have _id
- await store.updateResource(localResource, winner, {
- version: serverResource.version, // Act as the server resource no matter what
- dirty: !serverIsNewer, // It's dirty if we chose the local doc
- });
-
- // Decrypt the docs from the resources. Don't fetch the local doc from the
- // app database, because it might have been deleted.
- const winnerName = serverIsNewer ? 'Server' : 'Local';
- logger.debug(`Resolved conflict for ${serverResource.id} (${winnerName})`);
-
- // If the server won, update ourselves. If we won, we already have the
- // latest version, so do nothing.
- if (serverIsNewer) {
- const doc = await decryptDoc(winner.resourceGroupId, winner.encContent);
- if (winner.removed) {
- await db.remove(doc, true);
- } else {
- await db.update(doc, true);
- }
- }
- }
-
- db.flushChangesAsync();
-}
-
-export async function pull(resourceGroupId = null, createMissingResources = true) {
- if (_disabledForSession) {
- logger.debug('Legacy sync is disabled for current session');
- return;
- }
-
- if (!session.isLoggedIn()) {
- return;
- }
-
- // Try to fix duplicates first. Don't worry if this is called a lot since if there
- // are no duplicates found it doesn't contact the network.
- await fixDuplicateResourceGroups();
-
- let allResources;
- if (createMissingResources) {
- allResources = await getOrCreateAllActiveResources(resourceGroupId);
- } else {
- allResources = await store.allActiveResources(resourceGroupId);
- }
-
- let blacklistedConfigs;
- if (resourceGroupId) {
- // When doing specific sync, blacklist all configs except the one we're trying to sync.
- const allConfigs = await store.allConfigs();
- blacklistedConfigs = allConfigs.filter(c => c.resourceGroupId !== resourceGroupId);
- } else {
- // When doing a full sync, blacklist the inactive configs
- blacklistedConfigs = await store.findInactiveConfigs(resourceGroupId);
- }
-
- const resources = allResources.map(r => ({
- id: r.id,
- resourceGroupId: r.resourceGroupId,
- version: r.version,
- removed: r.removed,
- }));
-
- const blacklistedResourceGroupIds = blacklistedConfigs.map(c => c.resourceGroupId);
-
- const body = {
- resources,
- blacklist: blacklistedResourceGroupIds,
- };
-
- if (resources.length) {
- logger.debug(`Pulling with ${resources.length} resources`);
- }
-
- let responseBody;
- try {
- responseBody = await syncPull(body);
- } catch (e) {
- logger.error('Failed to sync changes', e, body);
- return;
- }
-
- const { updatedResources, createdResources, idsToPush, idsToRemove } = responseBody;
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
- // Insert all the created docs to the DB //
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
-
- await db.bufferChanges();
- for (const serverResource of createdResources) {
- let doc;
-
- try {
- const { resourceGroupId, encContent } = serverResource;
- doc = await decryptDoc(resourceGroupId, encContent);
- } catch (e) {
- logger.warn('Failed to decode created resource', e, serverResource);
- return;
- }
-
- // Check if resource type is blacklisted by user
- const config = await store.getConfig(serverResource.resourceGroupId);
-
- if (
- serverResource.type === models.clientCertificate.type &&
- config.syncDisableClientCertificates
- ) {
- logger.debug(`[sync] Skipping pulling blacklisted client certificate ${serverResource.id}`);
- continue;
- }
-
- if (serverResource.type === models.cookieJar.type && config.syncDisableCookieJars) {
- logger.debug(`[sync] Skipping pulling blacklisted cookie jar ${serverResource.id}`);
- continue;
- }
-
- // Update local Resource
- try {
- await store.insertResource(serverResource, { dirty: false });
- } catch (e) {
- // This probably means we already have it. This should never happen, but
- // might due to a rare race condition.
- logger.error('Failed to insert resource', e, serverResource);
- return;
- }
-
- // NOTE: If the above Resource insert succeeded, that means we have safely
- // insert the document. However, we're using an upsert here instead because
- // it's very possible that the client already had that document locally.
- // This might happen, for example, if the user logs out and back in again.
- const existingDoc = await db.get(doc.type, doc._id);
- if (existingDoc) {
- await db.update(doc, true);
- } else {
- // Mark as not seen if we created a new workspace from sync
- if (doc.type === models.workspace.type) {
- const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(doc._id);
- await models.workspaceMeta.update(workspaceMeta, { hasSeen: false });
- }
- await db.insert(doc, true);
- }
- }
-
- if (createdResources.length) {
- logger.debug(`Pull created ${createdResources.length} resources`);
- }
-
- db.flushChangesAsync();
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
- // Save all the updated docs to the DB //
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
-
- await db.bufferChanges();
- for (const serverResource of updatedResources) {
- try {
- const { resourceGroupId, encContent } = serverResource;
- const doc = await decryptDoc(resourceGroupId, encContent);
-
- // Update app database
- // Needs to be upsert because we could be "undeleting" something
- await db.upsert(doc, true);
-
- // Update local resource
- const resource = await store.getResourceByDocId(
- serverResource.id,
- serverResource.resourceGroupId,
- );
- await store.updateResource(resource, serverResource, { dirty: false });
- } catch (e) {
- logger.warn('Failed to decode updated resource', e, serverResource);
- }
- }
- db.flushChangesAsync();
-
- if (updatedResources.length) {
- logger.debug(`Pull updated ${updatedResources.length} resources`);
- }
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
- // Remove all the docs that need removing //
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
-
- await db.bufferChanges();
- for (const id of idsToRemove) {
- const resource = await store.getResourceByDocId(id);
- if (!resource) {
- throw new Error(`Could not find Resource to remove for ${id}`);
- }
-
- const doc = await decryptDoc(resource.resourceGroupId, resource.encContent);
- if (!doc) {
- throw new Error(`Could not find doc to remove ${id}`);
- }
-
- // Mark resource as deleted
- await store.updateResource(resource, { dirty: false, removed: true });
-
- // Remove from DB
- await db.remove(doc, true);
- }
- db.flushChangesAsync();
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
- // Push all the docs that need pushing //
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
-
- for (const id of idsToPush) {
- const resource = await store.getResourceByDocId(id);
- if (!resource) {
- throw new Error(`Could not find Resource to push for id ${id}`);
- }
-
- // Mark all resources to push as dirty for the next push
- await store.updateResource(resource, { dirty: true });
- }
-
- return updatedResources.length + createdResources.length;
-}
-
-export async function getOrCreateConfig(resourceGroupId) {
- const config = await store.getConfig(resourceGroupId);
-
- if (!config) {
- return store.insertConfig({ resourceGroupId });
- } else {
- return config;
- }
-}
-
-export async function ensureConfigExists(resourceGroupId, syncMode) {
- const config = await store.getConfig(resourceGroupId);
- if (!config) {
- await store.insertConfig({ resourceGroupId, syncMode });
- }
-}
-
-export async function createOrUpdateConfig(resourceGroupId, patch) {
- const config = await store.getConfig(resourceGroupId);
- const finalPatch = { resourceGroupId, ...patch };
-
- if (config) {
- return store.updateConfig(config, finalPatch);
- } else {
- return store.insertConfig(finalPatch);
- }
-}
-
-export async function logout() {
- await session.logout();
- await resetLocalData();
-}
-
-export async function cancelTrial() {
- await session.endTrial();
- await session.logout();
- await resetLocalData();
-}
-
-export async function resetLocalData() {
- for (const c of await store.allConfigs()) {
- await store.removeConfig(c);
- }
-
- for (const r of await store.allResources()) {
- await store.removeResource(r);
- }
-}
-
-export async function resetRemoteData() {
- await syncResetData();
-}
-
-// ~~~~~~~ //
-// HELPERS //
-// ~~~~~~~ //
-
-async function _handleChangeAndPush(event, doc, timestamp) {
- // Update the resource content and set dirty
- // TODO: Remove one of these steps since it does encryption twice
- // in the case where the resource does not exist yet
- const resource = await getOrCreateResourceForDoc(doc);
-
- const updatedResource = await store.updateResource(resource, {
- name: doc.name || 'n/a',
- lastEdited: timestamp,
- lastEditedBy: session.getAccountId(),
- encContent: await encryptDoc(resource.resourceGroupId, doc),
- removed: event === db.CHANGE_REMOVE,
- dirty: true,
- });
-
- // Debounce pushing of dirty resources
- logger.debug(`Queue ${event} ${updatedResource.id}`);
-}
-
-/**
- * Fetch a ResourceGroup. If it has been fetched before, lookup from memory
- *
- * @param resourceGroupId
- * @returns {*}
- */
-const _fetchResourceGroupPromises = {};
-const _resourceGroupCache = {};
-
-export async function fetchResourceGroup(resourceGroupId, invalidateCache = false) {
- if (invalidateCache) {
- delete _resourceGroupCache[resourceGroupId];
- delete _fetchResourceGroupPromises[resourceGroupId];
- }
-
- // PERF: If we're currently fetching, return stored promise
- // TODO: Maybe move parallel fetch caching into the fetch helper
- if (_fetchResourceGroupPromises[resourceGroupId]) {
- return _fetchResourceGroupPromises[resourceGroupId];
- }
-
- const promise = new Promise(async (resolve, reject) => {
- let resourceGroup = _resourceGroupCache[resourceGroupId];
-
- if (!resourceGroup) {
- try {
- resourceGroup = await syncGetResourceGroup(resourceGroupId);
- } catch (e) {
- if (e.statusCode === 404) {
- await store.removeResourceGroup(resourceGroupId);
- logger.debug('ResourceGroup not found. Deleting...');
- reject(new Error('ResourceGroup was not found'));
- return;
- } else {
- logger.error(`Failed to get ResourceGroup ${resourceGroupId}: ${e}`);
- reject(e);
- return;
- }
- }
-
- if (resourceGroup.isDisabled) {
- await store.removeResourceGroup(resourceGroup.id);
- logger.debug('ResourceGroup was disabled. Deleting...');
- reject(new Error('ResourceGroup was disabled'));
- return;
- }
-
- // Also make sure a config exists when we first fetch it.
- // (This may not be needed but we'll do it just in case)
- await ensureConfigExists(resourceGroupId);
- }
-
- // Bust cached promise because we're done with it.
- _fetchResourceGroupPromises[resourceGroupId] = null;
-
- // Cache the ResourceGroup for next time (they never change)
- _resourceGroupCache[resourceGroupId] = resourceGroup;
-
- // Return the ResourceGroup
- resolve(resourceGroup);
- });
-
- // Cache the Promise in case we get asked for the same thing before done
- _fetchResourceGroupPromises[resourceGroupId] = promise;
- return promise;
-}
-
-/**
- * Get a ResourceGroup's symmetric encryption key
- *
- * @param resourceGroupId
- * @private
- */
-async function _getResourceGroupSymmetricKey(resourceGroupId) {
- let key = resourceGroupSymmetricKeysCache[resourceGroupId];
-
- if (!key) {
- const resourceGroup = await fetchResourceGroup(resourceGroupId);
- const accountPrivateKey = await session.getPrivateKey();
-
- const symmetricKeyStr = crypt.decryptRSAWithJWK(
- accountPrivateKey,
- resourceGroup.encSymmetricKey,
- );
-
- key = JSON.parse(symmetricKeyStr);
-
- // Update cache
- resourceGroupSymmetricKeysCache[resourceGroupId] = key;
- }
-
- return key;
-}
-
-export async function encryptDoc(resourceGroupId, doc) {
- try {
- const symmetricKey = await _getResourceGroupSymmetricKey(resourceGroupId);
-
- // TODO: Turn on compression once enough users are on version >= 5.7.0
- // const jsonStr = JSON.stringify(doc);
- // const docStr = zlib.gzipSync(jsonStr);
-
- // Don't use compression for now
- const docStr = JSON.stringify(doc);
-
- const message = crypt.encryptAES(symmetricKey, docStr);
- return JSON.stringify(message);
- } catch (e) {
- logger.error(`Failed to encrypt for ${resourceGroupId}: ${e}`);
- throw e;
- }
-}
-
-export async function decryptDoc(resourceGroupId, messageJSON) {
- let decrypted;
- try {
- const symmetricKey = await _getResourceGroupSymmetricKey(resourceGroupId);
- const message = JSON.parse(messageJSON);
- decrypted = crypt.decryptAES(symmetricKey, message);
- } catch (e) {
- logger.error(`Failed to decrypt from ${resourceGroupId}: ${e}`, messageJSON);
- throw e;
- }
-
- try {
- decrypted = zlib.gunzipSync(decrypted);
- } catch (err) {
- // It's not compressed (legacy), which is okay for now
- }
-
- try {
- return JSON.parse(decrypted);
- } catch (e) {
- logger.error(`Failed to parse after decrypt from ${resourceGroupId}: ${e}`, decrypted);
- throw e;
- }
-}
-
-async function _getWorkspaceForDoc(doc) {
- const ancestors = await db.withAncestors(doc);
- return ancestors.find(d => d.type === models.workspace.type);
-}
-
-export async function createResourceGroup(parentId, name) {
- // Generate symmetric key for ResourceGroup
- const rgSymmetricJWK = await crypt.generateAES256Key();
- const rgSymmetricJWKStr = JSON.stringify(rgSymmetricJWK);
-
- // Encrypt the symmetric key with Account public key
- const publicJWK = session.getPublicKey();
- const encRGSymmetricJWK = crypt.encryptRSAWithJWK(publicJWK, rgSymmetricJWKStr);
-
- // Create the new ResourceGroup
- let resourceGroup;
- try {
- resourceGroup = await syncCreateResourceGroup(parentId, name, encRGSymmetricJWK);
- } catch (e) {
- logger.error(`Failed to create ResourceGroup: ${e}`);
- throw e;
- }
-
- // Create a config for it
- await ensureConfigExists(resourceGroup.id, store.SYNC_MODE_UNSET);
-
- logger.debug(`Created ResourceGroup ${resourceGroup.id}`);
- return resourceGroup;
-}
-
-export async function createResource(doc, resourceGroupId) {
- return store.insertResource({
- id: doc._id,
- name: doc.name || 'n/a', // Set name to the doc name if it has one
- resourceGroupId: resourceGroupId,
- version: NO_VERSION,
- createdBy: session.getAccountId(),
- lastEdited: doc.modified,
- lastEditedBy: session.getAccountId(),
- removed: false,
- type: doc.type,
- encContent: await encryptDoc(resourceGroupId, doc),
- dirty: true,
- });
-}
-
-export async function createResourceForDoc(doc) {
- // No resource yet, so create one
- const workspace = await _getWorkspaceForDoc(doc);
-
- if (!workspace) {
- // Workspace was probably deleted before it's children could be synced.
- // TODO: Handle this case better
- throw new Error(`Could not find workspace for doc ${doc._id}`);
- }
-
- let workspaceResource = await store.getResourceByDocId(workspace._id);
-
- if (!workspaceResource) {
- const workspaceResourceGroup = await createResourceGroup(workspace._id, workspace.name);
- workspaceResource = await createResource(workspace, workspaceResourceGroup.id);
- }
-
- if (workspace === doc) {
- // If the current doc IS a Workspace, just return it
- return workspaceResource;
- } else {
- return createResource(doc, workspaceResource.resourceGroupId);
- }
-}
-
-export async function getOrCreateResourceForDoc(doc) {
- const [resource, ...extras] = await store.findResourcesByDocId(doc._id);
-
- // Sometimes there may be multiple resources created by accident for
- // the same doc. Let's delete the extras here if there are any.
- for (const resource of extras) {
- await store.removeResource(resource);
- }
-
- if (resource) {
- return resource;
- } else {
- return createResourceForDoc(doc);
- }
-}
-
-export async function getOrCreateAllActiveResources(resourceGroupId = null) {
- const startTime = Date.now();
- const activeResourceMap = {};
-
- let activeResources;
- if (resourceGroupId) {
- activeResources = await store.activeResourcesForResourceGroup(resourceGroupId);
- } else {
- activeResources = await store.allActiveResources();
- }
-
- for (const r of activeResources) {
- activeResourceMap[r.id] = r;
- }
-
- // Make sure Workspace is first, because the loop below depends on it
- const modelTypes = Object.keys(WHITE_LIST).sort((a, b) =>
- a.type === models.workspace.type ? 1 : -1,
- );
-
- let created = 0;
- for (const type of modelTypes) {
- for (const doc of await db.all(type)) {
- if (doc.isPrivate) {
- // logger.debug(`Skip private doc ${doc._id}`);
- continue;
- }
-
- const resource = await store.getResourceByDocId(doc._id);
- if (!resource) {
- try {
- activeResourceMap[doc._id] = await createResourceForDoc(doc);
- created++;
- } catch (e) {
- // logger.warn(`Failed to create resource for ${doc._id} ${e}`, {doc});
- }
- }
- }
- }
-
- const resources = Object.keys(activeResourceMap).map(k => activeResourceMap[k]);
-
- const time = (Date.now() - startTime) / 1000;
- if (created > 0) {
- logger.debug(`Created ${created}/${resources.length} Resources (${time.toFixed(2)}s)`);
- }
- return resources;
-}
diff --git a/packages/insomnia-app/app/sync-legacy/logger.js b/packages/insomnia-app/app/sync-legacy/logger.js
deleted file mode 100644
index 5c7bf391eb..0000000000
--- a/packages/insomnia-app/app/sync-legacy/logger.js
+++ /dev/null
@@ -1,37 +0,0 @@
-export default class Logger {
- constructor() {
- this._logs = [];
- }
-
- debug(message, ...args) {
- this._log('debug', message, ...args);
- }
-
- warn(message, ...args) {
- this._log('warn', message, ...args);
- }
-
- error(message, ...args) {
- this._log('error', message, ...args);
- }
-
- tail() {
- return this._logs;
- }
-
- /** @private */
- _log(type, message, ...args) {
- let fn;
- if (type === 'debug') {
- fn = 'log';
- } else if (type === 'warn') {
- fn = 'warn';
- } else {
- fn = 'error';
- }
-
- console[fn](`[sync] ${message}`, ...args);
- const date = new Date();
- this._logs.push({ type, date, message });
- }
-}
diff --git a/packages/insomnia-app/app/sync-legacy/network.js b/packages/insomnia-app/app/sync-legacy/network.js
deleted file mode 100644
index 57bf076b13..0000000000
--- a/packages/insomnia-app/app/sync-legacy/network.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import * as fetch from '../account/fetch';
-import * as session from '../account/session';
-import * as crypt from '../account/crypt';
-
-export async function syncCreateResourceGroup(parentResourceId, name, encSymmetricKey) {
- return fetch.post(
- '/api/resource_groups',
- {
- parentResourceId,
- name,
- encSymmetricKey,
- },
- session.getCurrentSessionId(),
- );
-}
-
-export async function syncGetResourceGroup(id) {
- return fetch.get(`/api/resource_groups/${id}`, session.getCurrentSessionId());
-}
-
-export async function syncPull(body) {
- return fetch.post('/sync/pull', body, session.getCurrentSessionId(), true);
-}
-
-export async function syncPush(body) {
- return fetch.post('/sync/push', body, session.getCurrentSessionId(), true);
-}
-
-export async function syncResetData() {
- return fetch.post('/auth/reset', null, session.getCurrentSessionId());
-}
-
-export async function syncFixDupes(resourceGroupIds) {
- return fetch.post('/sync/fix-dupes', { ids: resourceGroupIds }, session.getCurrentSessionId());
-}
-
-export async function unshareWithAllTeams(resourceGroupId) {
- return fetch.put(
- `/api/resource_groups/${resourceGroupId}/unshare`,
- null,
- session.getCurrentSessionId(),
- );
-}
-
-export async function shareWithTeam(resourceGroupId, teamId) {
- // Ask the server what we need to do to invite the member
- const instructions = await fetch.post(
- `/api/resource_groups/${resourceGroupId}/share-a`,
- {
- teamId,
- },
- session.getCurrentSessionId(),
- );
-
- const privateKeyJWK = session.getPrivateKey();
- const resourceGroupSymmetricKey = crypt.decryptRSAWithJWK(
- privateKeyJWK,
- instructions.encSymmetricKey,
- );
-
- // Build the invite data request
- const newKeys = {};
- for (const accountId of Object.keys(instructions.keys)) {
- const accountPublicKeyJWK = JSON.parse(instructions.keys[accountId]);
- newKeys[accountId] = crypt.encryptRSAWithJWK(accountPublicKeyJWK, resourceGroupSymmetricKey);
- }
-
- // Actually share it with the team
- await fetch.post(
- `/api/resource_groups/${resourceGroupId}/share-b`,
- {
- teamId,
- keys: newKeys,
- },
- session.getCurrentSessionId(),
- );
-}
diff --git a/packages/insomnia-app/app/sync-legacy/storage.js b/packages/insomnia-app/app/sync-legacy/storage.js
deleted file mode 100644
index 85188e1ff1..0000000000
--- a/packages/insomnia-app/app/sync-legacy/storage.js
+++ /dev/null
@@ -1,240 +0,0 @@
-import NeDB from 'nedb';
-import fsPath from 'path';
-import crypto from 'crypto';
-import * as util from '../common/misc';
-import { DB_PERSIST_INTERVAL } from '../common/constants';
-
-const TYPE_RESOURCE = 'Resource';
-const TYPE_CONFIG = 'Config';
-
-export const SYNC_MODE_OFF = 'paused';
-export const SYNC_MODE_ON = 'active';
-export const SYNC_MODE_NEVER = 'never';
-export const SYNC_MODE_UNSET = 'unset';
-let changeListeners = [];
-
-export function onChange(callback) {
- changeListeners.push(callback);
-}
-
-export function offChange(callback) {
- changeListeners = changeListeners.filter(l => l !== callback);
-}
-
-let _changeTimeout = null;
-function _notifyChange() {
- clearTimeout(_changeTimeout);
- _changeTimeout = setTimeout(() => {
- for (const fn of changeListeners) {
- fn();
- }
- }, 200);
-}
-
-export function allActiveResources(resourceGroupId = null) {
- if (resourceGroupId) {
- return findActiveResources({ resourceGroupId });
- } else {
- return findActiveResources({});
- }
-}
-
-export function activeResourcesForResourceGroup(resourceGroupId) {
- return findActiveResources({ resourceGroupId });
-}
-
-export function allResources() {
- return findResources({});
-}
-
-export async function findResources(query = {}) {
- return _execDB(TYPE_RESOURCE, 'find', query);
-}
-
-export async function findActiveResources(query) {
- const configs = await findActiveConfigs();
- const resourceGroupIds = configs.map(c => c.resourceGroupId);
- return findResources(Object.assign({ resourceGroupId: { $in: resourceGroupIds } }, query));
-}
-
-export async function findActiveDirtyResources() {
- return findActiveResources({ dirty: true });
-}
-
-export async function findActiveDirtyResourcesForResourceGroup(resourceGroupId) {
- return findActiveResources({ dirty: true, resourceGroupId });
-}
-
-export async function findDirtyResourcesForResourceGroup(resourceGroupId) {
- return findResources({ dirty: true, resourceGroupId });
-}
-
-export async function findResourcesForResourceGroup(resourceGroupId) {
- return findResources({ resourceGroupId });
-}
-
-export async function getResourceByDocId(id, resourceGroupId = null) {
- let query;
- if (resourceGroupId) {
- query = { id, resourceGroupId };
- } else {
- query = { id };
- }
-
- const rawDocs = await _execDB(TYPE_RESOURCE, 'find', query);
- return rawDocs.length >= 1 ? rawDocs[0] : null;
-}
-
-/**
- * This function is temporary and should only be called when cleaning
- * up duplicate ResourceGroups
- * @param id
- * @returns {*}
- */
-export function findResourcesByDocId(id) {
- return _execDB(TYPE_RESOURCE, 'find', { id });
-}
-
-/**
- * This function is temporary and should only be called when cleaning
- * up duplicate ResourceGroups
- * @param resourceGroupId
- * @returns {*}
- */
-export async function removeResourceGroup(resourceGroupId) {
- await _execDB(TYPE_RESOURCE, 'remove', { resourceGroupId }, { multi: true });
- await _execDB(TYPE_CONFIG, 'remove', { resourceGroupId }, { multi: true });
- _notifyChange();
-}
-
-export async function insertResource(resource) {
- const h = crypto.createHash('md5');
- h.update(resource.resourceGroupId);
- h.update(resource.id);
- const newResource = Object.assign({}, resource, {
- _id: `rs_${h.digest('hex')}`,
- });
- await _execDB(TYPE_RESOURCE, 'insert', newResource);
- _notifyChange();
- return newResource;
-}
-
-export async function updateResource(resource, ...patches) {
- const newDoc = Object.assign({}, resource, ...patches);
- await _execDB(TYPE_RESOURCE, 'update', { _id: resource._id }, newDoc, {
- multi: true,
- });
- _notifyChange();
- return newDoc;
-}
-
-export async function removeResource(resource) {
- await _execDB(TYPE_RESOURCE, 'remove', { _id: resource._id }, { multi: true });
- _notifyChange();
-}
-
-// ~~~~~~ //
-// Config //
-// ~~~~~~ //
-
-export function findConfigs(query) {
- return _execDB(TYPE_CONFIG, 'find', query);
-}
-
-export function allConfigs() {
- return findConfigs({});
-}
-
-export function findInactiveConfigs(excludedResourceGroupId = null) {
- if (excludedResourceGroupId) {
- return findConfigs({
- $not: { syncMode: SYNC_MODE_ON, excludedResourceGroupId },
- });
- } else {
- return findConfigs({ $not: { syncMode: SYNC_MODE_ON } });
- }
-}
-
-export function findActiveConfigs(resourceGroupId = null) {
- if (resourceGroupId) {
- return findConfigs({ syncMode: SYNC_MODE_ON, resourceGroupId });
- } else {
- return findConfigs({ syncMode: SYNC_MODE_ON });
- }
-}
-
-export async function getConfig(resourceGroupId) {
- const rawDocs = await _execDB(TYPE_CONFIG, 'find', { resourceGroupId });
- return rawDocs.length >= 1 ? _initConfig(rawDocs[0]) : null;
-}
-
-export async function updateConfig(config, ...patches) {
- const doc = _initConfig(Object.assign(config, ...patches));
- await _execDB(TYPE_CONFIG, 'update', { _id: doc._id }, doc);
- return doc;
-}
-
-export function removeConfig(config) {
- return _execDB(TYPE_CONFIG, 'remove', { _id: config._id });
-}
-
-export async function insertConfig(config) {
- const doc = _initConfig(config);
- await _execDB(TYPE_CONFIG, 'insert', doc);
- return doc;
-}
-
-function _initConfig(data) {
- return Object.assign(
- {
- _id: util.generateId('scf'),
- syncMode: SYNC_MODE_UNSET,
- resourceGroupId: null,
- },
- data,
- );
-}
-
-export function initDB(config, forceReset) {
- if (!_database || forceReset) {
- const basePath = util.getDataDirectory();
- _database = {};
-
- // NOTE: Do not EVER change this. EVER!
- const resourcePath = fsPath.join(basePath, 'sync/Resource.db');
- const configPath = fsPath.join(basePath, 'sync/Config.db');
-
- // Fill in the defaults
- _database.Resource = new NeDB(
- Object.assign({ filename: resourcePath, autoload: true }, config),
- );
-
- _database.Config = new NeDB(Object.assign({ filename: configPath, autoload: true }, config));
-
- for (const key of Object.keys(_database)) {
- _database[key].persistence.setAutocompactionInterval(DB_PERSIST_INTERVAL);
- }
-
- // Done
- console.log(`[sync] Initialize Sync DB at ${basePath}`);
- }
-}
-
-// ~~~~~~~ //
-// Helpers //
-// ~~~~~~~ //
-
-let _database = null;
-
-function _getDB(type, config = {}) {
- initDB(config);
- return _database[type];
-}
-
-function _execDB(type, fnName, ...args) {
- return new Promise((resolve, reject) => {
- _getDB(type)[fnName](...args, (err, data) => {
- err ? reject(err) : resolve(data);
- });
- });
-}
diff --git a/packages/insomnia-app/app/ui/components/dropdowns/sync-legacy-dropdown.js b/packages/insomnia-app/app/ui/components/dropdowns/sync-legacy-dropdown.js
deleted file mode 100644
index 8ad51b31eb..0000000000
--- a/packages/insomnia-app/app/ui/components/dropdowns/sync-legacy-dropdown.js
+++ /dev/null
@@ -1,234 +0,0 @@
-// @flow
-import * as React from 'react';
-import autobind from 'autobind-decorator';
-import { Dropdown, DropdownButton, DropdownDivider, DropdownItem } from '../base/dropdown';
-import { showModal } from '../modals';
-import * as syncStorage from '../../../sync-legacy/storage';
-import * as sync from '../../../sync-legacy';
-import WorkspaceShareSettingsModal from '../modals/workspace-share-settings-modal';
-import SetupSyncModal from '../modals/setup-sync-modal';
-import type { Workspace } from '../../../models/workspace';
-import * as session from '../../../account/session';
-import { clickLink } from '../../../common/misc';
-
-type Props = {
- workspace: Workspace,
-
- // Optional
- className?: string,
-};
-
-type State = {
- loggedIn: boolean | null,
- loading: boolean,
- resourceGroupId: string | null,
- syncMode: string | null,
- syncPercent: number,
- workspaceName: string,
-};
-
-@autobind
-class SyncLegacyDropdown extends React.PureComponent
- You have not yet configured sync for your {workspace.name} workspace.
-
-
-
-
-