Add SSH TOTP authentication support (#350)

* Add SSH TOTP authentication support

- Implement keyboard-interactive authentication for SSH connections
- Add TOTP dialog component for Terminal and File Manager
- Handle TOTP prompts in WebSocket and HTTP connections
- Disable Server Stats for TOTP-enabled servers
- Add i18n support for TOTP-related messages

* Update src/backend/ssh/server-stats.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update src/backend/ssh/file-manager.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
Co-authored-by: Karmaa <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
ZacharyZcR
2025-10-08 09:01:48 +08:00
committed by LukeGus
parent 75ffa65a38
commit a352581421
10 changed files with 511 additions and 16 deletions

View File

@@ -119,11 +119,16 @@ export function Server({
setMetrics(data);
setShowStatsUI(true);
}
} catch (error) {
} catch (error: any) {
if (!cancelled) {
setMetrics(null);
setShowStatsUI(false);
toast.error(t("serverStats.failedToFetchMetrics"));
if (error?.code === "TOTP_REQUIRED" ||
(error?.response?.status === 403 && error?.response?.data?.error === "TOTP_REQUIRED")) {
toast.error(t("serverStats.totpUnavailable"));
} else {
toast.error(t("serverStats.failedToFetchMetrics"));
}
}
} finally {
if (!cancelled) {
@@ -210,17 +215,28 @@ export function Server({
setMetrics(data);
setShowStatsUI(true);
} catch (error: any) {
if (error?.response?.status === 503) {
if (error?.code === "TOTP_REQUIRED" ||
(error?.response?.status === 403 && error?.response?.data?.error === "TOTP_REQUIRED")) {
toast.error(t("serverStats.totpUnavailable"));
setMetrics(null);
setShowStatsUI(false);
} else if (error?.response?.status === 503 || error?.status === 503) {
setServerStatus("offline");
} else if (error?.response?.status === 504) {
setMetrics(null);
setShowStatsUI(false);
} else if (error?.response?.status === 504 || error?.status === 504) {
setServerStatus("offline");
} else if (error?.response?.status === 404) {
setMetrics(null);
setShowStatsUI(false);
} else if (error?.response?.status === 404 || error?.status === 404) {
setServerStatus("offline");
setMetrics(null);
setShowStatsUI(false);
} else {
setServerStatus("offline");
setMetrics(null);
setShowStatsUI(false);
}
setMetrics(null);
setShowStatsUI(false);
} finally {
setIsRefreshing(false);
}