Settings Advanced page

This commit is contained in:
Marc Brooks 2025-10-13 18:05:59 -05:00
parent 5613555b39
commit 0eb577b6f7
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
2 changed files with 105 additions and 59 deletions

View File

@ -544,6 +544,52 @@
"local_auth_update_current_password_label": "Current Password", "local_auth_update_current_password_label": "Current Password",
"local_auth_update_description": "Enter your current password and a new password to update your local device protection.", "local_auth_update_description": "Enter your current password and a new password to update your local device protection.",
"local_auth_update_new_password_label": "New Password", "local_auth_update_new_password_label": "New Password",
"local_auth_update_password_button": "Update Password",
"local_auth_update_title": "Change Local Device Password", "local_auth_update_title": "Change Local Device Password",
"local_auth_update_password_button": "Update Password"
"advanced_description": "Access additional settings for troubleshooting and customization",
"advanced_dev_channel_description": "Receive early updates from the development channel",
"advanced_dev_channel_title": "Dev Channel Updates",
"advanced_developer_mode_description": "Enable advanced features for developers",
"advanced_developer_mode_enabled_title": "Developer Mode Enabled",
"advanced_developer_mode_title": "Developer Mode",
"advanced_developer_mode_warning_advanced": "For advanced users only. Not for production use.",
"advanced_developer_mode_warning_risks": "Only use if you understand the risks",
"advanced_developer_mode_warning_security": "Security is weakened while active",
"advanced_disable_usb_emulation": "Disable USB Emulation",
"advanced_enable_usb_emulation": "Enable USB Emulation",
"advanced_error_loopback_disable": "Failed to disable loopback-only mode: {error}",
"advanced_error_loopback_enable": "Failed to enable loopback-only mode: {error}",
"advanced_error_reset_config": "Failed to reset configuration: {error}",
"advanced_error_set_dev_channel": "Failed to set dev channel state: {error}",
"advanced_error_set_dev_mode": "Failed to set dev mode: {error}",
"advanced_error_update_ssh_key": "Failed to update SSH key: {error}",
"advanced_error_usb_emulation_disable": "Failed to disable USB emulation: {error}",
"advanced_error_usb_emulation_enable": "Failed to enable USB emulation: {error}",
"advanced_loopback_only_description": "Restrict web interface access to localhost only (127.0.0.1)",
"advanced_loopback_only_title": "Loopback-Only Mode",
"advanced_loopback_warning_before": "Before enabling this feature, make sure you have either:",
"advanced_loopback_warning_cloud": "Cloud access enabled and working",
"advanced_loopback_warning_confirm": "I Understand, Enable Anyway",
"advanced_loopback_warning_description": "WARNING: This will restrict web interface access to localhost (127.0.0.1) only.",
"advanced_loopback_warning_ssh": "SSH access configured and tested",
"advanced_loopback_warning_title": "Enable Loopback-Only Mode?",
"advanced_reset_config_button": "Reset Config",
"advanced_reset_config_description": "Reset configuration to default. This will log you out.",
"advanced_reset_config_title": "Reset Configuration",
"advanced_ssh_access_description": "Add your SSH public key to enable secure remote access to the device",
"advanced_ssh_access_title": "SSH Access",
"advanced_ssh_default_user": "The default SSH user is",
"advanced_ssh_public_key_label": "SSH Public Key",
"advanced_ssh_public_key_placeholder": "Enter your SSH public key",
"advanced_success_loopback_disabled": "Loopback-only mode disabled. Restart your device to apply.",
"advanced_success_loopback_enabled": "Loopback-only mode enabled. Restart your device to apply.",
"advanced_success_reset_config": "Configuration reset to default successfully",
"advanced_success_update_ssh_key": "SSH key updated successfully",
"advanced_title": "Advanced",
"advanced_troubleshooting_mode_description": "Diagnostic tools and additional controls for troubleshooting and development purposes",
"advanced_troubleshooting_mode_title": "Troubleshooting Mode",
"advanced_update_ssh_key_button": "Update SSH Key",
"advanced_usb_emulation_description": "Control the USB emulation state",
"advanced_usb_emulation_title": "USB Emulation"
} }

View File

