From 3f5cdc533ec3fa309f72c56c936de65042fbe73a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 24 Mar 2026 10:18:23 +0200 Subject: [PATCH] feat(standalone/setup): sync from server without refresh --- apps/client/src/setup.tsx | 31 ++++++++++++++--- apps/server/src/services/sql_init.ts | 27 +-------------- .../trilium-core/src/services/sql_init.ts | 33 ++++++++++++++++--- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/apps/client/src/setup.tsx b/apps/client/src/setup.tsx index 2c784a8022..6bb94a349b 100644 --- a/apps/client/src/setup.tsx +++ b/apps/client/src/setup.tsx @@ -1,7 +1,7 @@ import "./setup.css"; import { ComponentChildren, render } from "preact"; -import { useState } from "preact/hooks"; +import { useEffect, useState } from "preact/hooks"; import { initLocale, t } from "./services/i18n"; import server from "./services/server"; @@ -22,7 +22,7 @@ async function main() { type State = "firstOptions" | "syncFromDesktop" | "syncFromServer" | "syncInProgress" | "syncFailed"; function App() { - const [ state, setState ] = useState("syncInProgress"); + const [ state, setState ] = useState("syncFromServer"); return (
@@ -70,15 +70,37 @@ function SetupOptions({ setState }: { setState: (state: State) => void }) { } function SyncInProgress() { + const { outstandingPullCount, initialized } = useOutstandingSyncInfo(); + return (

{t("setup.sync-in-progress-title")}

{t("setup.sync-in-progress-description")}

+

Outstanding sync objects: {outstandingPullCount}

); } +function useOutstandingSyncInfo() { + const [ outstandingPullCount, setOutstandingPullCount ] = useState(0); + const [ initialized, setInitialized ] = useState(false); + + async function refresh() { + const { outstandingPullCount, initialized } = await server.get("sync/stats"); + setOutstandingPullCount(outstandingPullCount); + setInitialized(initialized); + } + + useEffect(() => { + const interval = setInterval(refresh, 1000); + refresh(); + + return () => clearInterval(interval); + }, []); + return { outstandingPullCount, initialized }; +} + function Spinner() { return (
@@ -93,11 +115,12 @@ function SyncFromServer({ setState }: { setState: (state: State) => void }) { const [serverUrl, setServerUrl] = useState(""); const [password, setPassword] = useState(""); - function handleFinishSetup() { - server.post("setup/sync-from-server", { + async function handleFinishSetup() { + await server.post("setup/sync-from-server", { syncServerHost: serverUrl, password }); + setState("syncInProgress"); } return ( diff --git a/apps/server/src/services/sql_init.ts b/apps/server/src/services/sql_init.ts index 5419713d31..263f4c7488 100644 --- a/apps/server/src/services/sql_init.ts +++ b/apps/server/src/services/sql_init.ts @@ -14,32 +14,7 @@ const setDbAsInitialized = coreSqlInit.setDbAsInitialized; const createInitialDatabase = coreSqlInit.createInitialDatabase; const initializeDb = coreSqlInit.initializeDb; export const getDbSize = coreSqlInit.getDbSize; - -async function createDatabaseForSync(options: OptionRow[], syncServerHost = "", syncProxy = "") { - log.info("Creating database for sync"); - - if (isDbInitialized()) { - throw new Error("DB is already initialized"); - } - - const schema = fs.readFileSync(`${resourceDir.DB_INIT_DIR}/schema.sql`, "utf8"); - - // We have to import async since options init requires keyboard actions which require translations. - const optionsInitService = (await import("./options_init.js")).default; - - sql.transactional(() => { - sql.executeScript(schema); - - optionsInitService.initNotSyncedOptions(false, { syncServerHost, syncProxy }); - - // document options required for sync to kick off - for (const opt of options) { - new BOption(opt).save(); - } - }); - - log.info("Schema and not synced options generated."); -} +const createDatabaseForSync = coreSqlInit.createDatabaseForSync; export default { dbReady, diff --git a/packages/trilium-core/src/services/sql_init.ts b/packages/trilium-core/src/services/sql_init.ts index aeda218301..074c1d673e 100644 --- a/packages/trilium-core/src/services/sql_init.ts +++ b/packages/trilium-core/src/services/sql_init.ts @@ -1,4 +1,4 @@ -import { deferred } from "@triliumnext/commons"; +import { deferred, OptionRow } from "@triliumnext/commons"; import { getSql } from "./sql"; import { getLog } from "./log"; import { isElectron } from "./utils"; @@ -11,6 +11,7 @@ import BNote from "../becca/entities/bnote"; import BBranch from "../becca/entities/bbranch"; import hidden_subtree from "./hidden_subtree"; import TaskContext from "./task_context"; +import BOption from "../becca/entities/boption"; export const dbReady = deferred(); @@ -75,10 +76,6 @@ async function initDbConnection() { dbReady.resolve(); } -async function createDatabaseForSync(a: any, b: string, c: any) { - console.error("createDatabaseForSync is not implemented yet"); -} - function setDbAsInitialized() { if (!isDbInitialized()) { optionService.setOption("initialized", "true"); @@ -222,4 +219,30 @@ async function createInitialDatabase(skipDemoDb?: boolean) { initDbConnection(); } +async function createDatabaseForSync(options: OptionRow[], syncServerHost = "", syncProxy = "") { + const log = getLog(); + const sql = getSql(); + log.info("Creating database for sync"); + + if (isDbInitialized()) { + throw new Error("DB is already initialized"); + } + + // We have to import async since options init requires keyboard actions which require translations. + const optionsInitService = (await import("./options_init.js")).default; + + sql.transactional(() => { + sql.executeScript(schema); + + optionsInitService.initNotSyncedOptions(false, { syncServerHost, syncProxy }); + + // document options required for sync to kick off + for (const opt of options) { + new BOption(opt).save(); + } + }); + + log.info("Schema and not synced options generated."); +} + export default { isDbInitialized, createDatabaseForSync, setDbAsInitialized, schemaExists, getDbSize, initDbConnection, dbReady, initializeDb, createInitialDatabase };