feat(standalone/setup): report errors in initial sync request

This commit is contained in:
Elian Doran
2026-03-24 18:24:29 +02:00
parent 4dcb08745b
commit cb3b362bad
7 changed files with 39 additions and 21 deletions

View File

@@ -83,6 +83,7 @@ body.setup {
display: flex;
flex-direction: column;
padding-top: 1em;
overflow: auto;
}
>footer {
@@ -102,6 +103,10 @@ body.setup {
gap: 1rem;
width: 80%;
margin: auto;
.admonition {
margin: 0;
}
}
.form-item-with-icon {

View File

@@ -1,10 +1,12 @@
import "./setup.css";
import { SetupSyncFromServerResponse } from "@triliumnext/commons";
import { ComponentChildren, render } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { initLocale, t } from "./services/i18n";
import server from "./services/server";
import Admonition from "./widgets/react/Admonition";
import Button from "./widgets/react/Button";
import { Card, CardFrame, CardSection } from "./widgets/react/Card";
import Collapsible from "./widgets/react/Collapsible";
@@ -212,15 +214,25 @@ function SyncFromServer({ setState }: { setState: (state: State) => void }) {
const [ syncServerHost, setSyncServerHost ] = useState("");
const [ password, setPassword ] = useState("");
const [ syncProxy, setSyncProxy ] = useState("");
const [ error, setError ] = useState<string | null>(null);
const isValid = syncServerHost.trim() !== "" && password !== "";
async function handleFinishSetup() {
await server.post("setup/sync-from-server", {
syncServerHost: syncServerHost.trim(),
syncProxy: syncProxy.trim(),
password
});
setState("syncInProgress");
try {
const resp = await server.post<SetupSyncFromServerResponse>("setup/sync-from-server", {
syncServerHost: syncServerHost.trim(),
syncProxy: syncProxy.trim(),
password
});
if (resp.result === "success") {
setState("syncInProgress");
} else {
setError(t("setup.sync-failed", { message: resp.error }));
}
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
}
return (
@@ -230,7 +242,7 @@ function SyncFromServer({ setState }: { setState: (state: State) => void }) {
<p>{t("setup.sync-from-server-page-description")}</p>
<main>
<form onSubmit={handleFinishSetup}>
<form>
<FormItemWithIcon icon="bx bx-server">
<FormTextBox placeholder="https://example.com" currentValue={syncServerHost} onChange={setSyncServerHost} required />
</FormItemWithIcon>
@@ -244,6 +256,8 @@ function SyncFromServer({ setState }: { setState: (state: State) => void }) {
<FormTextBox placeholder="http://my-proxy.com:8080" currentValue={syncProxy} onChange={setSyncProxy} />
</FormItemWithIcon>
</Collapsible>
{error && <Admonition className="error" type="caution">{error}</Admonition>}
</form>
</main>

View File

@@ -101,16 +101,6 @@ class SetupController {
const syncProxy = this.syncProxyInput.value.trim();
const password = this.passwordInput.value;
if (!syncServerHost) {
showAlert("Trilium server address can't be empty");
return;
}
if (!password) {
showAlert("Password can't be empty");
return;
}
if (resp.result === "success") {
hideAlert();
this.setStep("sync-in-progress");

View File

@@ -2264,6 +2264,7 @@
"sync-from-desktop-step5": "Click \"Test sync\" button to verify connection is successful.",
"sync-from-desktop-final": "Once you've completed these steps, you can proceed to the next step.",
"sync-from-desktop-waiting": "Waiting for connection...",
"advanced-options": "Advanced options"
"advanced-options": "Advanced options",
"sync-failed": "Failed to sync: {{message}}"
}
}

View File

@@ -370,3 +370,10 @@ export interface SetupSyncSeedResponse {
syncVersion: number;
options: OptionRow[];
}
export type SetupSyncFromServerResponse = {
result: "success";
} | {
result: "failure";
error: string;
}

View File

@@ -3,6 +3,7 @@ import setupService from "../../services/setup.js";
import { getLog } from "../../services/log.js";
import appInfo from "../../services/app_info.js";
import type { Request } from "express";
import { SetupSyncFromServerResponse } from "@triliumnext/commons";
function getStatus() {
return {
@@ -16,7 +17,7 @@ async function setupNewDocument() {
await sqlInit.createInitialDatabase();
}
function setupSyncFromServer(req: Request) {
function setupSyncFromServer(req: Request): Promise<SetupSyncFromServerResponse> {
const { syncServerHost, syncProxy, password } = req.body;
return setupService.setupSyncFromSyncServer(syncServerHost, syncProxy, password);

View File

@@ -6,7 +6,7 @@ import syncOptions from "./sync_options.js";
import appInfo from "./app_info.js";
import { timeLimit } from "./utils/index.js";
import becca from "../becca/becca.js";
import type { SetupStatusResponse, SetupSyncSeedResponse } from "@triliumnext/commons";
import type { SetupStatusResponse, SetupSyncFromServerResponse, SetupSyncSeedResponse } from "@triliumnext/commons";
import request from "./request.js";
async function hasSyncServerSchemaAndSeed() {
@@ -61,7 +61,7 @@ async function requestToSyncServer<T>(method: string, path: string, body?: strin
)) as T;
}
async function setupSyncFromSyncServer(syncServerHost: string, syncProxy: string, password: string) {
async function setupSyncFromSyncServer(syncServerHost: string, syncProxy: string, password: string): Promise<SetupSyncFromServerResponse> {
if (sqlInit.isDbInitialized()) {
return {
result: "failure",