Skip to content

Commit 446cc7a

Browse files
committed
feat: add TOTP input field to sync-from-server setup UI
1 parent d595a1d commit 446cc7a

5 files changed

Lines changed: 51 additions & 6 deletions

File tree

apps/client/src/setup.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import "jquery";
2-
import utils from "./services/utils.js";
2+
33
import ko from "knockout";
44

5+
import utils from "./services/utils.js";
6+
57
// TriliumNextTODO: properly make use of below types
68
// type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | "";
79
// type SetupModelStep = "sync-in-progress" | "setup-type" | "new-document-in-progress" | "sync-from-desktop";
@@ -16,6 +18,8 @@ class SetupModel {
1618
syncServerHost: ko.Observable<string | undefined>;
1719
syncProxy: ko.Observable<string | undefined>;
1820
password: ko.Observable<string | undefined>;
21+
totpToken: ko.Observable<string | undefined>;
22+
totpEnabled: ko.Observable<boolean>;
1923

2024
constructor(syncInProgress: boolean) {
2125
this.syncInProgress = syncInProgress;
@@ -27,6 +31,8 @@ class SetupModel {
2731
this.syncServerHost = ko.observable();
2832
this.syncProxy = ko.observable();
2933
this.password = ko.observable();
34+
this.totpToken = ko.observable();
35+
this.totpEnabled = ko.observable(false);
3036

3137
if (this.syncInProgress) {
3238
setInterval(checkOutstandingSyncs, 1000);
@@ -40,7 +46,7 @@ class SetupModel {
4046
return !!this.setupType();
4147
}
4248

43-
selectSetupType() {
49+
async selectSetupType() {
4450
if (this.setupType() === "new-document") {
4551
this.step("new-document-in-progress");
4652

@@ -52,6 +58,24 @@ class SetupModel {
5258
}
5359
}
5460

61+
async checkTotpStatus() {
62+
const syncServerHost = this.syncServerHost();
63+
if (!syncServerHost) {
64+
this.totpEnabled(false);
65+
return;
66+
}
67+
68+
try {
69+
const resp = await $.post("api/setup/check-server-totp", {
70+
syncServerHost
71+
});
72+
this.totpEnabled(!!resp.totpEnabled);
73+
} catch {
74+
// If we can't reach the server, don't show TOTP field yet
75+
this.totpEnabled(false);
76+
}
77+
}
78+
5579
back() {
5680
this.step("setup-type");
5781
this.setupType("");
@@ -72,11 +96,22 @@ class SetupModel {
7296
return;
7397
}
7498

99+
// Check TOTP status before submitting (in case it wasn't checked yet)
100+
await this.checkTotpStatus();
101+
102+
const totpToken = this.totpToken();
103+
104+
if (this.totpEnabled() && !totpToken) {
105+
showAlert("TOTP token can't be empty when two-factor authentication is enabled");
106+
return;
107+
}
108+
75109
// not using server.js because it loads too many dependencies
76110
const resp = await $.post("api/setup/sync-from-server", {
77-
syncServerHost: syncServerHost,
78-
syncProxy: syncProxy,
79-
password: password
111+
syncServerHost,
112+
syncProxy,
113+
password,
114+
totpToken
80115
});
81116

82117
if (resp.result === "success") {

apps/server/src/assets/translations/cn/server.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@
155155
"proxy-instruction": "如果您将代理设置留空,将使用系统代理(仅适用于桌面应用)",
156156
"password": "密码",
157157
"password-placeholder": "密码",
158+
"totp-token": "TOTP 验证码",
159+
"totp-token-placeholder": "请输入 TOTP 验证码",
158160
"back": "返回",
159161
"finish-setup": "完成设置"
160162
},

apps/server/src/assets/translations/en/server.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@
252252
"proxy-instruction": "If you leave proxy setting blank, system proxy will be used (applies to the desktop application only)",
253253
"password": "Password",
254254
"password-placeholder": "Password",
255+
"totp-token": "TOTP Token",
256+
"totp-token-placeholder": "Enter your TOTP code",
255257
"back": "Back",
256258
"finish-setup": "Finish setup"
257259
},

apps/server/src/assets/translations/tw/server.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@
155155
"proxy-instruction": "如果您將代理設定留空,將使用系統代理(僅適用於桌面版)",
156156
"password": "密碼",
157157
"password-placeholder": "密碼",
158+
"totp-token": "TOTP 驗證碼",
159+
"totp-token-placeholder": "請輸入 TOTP 驗證碼",
158160
"back": "返回",
159161
"finish-setup": "完成設定"
160162
},

apps/server/src/assets/views/setup.ejs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129

130130
<div class="form-group">
131131
<label for="sync-server-host"><%= t("setup_sync-from-server.server-host") %></label>
132-
<input type="text" id="syncServerHost" class="form-control" data-bind="value: syncServerHost" placeholder="<%= t("setup_sync-from-server.server-host-placeholder") %>">
132+
<input type="text" id="syncServerHost" class="form-control" data-bind="value: syncServerHost, event: { blur: checkTotpStatus }" placeholder="<%= t("setup_sync-from-server.server-host-placeholder") %>">
133133
</div>
134134
<div class="form-group">
135135
<label for="sync-proxy"><%= t("setup_sync-from-server.proxy-server") %></label>
@@ -141,6 +141,10 @@
141141
<label for="password"><%= t("setup_sync-from-server.password") %></label>
142142
<input type="password" id="password" class="form-control" data-bind="value: password" placeholder="<%= t("setup_sync-from-server.password-placeholder") %>">
143143
</div>
144+
<div class="form-group" style="margin-bottom: 8px;" data-bind="visible: totpEnabled">
145+
<label for="totpToken"><%= t("setup_sync-from-server.totp-token") %></label>
146+
<input type="text" id="totpToken" class="form-control" data-bind="value: totpToken" placeholder="<%= t("setup_sync-from-server.totp-token-placeholder") %>" autocomplete="one-time-code">
147+
</div>
144148

145149
<button type="button" data-bind="click: back" class="btn btn-secondary"><%= t("setup_sync-from-server.back") %></button>
146150

0 commit comments

Comments
 (0)