This commit is contained in:
Adam Shiervani 2025-04-11 00:04:58 +02:00 committed by Siyuan Miao
parent 4353240822
commit bafbc3da4c
2 changed files with 64 additions and 42 deletions

View File

@ -14,17 +14,17 @@ import notifications from "@/notifications";
import { DEVICE_API } from "@/ui.config";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { isOnDevice } from "@/main";
import { TextAreaWithLabel } from "@components/TextArea";
import { LocalDevice } from "./devices.$id";
import { SettingsItem } from "./devices.$id.settings";
import { CloudState } from "./adopt";
import { TextAreaWithLabel } from "@components/TextArea";
export interface TLSState {
mode: "self-signed" | "custom" | "disabled";
certificate?: string;
privateKey?: string;
};
}
export const loader = async () => {
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
const handleTlsModeChange = (value: string) => {
setTlsMode(value);
// For "disabled" and "self-signed" modes, immediately apply the settings
if (value !== "custom") {
updateTlsState(value);
}
};
const handleTlsCertChange = (value: string) => {
@ -160,21 +188,10 @@ export default function SettingsAccessIndexRoute() {
setTlsKey(value);
};
const handleTlsUpdate = useCallback(() => {
const state = { mode: tlsMode } as TLSState;
if (tlsMode !== "disabled") {
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]);
// Update the custom TLS settings button click handler
const handleCustomTlsUpdate = () => {
updateTlsState(tlsMode, tlsCert, tlsKey);
};
// Fetch device ID and cloud state on component mount
useEffect(() => {
@ -203,11 +220,9 @@ export default function SettingsAccessIndexRoute() {
/>
<>
<SettingsItem
title="HTTPS/TLS Mode"
description={<>
Select the TLS mode for your device <sup>experimental</sup><br />
<small>The feature might not work as expected, please report any issues if you encounter any.</small>
</>}
title="HTTPS Mode"
badge="Experimental"
description="Configure secure HTTPS access to your device"
>
<SelectMenuBasic
size="SM"
@ -227,13 +242,15 @@ export default function SettingsAccessIndexRoute() {
<div className="space-y-4">
<SettingsItem
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">
<TextAreaWithLabel
label="Certificate"
rows={3}
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
placeholder={
"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
}
value={tlsCert}
onChange={e => handleTlsCertChange(e.target.value)}
/>
@ -243,32 +260,28 @@ export default function SettingsAccessIndexRoute() {
<div className="space-y-4">
<TextAreaWithLabel
label="Private Key"
description="For security reasons, it will not be displayed after saving."
rows={3}
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
placeholder={
"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
}
value={tlsKey}
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 className="flex items-center gap-x-2">
<Button
size="SM"
theme="primary"
text="Update TLS Settings"
onClick={handleCustomTlsUpdate}
/>
</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
title="Authentication Mode"
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}

View File

@ -246,6 +246,7 @@ export function SettingsItem({
children,
className,
loading,
badge,
}: {
title: string;
description: string | React.ReactNode;
@ -253,6 +254,7 @@ export function SettingsItem({
className?: string;
name?: string;
loading?: boolean;
badge?: string;
}) {
return (
<label
@ -263,10 +265,17 @@ export function SettingsItem({
>
<div className="space-y-0.5">
<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" />}
</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>
{children ? <div>{children}</div> : null}
</label>