mirror of https://github.com/jetkvm/kvm.git
Ui fixes
This commit is contained in:
parent
4353240822
commit
bafbc3da4c
|
@ -14,17 +14,17 @@ import notifications from "@/notifications";
|
||||||
import { DEVICE_API } from "@/ui.config";
|
import { DEVICE_API } from "@/ui.config";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import { isOnDevice } from "@/main";
|
import { isOnDevice } from "@/main";
|
||||||
|
import { TextAreaWithLabel } from "@components/TextArea";
|
||||||
|
|
||||||
import { LocalDevice } from "./devices.$id";
|
import { LocalDevice } from "./devices.$id";
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
import { CloudState } from "./adopt";
|
import { CloudState } from "./adopt";
|
||||||
import { TextAreaWithLabel } from "@components/TextArea";
|
|
||||||
|
|
||||||
export interface TLSState {
|
export interface TLSState {
|
||||||
mode: "self-signed" | "custom" | "disabled";
|
mode: "self-signed" | "custom" | "disabled";
|
||||||
certificate?: string;
|
certificate?: string;
|
||||||
privateKey?: string;
|
privateKey?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const loader = async () => {
|
export const loader = async () => {
|
||||||
if (isOnDevice) {
|
if (isOnDevice) {
|
||||||
|
@ -147,9 +147,37 @@ export default function SettingsAccessIndexRoute() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function to update TLS state - accepts a mode parameter
|
||||||
|
const updateTlsState = useCallback(
|
||||||
|
(mode: string, cert?: string, key?: string) => {
|
||||||
|
const state = { mode } as TLSState;
|
||||||
|
if (cert && key) {
|
||||||
|
state.certificate = cert;
|
||||||
|
state.privateKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
send("setTLSState", { state }, resp => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to update TLS settings: ${resp.error.data || "Unknown error"}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications.success("TLS settings updated successfully");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[send],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle TLS mode change
|
// Handle TLS mode change
|
||||||
const handleTlsModeChange = (value: string) => {
|
const handleTlsModeChange = (value: string) => {
|
||||||
setTlsMode(value);
|
setTlsMode(value);
|
||||||
|
|
||||||
|
// For "disabled" and "self-signed" modes, immediately apply the settings
|
||||||
|
if (value !== "custom") {
|
||||||
|
updateTlsState(value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTlsCertChange = (value: string) => {
|
const handleTlsCertChange = (value: string) => {
|
||||||
|
@ -160,21 +188,10 @@ export default function SettingsAccessIndexRoute() {
|
||||||
setTlsKey(value);
|
setTlsKey(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTlsUpdate = useCallback(() => {
|
// Update the custom TLS settings button click handler
|
||||||
const state = { mode: tlsMode } as TLSState;
|
const handleCustomTlsUpdate = () => {
|
||||||
if (tlsMode !== "disabled") {
|
updateTlsState(tlsMode, tlsCert, tlsKey);
|
||||||
state.certificate = tlsCert;
|
};
|
||||||
state.privateKey = tlsKey;
|
|
||||||
}
|
|
||||||
send("setTLSState", { state }, resp => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
notifications.error(`Failed to update TLS settings: ${resp.error.data || "Unknown error"}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifications.success("TLS settings updated successfully");
|
|
||||||
});
|
|
||||||
}, [send, tlsMode, tlsCert, tlsKey]);
|
|
||||||
|
|
||||||
// Fetch device ID and cloud state on component mount
|
// Fetch device ID and cloud state on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -203,11 +220,9 @@ export default function SettingsAccessIndexRoute() {
|
||||||
/>
|
/>
|
||||||
<>
|
<>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="HTTPS/TLS Mode"
|
title="HTTPS Mode"
|
||||||
description={<>
|
badge="Experimental"
|
||||||
Select the TLS mode for your device <sup>experimental</sup><br />
|
description="Configure secure HTTPS access to your device"
|
||||||
<small>The feature might not work as expected, please report any issues if you encounter any.</small>
|
|
||||||
</>}
|
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
|
@ -227,13 +242,15 @@ export default function SettingsAccessIndexRoute() {
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="TLS Certificate"
|
title="TLS Certificate"
|
||||||
description="Enter your TLS certificate here, if intermediate or root CA is used, you can paste the entire chain here too"
|
description="Paste your TLS certificate below. For certificate chains, include the entire chain (leaf, intermediate, and root certificates)."
|
||||||
/>
|
/>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<TextAreaWithLabel
|
<TextAreaWithLabel
|
||||||
label="Certificate"
|
label="Certificate"
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
|
placeholder={
|
||||||
|
"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||||
|
}
|
||||||
value={tlsCert}
|
value={tlsCert}
|
||||||
onChange={e => handleTlsCertChange(e.target.value)}
|
onChange={e => handleTlsCertChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -243,32 +260,28 @@ export default function SettingsAccessIndexRoute() {
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<TextAreaWithLabel
|
<TextAreaWithLabel
|
||||||
label="Private Key"
|
label="Private Key"
|
||||||
|
description="For security reasons, it will not be displayed after saving."
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
|
placeholder={
|
||||||
|
"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
|
||||||
|
}
|
||||||
value={tlsKey}
|
value={tlsKey}
|
||||||
onChange={e => handleTlsKeyChange(e.target.value)}
|
onChange={e => handleTlsKeyChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
|
||||||
Private key won't be shown again after saving.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="primary"
|
||||||
|
text="Update TLS Settings"
|
||||||
|
onClick={handleCustomTlsUpdate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center gap-x-2">
|
|
||||||
<Button
|
|
||||||
size="SM"
|
|
||||||
theme="light"
|
|
||||||
text="Update TLS Settings"
|
|
||||||
onClick={handleTlsUpdate}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Authentication Mode"
|
title="Authentication Mode"
|
||||||
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}
|
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}
|
||||||
|
|
|
@ -246,6 +246,7 @@ export function SettingsItem({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
loading,
|
loading,
|
||||||
|
badge,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
description: string | React.ReactNode;
|
description: string | React.ReactNode;
|
||||||
|
@ -253,6 +254,7 @@ export function SettingsItem({
|
||||||
className?: string;
|
className?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
badge?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
|
@ -263,10 +265,17 @@ export function SettingsItem({
|
||||||
>
|
>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
<h3 className="text-base font-semibold text-black dark:text-white">{title}</h3>
|
<div className="flex items-center text-base font-semibold text-black dark:text-white">
|
||||||
|
{title}
|
||||||
|
{badge && (
|
||||||
|
<span className="ml-2 rounded-full bg-red-500 px-2 py-1 text-[10px] font-medium leading-none text-white dark:border dark:border-red-700 dark:bg-red-800 dark:text-red-50">
|
||||||
|
{badge}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{loading && <LoadingSpinner className="h-4 w-4 text-blue-500" />}
|
{loading && <LoadingSpinner className="h-4 w-4 text-blue-500" />}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-slate-700 dark:text-slate-300">{description}</p>
|
<div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>
|
||||||
</div>
|
</div>
|
||||||
{children ? <div>{children}</div> : null}
|
{children ? <div>{children}</div> : null}
|
||||||
</label>
|
</label>
|
||||||
|
|
Loading…
Reference in New Issue