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 { 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,31 +260,27 @@ 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>
)}
<div className="space-y-4">
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
<Button <Button
size="SM" size="SM"
theme="light" theme="primary"
text="Update TLS Settings" text="Update TLS Settings"
onClick={handleTlsUpdate} onClick={handleCustomTlsUpdate}
/> />
</div> </div>
</div> </div>
)}
<SettingsItem <SettingsItem
title="Authentication Mode" title="Authentication Mode"

View File

@ -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>