mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
backup second pass (#6388)
* some notes on approach * some broken fs logic * some more broke stuff * fix backup logic * rename export to backup * wire up a restore * restore check * only update if newer version * extract to function * rename export to backup
This commit is contained in:
parent
5407006663
commit
7e3e44c50a
@ -1,3 +1,4 @@
|
||||
|
||||
import electron, { app, ipcMain, session } from 'electron';
|
||||
import { BrowserWindow } from 'electron';
|
||||
import contextMenu from 'electron-context-menu';
|
||||
@ -8,6 +9,7 @@ import { userDataFolder } from '../config/config.json';
|
||||
import { changelogUrl, getAppVersion, isDevelopment, isMac } from './common/constants';
|
||||
import { database } from './common/database';
|
||||
import log, { initializeLogging } from './common/log';
|
||||
import { backupIfNewerVersionAvailable } from './main/backup';
|
||||
import { registerElectronHandlers } from './main/ipc/electron';
|
||||
import { registergRPCHandlers } from './main/ipc/grpc';
|
||||
import { registerMainHandlers } from './main/ipc/main';
|
||||
@ -228,7 +230,8 @@ async function _trackStats() {
|
||||
launches: oldStats.launches + 1,
|
||||
});
|
||||
|
||||
ipcMain.once('halfSecondAfterAppStart', () => {
|
||||
ipcMain.once('halfSecondAfterAppStart', async () => {
|
||||
backupIfNewerVersionAvailable();
|
||||
const { currentVersion, launches, lastVersion } = stats;
|
||||
|
||||
const firstLaunch = launches === 1;
|
||||
@ -244,7 +247,7 @@ async function _trackStats() {
|
||||
message: `Updated to ${currentVersion}`,
|
||||
};
|
||||
// Wait a bit before showing the user because the app just launched.
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
// @ts-expect-error -- TSCONVERSION likely needs to be window.webContents.send instead
|
||||
window.send('show-notification', notification);
|
||||
|
75
packages/insomnia/src/main/backup.ts
Normal file
75
packages/insomnia/src/main/backup.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { copyFile, mkdir, readdir } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import electron from 'electron';
|
||||
|
||||
import appConfig from '../../config/config.json';
|
||||
import { version } from '../../package.json';
|
||||
import * as models from '../models';
|
||||
import { insomniaFetch } from './insomniaFetch';
|
||||
|
||||
export async function backupIfNewerVersionAvailable() {
|
||||
try {
|
||||
const settings = await models.settings.getOrCreate();
|
||||
console.log('[main] Checking for newer version than ', version);
|
||||
const response = await insomniaFetch<{ url: string }>({
|
||||
method: 'GET',
|
||||
origin: 'https://updates.insomnia.rest',
|
||||
path: `/builds/check/mac?v=${version}&app=${appConfig.appId}&channel=${settings.updateChannel}`,
|
||||
sessionId: null,
|
||||
});
|
||||
if (response) {
|
||||
console.log('[main] Found newer version', response);
|
||||
backup();
|
||||
return;
|
||||
}
|
||||
console.log('[main] No newer version');
|
||||
} catch (err) {
|
||||
console.log('[main] Error checking for newer version', err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function backup() {
|
||||
try {
|
||||
const dataPath = process.env['INSOMNIA_DATA_PATH'] || electron.app.getPath('userData');
|
||||
const versionPath = path.join(dataPath, 'backups', version);
|
||||
await mkdir(versionPath, { recursive: true });
|
||||
// skip if backup already exists at version path
|
||||
const backupFiles = await readdir(versionPath);
|
||||
if (backupFiles.length) {
|
||||
console.log('Backup found at:', versionPath);
|
||||
return;
|
||||
}
|
||||
const files = await readdir(dataPath);
|
||||
files.forEach(async (file: string) => {
|
||||
if (file.endsWith('.db')) {
|
||||
await copyFile(path.join(dataPath, file), path.join(versionPath, file));
|
||||
}
|
||||
});
|
||||
console.log('Exported backup to:', versionPath);
|
||||
} catch (err) {
|
||||
console.log('Error exporting backup:', err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function restoreBackup(version: string) {
|
||||
try {
|
||||
const dataPath = process.env['INSOMNIA_DATA_PATH'] || electron.app.getPath('userData');
|
||||
const versionPath = path.join(dataPath, 'backups', version);
|
||||
const files = await readdir(versionPath);
|
||||
if (!files.length) {
|
||||
console.log('No backup found at:', versionPath);
|
||||
return;
|
||||
}
|
||||
files.forEach(async (file: string) => {
|
||||
if (file.endsWith('.db')) {
|
||||
await copyFile(path.join(versionPath, file), path.join(dataPath, file));
|
||||
}
|
||||
});
|
||||
console.log('Restored backup from:', versionPath);
|
||||
} catch (err) {
|
||||
console.log('Error restoring backup:', err);
|
||||
}
|
||||
electron.app.relaunch();
|
||||
electron.app.exit();
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import electron from 'electron';
|
||||
|
||||
import { version } from '../../package.json';
|
||||
import { database as db } from '../common/database';
|
||||
import { exportRequestsData } from '../common/export';
|
||||
import * as models from '../models';
|
||||
import { isGrpcRequest } from '../models/grpc-request';
|
||||
import { isRequest } from '../models/request';
|
||||
import { isWebSocketRequest } from '../models/websocket-request';
|
||||
export async function exportAllWorkspaces() {
|
||||
try {
|
||||
const projects = await models.project.all();
|
||||
await Promise.all(projects.map(async project => {
|
||||
const dataPath = process.env['INSOMNIA_DATA_PATH'] || electron.app.getPath('userData');
|
||||
const versionPath = path.join(dataPath, 'backups', version);
|
||||
await mkdir(versionPath, { recursive: true });
|
||||
const fileName = path.join(versionPath, `${project.name}.insomnia.json`);
|
||||
const workspaces = await models.workspace.findByParentId(project._id);
|
||||
const docs = await Promise.all(workspaces.map(parentDoc => db.withDescendants(parentDoc)));
|
||||
const requests = docs.flat().filter(doc => isRequest(doc) || isGrpcRequest(doc) || isWebSocketRequest(doc));
|
||||
const stringifiedExport = await exportRequestsData(requests, true, 'json');
|
||||
await writeFile(fileName, stringifiedExport);
|
||||
console.log('Exported project backup to:', fileName);
|
||||
}
|
||||
));
|
||||
} catch (err) {
|
||||
console.log('Error exporting project backup:', err);
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import fs from 'fs';
|
||||
|
||||
import { SegmentEvent, trackPageView, trackSegmentEvent } from '../analytics';
|
||||
import { authorizeUserInWindow } from '../authorizeUserInWindow';
|
||||
import { exportAllWorkspaces } from '../export';
|
||||
import { backup, restoreBackup } from '../backup';
|
||||
import { insomniaFetch } from '../insomniaFetch';
|
||||
import installPlugin from '../install-plugin';
|
||||
import { axiosRequest } from '../network/axios-request';
|
||||
@ -24,7 +24,8 @@ export interface MainBridgeAPI {
|
||||
restart: () => void;
|
||||
halfSecondAfterAppStart: () => void;
|
||||
manualUpdateCheck: () => void;
|
||||
exportAllWorkspaces: () => Promise<void>;
|
||||
backup: () => Promise<void>;
|
||||
restoreBackup: (version: string) => Promise<void>;
|
||||
spectralRun: (options: { contents: string; rulesetPath: string }) => Promise<ISpectralDiagnostic[]>;
|
||||
authorizeUserInWindow: typeof authorizeUserInWindow;
|
||||
setMenuBarVisibility: (visible: boolean) => void;
|
||||
@ -54,8 +55,11 @@ export function registerMainHandlers() {
|
||||
w.webContents.send('loggedIn');
|
||||
});
|
||||
});
|
||||
ipcMain.handle('exportAllWorkspaces', async () => {
|
||||
return exportAllWorkspaces();
|
||||
ipcMain.handle('backup', async () => {
|
||||
return backup();
|
||||
});
|
||||
ipcMain.handle('restoreBackup', async (_, options: string) => {
|
||||
return restoreBackup(options);
|
||||
});
|
||||
ipcMain.handle('authorizeUserInWindow', (_, options: Parameters<typeof authorizeUserInWindow>[0]) => {
|
||||
const { url, urlSuccessRegex, urlFailureRegex, sessionId } = options;
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
import { delay } from '../common/misc';
|
||||
import * as models from '../models/index';
|
||||
import { invariant } from '../utils/invariant';
|
||||
import { exportAllWorkspaces } from './export';
|
||||
const isUpdateSupported = () => {
|
||||
if (process.platform === 'linux') {
|
||||
console.log('[updater] Not supported on this platform', process.platform);
|
||||
@ -62,7 +61,6 @@ export const init = async () => {
|
||||
autoUpdater.on('update-downloaded', async (_error, releaseNotes, releaseName) => {
|
||||
console.log(`[updater] Downloaded ${releaseName}`);
|
||||
_sendUpdateStatus('Performing backup...');
|
||||
await exportAllWorkspaces();
|
||||
_sendUpdateStatus('Updated (Restart Required)');
|
||||
|
||||
dialog.showMessageBox({
|
||||
@ -79,6 +77,7 @@ export const init = async () => {
|
||||
});
|
||||
|
||||
if (isUpdateSupported()) {
|
||||
// perhaps disable this method of upgrading just incase it trigger before backup is complete
|
||||
// on app start
|
||||
const settings = await models.settings.getOrCreate();
|
||||
const updateUrl = getUpdateUrl(settings.updateChannel);
|
||||
|
@ -43,7 +43,8 @@ const main: Window['main'] = {
|
||||
openInBrowser: options => ipcRenderer.send('openInBrowser', options),
|
||||
halfSecondAfterAppStart: () => ipcRenderer.send('halfSecondAfterAppStart'),
|
||||
manualUpdateCheck: () => ipcRenderer.send('manualUpdateCheck'),
|
||||
exportAllWorkspaces: () => ipcRenderer.invoke('exportAllWorkspaces'),
|
||||
backup: () => ipcRenderer.invoke('backup'),
|
||||
restoreBackup: options => ipcRenderer.invoke('restoreBackup', options),
|
||||
authorizeUserInWindow: options => ipcRenderer.invoke('authorizeUserInWindow', options),
|
||||
spectralRun: options => ipcRenderer.invoke('spectralRun', options),
|
||||
setMenuBarVisibility: options => ipcRenderer.send('setMenuBarVisibility', options),
|
||||
|
@ -45,7 +45,9 @@ export const ImportExport: FC<Props> = ({ hideSettingsModal }) => {
|
||||
exportAllToFile(projectName, workspacesForActiveProject);
|
||||
hideSettingsModal();
|
||||
};
|
||||
|
||||
// here we should list all the folders which contain insomnia.*.db files
|
||||
// and have some big red button to overwrite the current data with the backup
|
||||
// and once complete trigger an app restart?
|
||||
return (
|
||||
<Fragment>
|
||||
<div data-testid="import-export-tab">
|
||||
|
Loading…
Reference in New Issue
Block a user