mirror of
https://github.com/frappe/books.git
synced 2024-11-12 16:36:27 +00:00
Merge pull request #464 from 18alantom/fix-dbfile-issues
fix: db file related issues
This commit is contained in:
commit
2351b90f59
@ -1,9 +1,12 @@
|
||||
import { constants } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { DatabaseDemuxBase, DatabaseMethod } from 'utils/db/types';
|
||||
import { getSchemas } from '../../schemas';
|
||||
import { databaseMethodSet } from '../helpers';
|
||||
import {
|
||||
databaseMethodSet,
|
||||
emitMainProcessError,
|
||||
unlinkIfExists,
|
||||
} from '../helpers';
|
||||
import patches from '../patches';
|
||||
import { BespokeQueries } from './bespoke';
|
||||
import DatabaseCore from './core';
|
||||
@ -22,7 +25,7 @@ export class DatabaseManager extends DatabaseDemuxBase {
|
||||
}
|
||||
|
||||
async createNewDatabase(dbPath: string, countryCode: string) {
|
||||
await this.#unlinkIfExists(dbPath);
|
||||
await unlinkIfExists(dbPath);
|
||||
return await this.connectToDatabase(dbPath, countryCode);
|
||||
}
|
||||
|
||||
@ -61,12 +64,35 @@ export class DatabaseManager extends DatabaseDemuxBase {
|
||||
try {
|
||||
await this.#runPatchesAndMigrate();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
await this.db!.close();
|
||||
copyPath && (await fs.copyFile(copyPath, dbPath));
|
||||
throw err;
|
||||
this.#handleFailedMigration(err, dbPath, copyPath);
|
||||
} finally {
|
||||
copyPath && (await fs.unlink(copyPath));
|
||||
await unlinkIfExists(copyPath);
|
||||
}
|
||||
}
|
||||
|
||||
async #handleFailedMigration(
|
||||
error: unknown,
|
||||
dbPath: string,
|
||||
copyPath: string | null
|
||||
) {
|
||||
await this.db!.close();
|
||||
|
||||
if (copyPath) {
|
||||
await this.#restoreDbCopy(dbPath, copyPath);
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
error.message = `failed migration\n${error.message}`;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
async #restoreDbCopy(dbPath: string, copyPath: string) {
|
||||
try {
|
||||
await fs.copyFile(copyPath!, dbPath);
|
||||
} catch (err) {
|
||||
emitMainProcessError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,17 +156,6 @@ export class DatabaseManager extends DatabaseDemuxBase {
|
||||
return await queryFunction(this.db!, ...args);
|
||||
}
|
||||
|
||||
async #unlinkIfExists(dbPath: string) {
|
||||
const exists = await fs
|
||||
.access(dbPath, constants.W_OK)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (exists) {
|
||||
fs.unlink(dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
async #getIsFirstRun(): Promise<boolean> {
|
||||
if (!this.#isInitialized) {
|
||||
return true;
|
||||
@ -160,7 +175,14 @@ export class DatabaseManager extends DatabaseDemuxBase {
|
||||
|
||||
const dir = path.parse(src).dir;
|
||||
const dest = path.join(dir, '__premigratory_temp.db');
|
||||
await fs.copyFile(src, dest);
|
||||
|
||||
try {
|
||||
await fs.copyFile(src, dest);
|
||||
} catch (err) {
|
||||
emitMainProcessError(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { constants } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import { DatabaseMethod } from 'utils/db/types';
|
||||
import { CUSTOM_EVENTS } from 'utils/messages';
|
||||
import { KnexColumnType } from './database/types';
|
||||
|
||||
export const sqliteTypeMap: Record<string, KnexColumnType> = {
|
||||
@ -47,3 +50,32 @@ export const databaseMethodSet: Set<DatabaseMethod> = new Set([
|
||||
'close',
|
||||
'exists',
|
||||
]);
|
||||
|
||||
export function emitMainProcessError(
|
||||
error: unknown,
|
||||
more?: Record<string, unknown>
|
||||
) {
|
||||
(process.emit as Function)(CUSTOM_EVENTS.MAIN_PROCESS_ERROR, error, more);
|
||||
}
|
||||
|
||||
export async function checkFileAccess(filePath: string, mode?: number) {
|
||||
mode ??= constants.W_OK;
|
||||
return await fs
|
||||
.access(filePath, mode)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
||||
export async function unlinkIfExists(filePath: unknown) {
|
||||
if (!filePath || typeof filePath !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const exists = await checkFileAccess(filePath);
|
||||
if (exists) {
|
||||
await fs.unlink(filePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -73,3 +73,17 @@ export async function getErrorHandledReponse(func: () => Promise<unknown>) {
|
||||
export function rendererLog(main: Main, ...args: unknown[]) {
|
||||
main.mainWindow?.webContents.send(IPC_CHANNELS.CONSOLE_LOG, ...args);
|
||||
}
|
||||
|
||||
export function isNetworkError(error: Error) {
|
||||
switch (error?.message) {
|
||||
case 'net::ERR_INTERNET_DISCONNECTED':
|
||||
case 'net::ERR_PROXY_CONNECTION_FAILED':
|
||||
case 'net::ERR_CONNECTION_RESET':
|
||||
case 'net::ERR_CONNECTION_CLOSE':
|
||||
case 'net::ERR_NAME_NOT_RESOLVED':
|
||||
case 'net::ERR_CONNECTION_TIMED_OUT':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,24 @@
|
||||
import { emitMainProcessError } from 'backend/helpers';
|
||||
import { app, dialog } from 'electron';
|
||||
import { autoUpdater, UpdateInfo } from 'electron-updater';
|
||||
import { Main } from '../main';
|
||||
import { IPC_CHANNELS } from '../utils/messages';
|
||||
import { isNetworkError } from './helpers';
|
||||
|
||||
export default function registerAutoUpdaterListeners(main: Main) {
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.allowPrerelease = true;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
|
||||
autoUpdater.on('error', (error) => {
|
||||
if (!main.checkedForUpdate) {
|
||||
main.checkedForUpdate = true;
|
||||
}
|
||||
|
||||
if (isNetworkError(error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
main.mainWindow!.webContents.send(IPC_CHANNELS.MAIN_PROCESS_ERROR, error);
|
||||
dialog.showErrorBox(
|
||||
'Update Error: ',
|
||||
error == null ? 'unknown' : (error.stack || error).toString()
|
||||
);
|
||||
emitMainProcessError(error);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
@ -30,7 +31,7 @@ export default function registerAutoUpdaterListeners(main: Main) {
|
||||
if (!isCurrentBeta && isNextBeta) {
|
||||
const option = await dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: `Update Frappe Books?`,
|
||||
title: 'Update Available',
|
||||
message: `Download version ${nextVersion}?`,
|
||||
buttons: ['Yes', 'No'],
|
||||
});
|
||||
@ -44,4 +45,19 @@ export default function registerAutoUpdaterListeners(main: Main) {
|
||||
|
||||
await autoUpdater.downloadUpdate();
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', async () => {
|
||||
const option = await dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: 'Update Downloaded',
|
||||
message: 'Restart Frappe Books to install update?',
|
||||
buttons: ['Yes', 'No'],
|
||||
});
|
||||
|
||||
if (option.response === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { emitMainProcessError } from 'backend/helpers';
|
||||
import { app, dialog, ipcMain } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import fs from 'fs/promises';
|
||||
@ -11,7 +12,8 @@ import { getLanguageMap } from './getLanguageMap';
|
||||
import {
|
||||
getConfigFilesWithModified,
|
||||
getErrorHandledReponse,
|
||||
setAndGetCleanedConfigFiles,
|
||||
isNetworkError,
|
||||
setAndGetCleanedConfigFiles
|
||||
} from './helpers';
|
||||
import { saveHtmlAsPdf } from './saveHtmlAsPdf';
|
||||
|
||||
@ -52,10 +54,20 @@ export default function registerIpcMainActionListeners(main: Main) {
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.CHECK_FOR_UPDATES, async () => {
|
||||
if (!main.isDevelopment && !main.checkedForUpdate) {
|
||||
await autoUpdater.checkForUpdates();
|
||||
main.checkedForUpdate = true;
|
||||
if (main.isDevelopment || main.checkedForUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await autoUpdater.checkForUpdates();
|
||||
} catch (error) {
|
||||
if (isNetworkError(error as Error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitMainProcessError(error);
|
||||
}
|
||||
main.checkedForUpdate = true;
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (event, code) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { app } from 'electron';
|
||||
import { IPC_CHANNELS } from 'utils/messages';
|
||||
import { CUSTOM_EVENTS, IPC_CHANNELS } from 'utils/messages';
|
||||
import { Main } from '../main';
|
||||
|
||||
export default function registerProcessListeners(main: Main) {
|
||||
@ -17,12 +17,26 @@ export default function registerProcessListeners(main: Main) {
|
||||
}
|
||||
}
|
||||
|
||||
process.on(CUSTOM_EVENTS.MAIN_PROCESS_ERROR, (error, more) => {
|
||||
main.mainWindow!.webContents.send(
|
||||
IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR,
|
||||
error,
|
||||
more
|
||||
);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (error) => {
|
||||
main.mainWindow!.webContents.send(IPC_CHANNELS.MAIN_PROCESS_ERROR, error);
|
||||
main.mainWindow!.webContents.send(
|
||||
IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR,
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
main.mainWindow!.webContents.send(IPC_CHANNELS.MAIN_PROCESS_ERROR, error);
|
||||
main.mainWindow!.webContents.send(
|
||||
IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR,
|
||||
error
|
||||
);
|
||||
setTimeout(() => process.exit(1), 10000);
|
||||
});
|
||||
}
|
||||
|
@ -57,7 +57,11 @@ export function getErrorLogObject(
|
||||
error: Error,
|
||||
more: Record<string, unknown>
|
||||
): ErrorLog {
|
||||
const { name, stack, message } = error;
|
||||
const { name, stack, message, cause } = error;
|
||||
if (cause) {
|
||||
more.cause = cause;
|
||||
}
|
||||
|
||||
const errorLogObj = { name, stack, message, more };
|
||||
|
||||
fyo.errorLog.push(errorLogObj);
|
||||
|
@ -4,13 +4,16 @@ import { fyo } from 'src/initFyo';
|
||||
import { IPC_CHANNELS } from 'utils/messages';
|
||||
|
||||
export default function registerIpcRendererListeners() {
|
||||
ipcRenderer.on(IPC_CHANNELS.MAIN_PROCESS_ERROR, async (_, error) => {
|
||||
if (fyo.store.isDevelopment) {
|
||||
console.error(error);
|
||||
}
|
||||
ipcRenderer.on(
|
||||
IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR,
|
||||
async (_, error, more) => {
|
||||
if (fyo.store.isDevelopment) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
await handleError(true, error as Error);
|
||||
});
|
||||
await handleError(true, error as Error, more);
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(IPC_CHANNELS.CONSOLE_LOG, (_, ...stuff: unknown[]) => {
|
||||
if (!fyo.store.isDevelopment) {
|
||||
|
@ -33,7 +33,7 @@ export enum IPC_ACTIONS {
|
||||
|
||||
// ipcMain.send(...)
|
||||
export enum IPC_CHANNELS {
|
||||
MAIN_PROCESS_ERROR = 'main-process-error',
|
||||
LOG_MAIN_PROCESS_ERROR = 'main-process-error',
|
||||
CONSOLE_LOG = 'console-log',
|
||||
}
|
||||
|
||||
@ -42,3 +42,8 @@ export enum DB_CONN_FAILURE {
|
||||
CANT_OPEN = 'cant-open',
|
||||
CANT_CONNECT = 'cant-connect',
|
||||
}
|
||||
|
||||
// events
|
||||
export enum CUSTOM_EVENTS {
|
||||
MAIN_PROCESS_ERROR = 'main-process-error',
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user