@ -1,17 +1,17 @@
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSettingsStore } from "@hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import { Button } from "@components/Button";
import Checkbox from "@components/Checkbox";
import { ConfirmDialog } from "@components/ConfirmDialog";
import { GridCard } from "@components/Card"; import { GridCard } from "@components/Card";
import { SettingsItem } from "@components/SettingsItem"; import { SettingsItem } from "@components/SettingsItem";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import { Button } from "../components/Button"; import { TextAreaWithLabel } from "@components/TextArea";
import Checkbox from "../components/Checkbox"; import { isOnDevice } from "@/main";
import { ConfirmDialog } from "../components/ConfirmDialog"; import notifications from "@/notifications";
import { SettingsPageHeader } from "../components/SettingsPageheader"; import { m } from "@localizations/messages.js";
import { TextAreaWithLabel } from "../components/TextArea";
import { useSettingsStore } from "../hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "../hooks/useJsonRpc";
import { isOnDevice } from "../main";
import notifications from "../notifications";
export default function SettingsAdvancedRoute() { export default function SettingsAdvancedRoute() {
const { send } = useJsonRpc(); const { send } = useJsonRpc();
@ -65,7 +65,9 @@ export default function SettingsAdvancedRoute() {
send("setUsbEmulationState", { enabled: enabled }, (resp: JsonRpcResponse) => { send("setUsbEmulationState", { enabled: enabled }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to ${enabled ? "enable" : "disable"} USB emulation: ${resp.error.data || "Unknown error"}`, enabled
? m.advanced_error_usb_emulation_enable({error: resp.error.data || m.unknown_error()})
: m.advanced_error_usb_emulation_disable({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
@ -80,11 +82,11 @@ export default function SettingsAdvancedRoute() {
send("resetConfig", {}, (resp: JsonRpcResponse) => { send("resetConfig", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to reset configuration: ${resp.error.data || "Unknown error"}`, m.advanced_error_reset_config({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
notifications.success("Configuration reset to default successfully"); notifications.success(m.advanced_success_reset_config());
}); });
}, [send]); }, [send]);
@ -92,11 +94,11 @@ export default function SettingsAdvancedRoute() {
send("setSSHKeyState", { sshKey }, (resp: JsonRpcResponse) => { send("setSSHKeyState", { sshKey }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to update SSH key: ${resp.error.data || "Unknown error"}`, m.advanced_error_update_ssh_key({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
notifications.success("SSH key updated successfully"); notifications.success(m.advanced_success_update_ssh_key());
}); });
}, [send, sshKey]); }, [send, sshKey]);
@ -105,7 +107,7 @@ export default function SettingsAdvancedRoute() {
send("setDevModeState", { enabled: developerMode }, (resp: JsonRpcResponse) => { send("setDevModeState", { enabled: developerMode }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to set dev mode: ${resp.error.data || "Unknown error"}`, m.advanced_error_set_dev_mode({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
@ -120,7 +122,7 @@ export default function SettingsAdvancedRoute() {
send("setDevChannelState", { enabled }, (resp: JsonRpcResponse) => { send("setDevChannelState", { enabled }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to set dev channel state: ${resp.error.data || "Unknown error"}`, m.advanced_error_set_dev_channel({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
@ -135,19 +137,17 @@ export default function SettingsAdvancedRoute() {
send("setLocalLoopbackOnly", { enabled }, (resp: JsonRpcResponse) => { send("setLocalLoopbackOnly", { enabled }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to ${enabled ? "enable" : "disable"} loopback-only mode: ${resp.error.data || "Unknown error"}`, enabled
? m.advanced_error_loopback_enable({error: resp.error.data || m.unknown_error()})
: m.advanced_error_loopback_disable({error: resp.error.data || m.unknown_error()})
); );
return; return;
} }
setLocalLoopbackOnly(enabled); setLocalLoopbackOnly(enabled);
if (enabled) { if (enabled) {
notifications.success( notifications.success(m.advanced_success_loopback_enabled());
"Loopback-only mode enabled. Restart your device to apply.",
);
} else { } else {
notifications.success( notifications.success(m.advanced_success_loopback_disabled());
"Loopback-only mode disabled. Restart your device to apply.",
);
} }
}); });
}, },
@ -175,14 +175,14 @@ export default function SettingsAdvancedRoute() {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<SettingsPageHeader <SettingsPageHeader
title="Advanced" title={m.advanced_title()}
description="Access additional settings for troubleshooting and customization" description={m.advanced_description()}
/> />
<div className="space-y-4"> <div className="space-y-4">
<SettingsItem <SettingsItem
title="Dev Channel Updates" title={m.advanced_dev_channel_title()}
description="Receive early updates from the development channel" description={m.advanced_dev_channel_description()}
> >
<Checkbox <Checkbox
checked={devChannel} checked={devChannel}
@ -192,8 +192,8 @@ export default function SettingsAdvancedRoute() {
/> />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
title="Developer Mode" title={m.advanced_developer_mode_title()}
description="Enable advanced features for developers" description={m.advanced_developer_mode_description()}
> >
<Checkbox <Checkbox
checked={settings.developerMode} checked={settings.developerMode}
@ -219,18 +219,17 @@ export default function SettingsAdvancedRoute() {
<div className="space-y-3"> <div className="space-y-3">
<div className="space-y-2"> <div className="space-y-2">
<h3 className="text-base font-bold text-slate-900 dark:text-white"> <h3 className="text-base font-bold text-slate-900 dark:text-white">
Developer Mode Enabled {m.advanced_developer_mode_enabled_title()}
</h3> </h3>
<div> <div>
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300"> <ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
<li>Security is weakened while active</li> <li>{m.advanced_developer_mode_warning_security()}</li>
<li>Only use if you understand the risks</li> <li>{m.advanced_developer_mode_warning_risks()}</li>
</ul> </ul>
</div> </div>
</div> </div>
<div className="text-xs text-slate-700 dark:text-slate-300"> <div className="text-xs text-slate-700 dark:text-slate-300">
For advanced users only. Not for production use. {m.advanced_developer_mode_warning_advanced()}
</div> </div>
</div> </div>
</div> </div>
@ -238,8 +237,8 @@ export default function SettingsAdvancedRoute() {
)} )}
<SettingsItem <SettingsItem
title="Loopback-Only Mode" title={m.advanced_loopback_only_title()}
description="Restrict web interface access to localhost only (127.0.0.1)" description={m.advanced_loopback_only_description()}
> >
<Checkbox <Checkbox
checked={localLoopbackOnly} checked={localLoopbackOnly}
@ -250,25 +249,25 @@ export default function SettingsAdvancedRoute() {
{isOnDevice && settings.developerMode && ( {isOnDevice && settings.developerMode && (
<div className="space-y-4"> <div className="space-y-4">
<SettingsItem <SettingsItem
title="SSH Access" title={m.advanced_ssh_access_title()}
description="Add your SSH public key to enable secure remote access to the device" description={m.advanced_ssh_access_description()}
/> />
<div className="space-y-4"> <div className="space-y-4">
<TextAreaWithLabel <TextAreaWithLabel
label="SSH Public Key" label={m.advanced_ssh_public_key_label()}
value={sshKey || ""} value={sshKey || ""}
rows={3} rows={3}
onChange={e => setSSHKey(e.target.value)} onChange={e => setSSHKey(e.target.value)}
placeholder="Enter your SSH public key" placeholder={m.advanced_ssh_public_key_placeholder()}
/> />
<p className="text-xs text-slate-600 dark:text-slate-400"> <p className="text-xs text-slate-600 dark:text-slate-400">
The default SSH user is <strong>root</strong>. {m.advanced_ssh_default_user()}<strong>root</strong>.
</p> </p>
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
<Button <Button
size="SM" size="SM"
theme="primary" theme="primary"
text="Update SSH Key" text={m.advanced_update_ssh_key_button()}
onClick={handleUpdateSSHKey} onClick={handleUpdateSSHKey}
/> />
</div> </div>
@ -277,8 +276,8 @@ export default function SettingsAdvancedRoute() {
)} )}
<SettingsItem <SettingsItem
title="Troubleshooting Mode" title={m.advanced_troubleshooting_mode_title()}
description="Diagnostic tools and additional controls for troubleshooting and development purposes" description={m.advanced_troubleshooting_mode_description()}
> >
<Checkbox <Checkbox
defaultChecked={settings.debugMode} defaultChecked={settings.debugMode}
@ -291,27 +290,27 @@ export default function SettingsAdvancedRoute() {
{settings.debugMode && ( {settings.debugMode && (
<> <>
<SettingsItem <SettingsItem
title="USB Emulation" title={m.advanced_usb_emulation_title()}
description="Control the USB emulation state" description={m.advanced_usb_emulation_description()}
> >
<Button <Button
size="SM" size="SM"
theme="light" theme="light"
text={ text={
usbEmulationEnabled ? "Disable USB Emulation" : "Enable USB Emulation" usbEmulationEnabled ? m.advanced_disable_usb_emulation() : m.advanced_enable_usb_emulation()
} }
onClick={() => handleUsbEmulationToggle(!usbEmulationEnabled)} onClick={() => handleUsbEmulationToggle(!usbEmulationEnabled)}
/> />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
title="Reset Configuration" title={m.advanced_reset_config_title()}
description="Reset configuration to default. This will log you out." description={m.advanced_reset_config_description()}
> >
<Button <Button
size="SM" size="SM"
theme="light" theme="light"
text="Reset Config" text={m.advanced_reset_config_button()}
onClick={() => { onClick={() => {
handleResetConfig(); handleResetConfig();
window.location.reload(); window.location.reload();
@ -327,22 +326,23 @@ export default function SettingsAdvancedRoute() {
onClose={() => { onClose={() => {
setShowLoopbackWarning(false); setShowLoopbackWarning(false);
}} }}
title="Enable Loopback-Only Mode?" title={m.advanced_loopback_warning_title()}
description={ description={
<> <>
<p> <p>
WARNING: This will restrict web interface access to localhost (127.0.0.1) {m.advanced_loopback_warning_description()}
only. </p>
<p>
{m.advanced_loopback_warning_before()}
</p> </p>
<p>Before enabling this feature, make sure you have either:</p>
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300"> <ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
<li>SSH access configured and tested</li> <li>{m.advanced_loopback_warning_ssh()}</li>
<li>Cloud access enabled and working</li> <li>{m.advanced_loopback_warning_cloud()}</li>
</ul> </ul>
</> </>
} }
variant="warning" variant="warning"
confirmText="I Understand, Enable Anyway" confirmText={m.advanced_loopback_warning_confirm()}
onConfirm={confirmLoopbackModeEnable} onConfirm={confirmLoopbackModeEnable}
/> />
</div> </div>