mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 13:16:16 +02:00
feat(core): unified crash system using platform provider
This commit is contained in:
11
apps/client-standalone/src/lightweight/platform_provider.ts
Normal file
11
apps/client-standalone/src/lightweight/platform_provider.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { PlatformProvider } from "@triliumnext/core";
|
||||
|
||||
export default class StandalonePlatformProvider implements PlatformProvider {
|
||||
crash(message: string): void {
|
||||
console.error("[Standalone] FATAL:", message);
|
||||
self.postMessage({
|
||||
type: "FATAL_ERROR",
|
||||
message
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@ import LocalServerWorker from "./local-server-worker?worker";
|
||||
let localWorker: Worker | null = null;
|
||||
const pending = new Map();
|
||||
|
||||
function showFatalErrorDialog(message: string) {
|
||||
alert(message);
|
||||
}
|
||||
|
||||
export function startLocalServerWorker() {
|
||||
if (localWorker) return localWorker;
|
||||
localWorker = new LocalServerWorker();
|
||||
@@ -19,6 +23,13 @@ export function startLocalServerWorker() {
|
||||
localWorker.onmessage = (event) => {
|
||||
const msg = event.data;
|
||||
|
||||
// Handle fatal platform crashes (shown as a dialog to the user)
|
||||
if (msg?.type === "FATAL_ERROR") {
|
||||
console.error("[LocalBridge] Fatal error:", msg.message);
|
||||
showFatalErrorDialog(msg.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle worker error reports
|
||||
if (msg?.type === "WORKER_ERROR") {
|
||||
console.error("[LocalBridge] Worker reported error:", msg.error);
|
||||
|
||||
@@ -56,6 +56,7 @@ let WorkerMessagingProvider: typeof import('./lightweight/messaging_provider').d
|
||||
let BrowserExecutionContext: typeof import('./lightweight/cls_provider').default;
|
||||
let BrowserCryptoProvider: typeof import('./lightweight/crypto_provider').default;
|
||||
let FetchRequestProvider: typeof import('./lightweight/request_provider').default;
|
||||
let StandalonePlatformProvider: typeof import('./lightweight/platform_provider').default;
|
||||
let translationProvider: typeof import('./lightweight/translation_provider').default;
|
||||
let createConfiguredRouter: typeof import('./lightweight/browser_routes').createConfiguredRouter;
|
||||
|
||||
@@ -81,6 +82,7 @@ async function loadModules(): Promise<void> {
|
||||
clsModule,
|
||||
cryptoModule,
|
||||
requestModule,
|
||||
platformModule,
|
||||
translationModule,
|
||||
routesModule
|
||||
] = await Promise.all([
|
||||
@@ -89,6 +91,7 @@ async function loadModules(): Promise<void> {
|
||||
import('./lightweight/cls_provider.js'),
|
||||
import('./lightweight/crypto_provider.js'),
|
||||
import('./lightweight/request_provider.js'),
|
||||
import('./lightweight/platform_provider.js'),
|
||||
import('./lightweight/translation_provider.js'),
|
||||
import('./lightweight/browser_routes.js')
|
||||
]);
|
||||
@@ -98,6 +101,7 @@ async function loadModules(): Promise<void> {
|
||||
BrowserExecutionContext = clsModule.default;
|
||||
BrowserCryptoProvider = cryptoModule.default;
|
||||
FetchRequestProvider = requestModule.default;
|
||||
StandalonePlatformProvider = platformModule.default;
|
||||
translationProvider = translationModule.default;
|
||||
createConfiguredRouter = routesModule.createConfiguredRouter;
|
||||
|
||||
@@ -149,6 +153,7 @@ async function initialize(): Promise<void> {
|
||||
crypto: new BrowserCryptoProvider(),
|
||||
messaging: messagingProvider!,
|
||||
request: new FetchRequestProvider(),
|
||||
platform: new StandalonePlatformProvider(),
|
||||
translations: translationProvider,
|
||||
schema: schemaModule.default,
|
||||
dbConfig: {
|
||||
|
||||
@@ -18,6 +18,7 @@ import path, { join } from "path";
|
||||
|
||||
import { deferred, LOCALES } from "../../../packages/commons/src";
|
||||
import { PRODUCT_NAME } from "./app-info";
|
||||
import DesktopPlatformProvider from "./platform_provider";
|
||||
|
||||
async function main() {
|
||||
const userDataPath = getUserData();
|
||||
@@ -136,6 +137,7 @@ async function main() {
|
||||
executionContext: new ClsHookedExecutionContext(),
|
||||
messaging: new WebSocketMessagingProvider(),
|
||||
schema: fs.readFileSync(require.resolve("@triliumnext/core/src/assets/schema.sql"), "utf-8"),
|
||||
platform: new DesktopPlatformProvider(),
|
||||
translations: (await import("@triliumnext/server/src/services/i18n.js")).initializeTranslations,
|
||||
extraAppInfo: {
|
||||
nodeVersion: process.version,
|
||||
|
||||
9
apps/desktop/src/platform_provider.ts
Normal file
9
apps/desktop/src/platform_provider.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { PlatformProvider, t } from "@triliumnext/core";
|
||||
import electron from "electron";
|
||||
|
||||
export default class DesktopPlatformProvider implements PlatformProvider {
|
||||
crash(message: string): void {
|
||||
electron.dialog.showErrorBox(t("modals.error_title"), message);
|
||||
electron.app.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import path from "path";
|
||||
|
||||
import ClsHookedExecutionContext from "./cls_provider.js";
|
||||
import NodejsCryptoProvider from "./crypto_provider.js";
|
||||
import ServerPlatformProvider from "./platform_provider.js";
|
||||
import dataDirs from "./services/data_dir.js";
|
||||
import port from "./services/port.js";
|
||||
import NodeRequestProvider from "./services/request.js";
|
||||
@@ -54,6 +55,7 @@ async function startApplication() {
|
||||
executionContext: new ClsHookedExecutionContext(),
|
||||
messaging: new WebSocketMessagingProvider(),
|
||||
schema: fs.readFileSync(require.resolve("@triliumnext/core/src/assets/schema.sql"), "utf-8"),
|
||||
platform: new ServerPlatformProvider(),
|
||||
translations: (await import("./services/i18n.js")).initializeTranslations,
|
||||
extraAppInfo: {
|
||||
nodeVersion: process.version,
|
||||
|
||||
8
apps/server/src/platform_provider.ts
Normal file
8
apps/server/src/platform_provider.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { getLog, PlatformProvider } from "@triliumnext/core";
|
||||
|
||||
export default class ServerPlatformProvider implements PlatformProvider {
|
||||
crash(message: string): void {
|
||||
getLog().error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -99,17 +99,6 @@ export function stripTags(text: string) {
|
||||
return text.replace(/<(?:.|\n)*?>/gm, "");
|
||||
}
|
||||
|
||||
export async function crash(message: string) {
|
||||
if (isElectron) {
|
||||
const electron = await import("electron");
|
||||
electron.dialog.showErrorBox(t("modals.error_title"), message);
|
||||
electron.app.exit(1);
|
||||
} else {
|
||||
log.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export function getContentDisposition(filename: string) {
|
||||
return coreUtils.getContentDisposition(filename);
|
||||
@@ -405,7 +394,6 @@ export function waitForStreamToFinish(stream: any): Promise<void> {
|
||||
export default {
|
||||
compareVersions,
|
||||
constantTimeCompare,
|
||||
crash,
|
||||
envToBoolean,
|
||||
escapeHtml,
|
||||
escapeRegExp,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { initRequest, RequestProvider } from "./services/request";
|
||||
import { initTranslations, TranslationProvider } from "./services/i18n";
|
||||
import { initSchema } from "./services/sql_init";
|
||||
import appInfo from "./services/app_info";
|
||||
import PlatformProvider, { initPlatform } from "./services/platform";
|
||||
|
||||
export { getLog } from "./services/log";
|
||||
export type * from "./services/sql/types";
|
||||
@@ -90,13 +91,16 @@ export { default as consistency_checks } from "./services/consistency_checks";
|
||||
export { default as content_hash } from "./services/content_hash";
|
||||
export { default as sync_mutex } from "./services/sync_mutex";
|
||||
export { default as setup } from "./services/setup";
|
||||
export { getPlatform, type PlatformProvider } from "./services/platform";
|
||||
export { t } from "i18next";
|
||||
export type { RequestProvider, ExecOpts, CookieJar } from "./services/request";
|
||||
|
||||
export async function initializeCore({ dbConfig, executionContext, crypto, translations, messaging, request, schema, extraAppInfo }: {
|
||||
export async function initializeCore({ dbConfig, executionContext, crypto, translations, messaging, request, schema, extraAppInfo, platform }: {
|
||||
dbConfig: SqlServiceParams,
|
||||
executionContext: ExecutionContext,
|
||||
crypto: CryptoProvider,
|
||||
translations: TranslationProvider,
|
||||
platform: PlatformProvider,
|
||||
schema: string,
|
||||
messaging?: MessagingProvider,
|
||||
request?: RequestProvider,
|
||||
@@ -105,6 +109,7 @@ export async function initializeCore({ dbConfig, executionContext, crypto, trans
|
||||
dataDirectory: string;
|
||||
};
|
||||
}) {
|
||||
initPlatform(platform);
|
||||
initLog();
|
||||
await initTranslations(translations);
|
||||
initCrypto(crypto);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import backupService from "./backup.js";
|
||||
import { getSql } from "./sql/index.js";
|
||||
import { getLog } from "./log.js";
|
||||
import { crash } from "./utils/index.js";
|
||||
import { getPlatform } from "./platform.js";
|
||||
import appInfo from "./app_info.js";
|
||||
import * as cls from "./context.js";
|
||||
import { t } from "i18next";
|
||||
@@ -20,8 +20,7 @@ async function migrate() {
|
||||
const currentDbVersion = getDbVersion();
|
||||
|
||||
if (currentDbVersion < 214) {
|
||||
await crash(t("migration.old_version"));
|
||||
return;
|
||||
getPlatform().crash(t("migration.old_version"));
|
||||
}
|
||||
|
||||
// backup before attempting migration
|
||||
@@ -59,8 +58,7 @@ async function migrate() {
|
||||
log.info(`Migration to version ${mig.dbVersion} has been successful.`);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
crash(t("migration.error_message", { version: mig.dbVersion, stack: e.stack }));
|
||||
break; // crash() is sometimes async
|
||||
getPlatform().crash(t("migration.error_message", { version: mig.dbVersion, stack: e.stack }));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -126,7 +124,7 @@ async function migrateIfNecessary() {
|
||||
const currentDbVersion = getDbVersion();
|
||||
|
||||
if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== "true") {
|
||||
await crash(t("migration.wrong_db_version", { version: currentDbVersion, targetVersion: appInfo.dbVersion }));
|
||||
getPlatform().crash(t("migration.wrong_db_version", { version: currentDbVersion, targetVersion: appInfo.dbVersion }));
|
||||
}
|
||||
|
||||
if (!isDbUpToDate()) {
|
||||
|
||||
17
packages/trilium-core/src/services/platform.ts
Normal file
17
packages/trilium-core/src/services/platform.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Interface for platform-specific services. This is used to abstract away platform-specific implementations, such as crash reporting, from the core logic of the application.
|
||||
*/
|
||||
export interface PlatformProvider {
|
||||
crash(message: string): void;
|
||||
}
|
||||
|
||||
let platformProvider: PlatformProvider | null = null;
|
||||
|
||||
export function initPlatform(provider: PlatformProvider) {
|
||||
platformProvider = provider;
|
||||
}
|
||||
|
||||
export function getPlatform(): PlatformProvider {
|
||||
if (!platformProvider) throw new Error("Platform provider not initialized");
|
||||
return platformProvider;
|
||||
}
|
||||
@@ -202,7 +202,3 @@ export function isEmptyOrWhitespace(str: string | null | undefined) {
|
||||
export function escapeRegExp(str: string) {
|
||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||
}
|
||||
|
||||
export async function crash(message: string): never {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user