diff --git a/config.go b/config.go index 3e88457..e286344 100644 --- a/config.go +++ b/config.go @@ -205,6 +205,20 @@ func SaveConfig() error { return nil } +func RemoveConfig() error { + configLock.Lock() + defer configLock.Unlock() + + logger.Trace().Str("path", configPath).Msg("Removing config") + + err := os.Remove(configPath) + if err != nil { + return fmt.Errorf("failed to remove config file: %w", err) + } + + return nil +} + func ensureConfigLoaded() { if config == nil { LoadConfig() diff --git a/jsonrpc.go b/jsonrpc.go index e930f49..8049372 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -192,6 +192,24 @@ func rpcReboot(force bool) error { return nil } +func rpcFactoryReset() error { + logger.Info().Msg("Got factory reset request from JSONRPC") + + err := RemoveConfig() + if err != nil { + logger.Error().Err(err).Msg("failed to remove config") + return err + } + + err = rpcReboot(true) + if err != nil { + logger.Error().Err(err).Msg("failed to reboot after config remove") + return err + } + + return nil +} + var streamFactor = 1.0 func rpcGetStreamQualityFactor() (float64, error) { @@ -1038,6 +1056,7 @@ func rpcSetLocalLoopbackOnly(enabled bool) error { var rpcHandlers = map[string]RPCHandler{ "ping": {Func: rpcPing}, "reboot": {Func: rpcReboot, Params: []string{"force"}}, + "factoryReset": {Func: rpcFactoryReset}, "getDeviceID": {Func: rpcGetDeviceID}, "deregisterDevice": {Func: rpcDeregisterDevice}, "getCloudState": {Func: rpcGetCloudState}, diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 6fb5738..73d9b74 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -43,6 +43,7 @@ import SettingsVideoRoute from "./routes/devices.$id.settings.video"; import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance"; import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index"; import SettingsGeneralRebootRoute from "./routes/devices.$id.settings.general.reboot"; +import SettingsGeneralFactoryResetRoute from "./routes/devices.$id.settings.general.factory-reset"; import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update"; import SettingsNetworkRoute from "./routes/devices.$id.settings.network"; import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth"; @@ -145,6 +146,10 @@ if (isOnDevice) { path: "reboot", element: , }, + { + path: "factory-reset", + element: , + }, { path: "update", element: , diff --git a/ui/src/routes/devices.$id.settings.general._index.tsx b/ui/src/routes/devices.$id.settings.general._index.tsx index ecefdfa..e332d65 100644 --- a/ui/src/routes/devices.$id.settings.general._index.tsx +++ b/ui/src/routes/devices.$id.settings.general._index.tsx @@ -95,18 +95,33 @@ export default function SettingsGeneralRoute() {
+ +
+ +
+
+
diff --git a/ui/src/routes/devices.$id.settings.general.factory-reset.tsx b/ui/src/routes/devices.$id.settings.general.factory-reset.tsx new file mode 100644 index 0000000..1e3993b --- /dev/null +++ b/ui/src/routes/devices.$id.settings.general.factory-reset.tsx @@ -0,0 +1,29 @@ +import { useNavigate } from "react-router-dom"; +import { useCallback } from "react"; + +import { useJsonRpc } from "@/hooks/useJsonRpc"; +import { ConfirmDialog } from "@/components/ConfirmDialog"; + +export default function SettingsGeneralFactoryResetRoute() { + const navigate = useNavigate(); + const [send] = useJsonRpc(); + + const onConfirmUpdate = useCallback(() => { + // This is where we send the RPC to the golang binary + send("factoryReset", {}); + }, [send]); + + { + /* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */ + } + return ( + navigate("..")} + title="Factory Reset" + description="Do you want to proceed with factory resetting the JetKVM?" + variant="danger" + onConfirm={onConfirmUpdate} + /> + ); +} \ No newline at end of file diff --git a/ui/src/routes/devices.$id.settings.general.reboot.tsx b/ui/src/routes/devices.$id.settings.general.reboot.tsx index c6889f6..6dd8a11 100644 --- a/ui/src/routes/devices.$id.settings.general.reboot.tsx +++ b/ui/src/routes/devices.$id.settings.general.reboot.tsx @@ -2,7 +2,7 @@ import { useNavigate } from "react-router-dom"; import { useCallback } from "react"; import { useJsonRpc } from "@/hooks/useJsonRpc"; -import { Button } from "@components/Button"; +import { ConfirmDialog } from "@/components/ConfirmDialog"; export default function SettingsGeneralRebootRoute() { const navigate = useNavigate(); @@ -16,51 +16,14 @@ export default function SettingsGeneralRebootRoute() { { /* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */ } - return navigate("..")} onConfirmUpdate={onConfirmUpdate} />; -} - -export function Dialog({ - onClose, - onConfirmUpdate, -}: { - onClose: () => void; - onConfirmUpdate: () => void; -}) { - return ( -
-
- -
-
+ navigate("..")} + title="Reboot JetKVM" + description="Do you want to proceed with rebooting the JetKVM?" + variant="danger" + onConfirm={onConfirmUpdate} + /> ); -} - -function ConfirmationBox({ - onYes, - onNo, -}: { - onYes: () => void; - onNo: () => void; -}) { - return ( -
-
-

- Reboot JetKVM -

-

- Do you want to proceed with rebooting the system? -

- -
-
-
-
- ); -} +} \ No newline at end of file