Config form improvements (#652)
* Config reset work WIP * Config reset WIP * Fix tests, tsc, linting * Form reset adjustments * Add ManagedModeDialog * Remove debug logging * Add Suspense * Review fixes --------- Co-authored-by: philon- <philon-@users.noreply.github.com>
This commit is contained in:
co-authored by
philon-
parent
7adbe01723
commit
181c984b27
@@ -3,16 +3,29 @@ import {
|
||||
BluetoothValidationSchema,
|
||||
} from "@app/validation/config/bluetooth.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Bluetooth = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface BluetoothConfigProps {
|
||||
onFormInit: DynamicFormFormInit<BluetoothValidation>;
|
||||
}
|
||||
export const Bluetooth = ({ onFormInit }: BluetoothConfigProps) => {
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const onSubmit = (data: BluetoothValidation) => {
|
||||
if (deepCompareConfig(config.bluetooth, data, true)) {
|
||||
removeWorkingConfig("bluetooth");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -26,9 +39,11 @@ export const Bluetooth = () => {
|
||||
return (
|
||||
<DynamicForm<BluetoothValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={BluetoothValidationSchema}
|
||||
formId="Config_BluetoothConfig"
|
||||
defaultValues={config.bluetooth}
|
||||
values={getEffectiveConfig("bluetooth")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("bluetooth.title"),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
useDevice,
|
||||
ValidConfigType,
|
||||
ValidModuleConfigType,
|
||||
} from "@core/stores/deviceStore.ts";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function ConfigSuspender({
|
||||
configCase,
|
||||
moduleConfigCase,
|
||||
children,
|
||||
}: {
|
||||
configCase?: ValidConfigType;
|
||||
moduleConfigCase?: ValidModuleConfigType;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { config, moduleConfig } = useDevice();
|
||||
|
||||
let cfg = undefined;
|
||||
if (configCase) {
|
||||
cfg = config[configCase];
|
||||
} else if (moduleConfigCase) {
|
||||
cfg = moduleConfig[moduleConfigCase];
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
|
||||
const [ready, setReady] = useState(() => cfg !== undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (cfg !== undefined) setReady(true);
|
||||
}, [cfg]);
|
||||
|
||||
if (!ready) throw new Promise(() => {}); // triggers suspense fallback
|
||||
|
||||
return children;
|
||||
}
|
||||
@@ -3,18 +3,31 @@ import {
|
||||
DeviceValidationSchema,
|
||||
} from "@app/validation/config/device.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useUnsafeRolesDialog } from "@components/Dialog/UnsafeRolesDialog/useUnsafeRolesDialog.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Device = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface DeviceConfigProps {
|
||||
onFormInit: DynamicFormFormInit<DeviceValidation>;
|
||||
}
|
||||
export const Device = ({ onFormInit }: DeviceConfigProps) => {
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
const { validateRoleSelection } = useUnsafeRolesDialog();
|
||||
|
||||
const onSubmit = (data: DeviceValidation) => {
|
||||
if (deepCompareConfig(config.device, data, true)) {
|
||||
removeWorkingConfig("device");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -24,12 +37,15 @@ export const Device = () => {
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DynamicForm<DeviceValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={DeviceValidationSchema}
|
||||
formId="Config_DeviceConfig"
|
||||
defaultValues={config.device}
|
||||
values={getEffectiveConfig("device")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("device.title"),
|
||||
@@ -97,7 +113,8 @@ export const Device = () => {
|
||||
properties: {
|
||||
fieldLength: {
|
||||
max: 64,
|
||||
currentValueLength: config.device?.tzdef?.length,
|
||||
currentValueLength: getEffectiveConfig("device")?.tzdef
|
||||
?.length,
|
||||
showCharacterCount: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,16 +3,29 @@ import {
|
||||
DisplayValidationSchema,
|
||||
} from "@app/validation/config/display.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Display = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface DisplayConfigProps {
|
||||
onFormInit: DynamicFormFormInit<DisplayValidation>;
|
||||
}
|
||||
export const Display = ({ onFormInit }: DisplayConfigProps) => {
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const onSubmit = (data: DisplayValidation) => {
|
||||
if (deepCompareConfig(config.display, data, true)) {
|
||||
removeWorkingConfig("display");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -26,9 +39,11 @@ export const Display = () => {
|
||||
return (
|
||||
<DynamicForm<DisplayValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={DisplayValidationSchema}
|
||||
formId="Config_DisplayConfig"
|
||||
defaultValues={config.display}
|
||||
values={getEffectiveConfig("display")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("display.title"),
|
||||
|
||||
@@ -3,16 +3,29 @@ import {
|
||||
LoRaValidationSchema,
|
||||
} from "@app/validation/config/lora.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const LoRa = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface LoRaConfigProps {
|
||||
onFormInit: DynamicFormFormInit<LoRaValidation>;
|
||||
}
|
||||
export const LoRa = ({ onFormInit }: LoRaConfigProps) => {
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const onSubmit = (data: LoRaValidation) => {
|
||||
if (deepCompareConfig(config.lora, data, true)) {
|
||||
removeWorkingConfig("lora");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -26,9 +39,11 @@ export const LoRa = () => {
|
||||
return (
|
||||
<DynamicForm<LoRaValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={LoRaValidationSchema}
|
||||
formId="Config_LoRaConfig"
|
||||
defaultValues={config.lora}
|
||||
values={getEffectiveConfig("lora")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("lora.title"),
|
||||
|
||||
@@ -3,7 +3,10 @@ import {
|
||||
NetworkValidationSchema,
|
||||
} from "@app/validation/config/network.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import {
|
||||
convertIntToIpAddress,
|
||||
@@ -11,35 +14,50 @@ import {
|
||||
} from "@core/utils/ip.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Network = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface NetworkConfigProps {
|
||||
onFormInit: DynamicFormFormInit<NetworkValidation>;
|
||||
}
|
||||
export const Network = ({ onFormInit }: NetworkConfigProps) => {
|
||||
const { config, setWorkingConfig, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const networkConfig = getEffectiveConfig("network");
|
||||
|
||||
const onSubmit = (data: NetworkValidation) => {
|
||||
const payload = {
|
||||
...data,
|
||||
ipv4Config: create(
|
||||
Protobuf.Config.Config_NetworkConfig_IpV4ConfigSchema,
|
||||
{
|
||||
ip: convertIpAddressToInt(data.ipv4Config?.ip ?? ""),
|
||||
gateway: convertIpAddressToInt(data.ipv4Config?.gateway ?? ""),
|
||||
subnet: convertIpAddressToInt(data.ipv4Config?.subnet ?? ""),
|
||||
dns: convertIpAddressToInt(data.ipv4Config?.dns ?? ""),
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
if (deepCompareConfig(config.network, payload, true)) {
|
||||
removeWorkingConfig("network");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
case: "network",
|
||||
value: {
|
||||
...data,
|
||||
ipv4Config: create(
|
||||
Protobuf.Config.Config_NetworkConfig_IpV4ConfigSchema,
|
||||
{
|
||||
ip: convertIpAddressToInt(data.ipv4Config?.ip ?? ""),
|
||||
gateway: convertIpAddressToInt(data.ipv4Config?.gateway ?? ""),
|
||||
subnet: convertIpAddressToInt(data.ipv4Config?.subnet ?? ""),
|
||||
dns: convertIpAddressToInt(data.ipv4Config?.dns ?? ""),
|
||||
},
|
||||
),
|
||||
},
|
||||
value: payload,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DynamicForm<NetworkValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={NetworkValidationSchema}
|
||||
formId="Config_NetworkConfig"
|
||||
defaultValues={{
|
||||
@@ -57,6 +75,21 @@ export const Network = () => {
|
||||
enabledProtocols: config.network?.enabledProtocols ??
|
||||
Protobuf.Config.Config_NetworkConfig_ProtocolFlags.NO_BROADCAST,
|
||||
}}
|
||||
values={{
|
||||
...networkConfig,
|
||||
ipv4Config: {
|
||||
ip: convertIntToIpAddress(networkConfig?.ipv4Config?.ip ?? 0),
|
||||
gateway: convertIntToIpAddress(
|
||||
networkConfig?.ipv4Config?.gateway ?? 0,
|
||||
),
|
||||
subnet: convertIntToIpAddress(
|
||||
networkConfig?.ipv4Config?.subnet ?? 0,
|
||||
),
|
||||
dns: convertIntToIpAddress(networkConfig?.ipv4Config?.dns ?? 0),
|
||||
},
|
||||
enabledProtocols: networkConfig?.enabledProtocols ??
|
||||
Protobuf.Config.Config_NetworkConfig_ProtocolFlags.NO_BROADCAST,
|
||||
} as NetworkValidation}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("network.title"),
|
||||
|
||||
@@ -7,20 +7,33 @@ import {
|
||||
PositionValidationSchema,
|
||||
} from "@app/validation/config/position.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Position = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface PositionConfigProps {
|
||||
onFormInit: DynamicFormFormInit<PositionValidation>;
|
||||
}
|
||||
export const Position = ({ onFormInit }: PositionConfigProps) => {
|
||||
const { setWorkingConfig, config, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { flagsValue, activeFlags, toggleFlag, getAllFlags } = usePositionFlags(
|
||||
config?.position?.positionFlags ?? 0,
|
||||
getEffectiveConfig("position")?.positionFlags ?? 0,
|
||||
);
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const onSubmit = (data: PositionValidation) => {
|
||||
if (deepCompareConfig(config.position, data, true)) {
|
||||
removeWorkingConfig("position");
|
||||
return;
|
||||
}
|
||||
|
||||
return setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -44,9 +57,11 @@ export const Position = () => {
|
||||
data.positionFlags = flagsValue;
|
||||
return onSubmit(data);
|
||||
}}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={PositionValidationSchema}
|
||||
formId="Config_PositionConfig"
|
||||
defaultValues={config.position}
|
||||
values={getEffectiveConfig("position")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("position.title"),
|
||||
|
||||
@@ -3,16 +3,29 @@ import {
|
||||
PowerValidationSchema,
|
||||
} from "@app/validation/config/power.ts";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
export const Power = () => {
|
||||
const { config, setWorkingConfig } = useDevice();
|
||||
interface PowerConfigProps {
|
||||
onFormInit: DynamicFormFormInit<PowerValidation>;
|
||||
}
|
||||
export const Power = ({ onFormInit }: PowerConfigProps) => {
|
||||
const { setWorkingConfig, config, getEffectiveConfig, removeWorkingConfig } =
|
||||
useDevice();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const onSubmit = (data: PowerValidation) => {
|
||||
if (deepCompareConfig(config.power, data, true)) {
|
||||
removeWorkingConfig("power");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -26,9 +39,11 @@ export const Power = () => {
|
||||
return (
|
||||
<DynamicForm<PowerValidation>
|
||||
onSubmit={onSubmit}
|
||||
onFormInit={onFormInit}
|
||||
validationSchema={PowerValidationSchema}
|
||||
formId="Config_PowerConfig"
|
||||
defaultValues={config.power}
|
||||
values={getEffectiveConfig("power")}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("power.powerConfigSettings.label"),
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { PkiRegenerateDialog } from "@components/Dialog/PkiRegenerateDialog.tsx";
|
||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||
import { ManagedModeDialog } from "@components/Dialog/ManagedModeDialog.tsx";
|
||||
import {
|
||||
DynamicForm,
|
||||
type DynamicFormFormInit,
|
||||
} from "@components/Form/DynamicForm.tsx";
|
||||
import { useAppStore } from "@core/stores/appStore.ts";
|
||||
import { getX25519PrivateKey, getX25519PublicKey } from "@core/utils/x25519.ts";
|
||||
import {
|
||||
@@ -7,37 +11,82 @@ import {
|
||||
type RawSecurity,
|
||||
RawSecuritySchema,
|
||||
} from "@app/validation/config/security.ts";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { fromByteArray, toByteArray } from "base64-js";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type DefaultValues, useForm } from "react-hook-form";
|
||||
import { createZodResolver } from "@components/Form/createZodResolver.ts";
|
||||
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
|
||||
|
||||
type KeyState = {
|
||||
publicKey: string;
|
||||
privateKey: string;
|
||||
privateKeyDialogOpen: boolean;
|
||||
};
|
||||
interface SecurityConfigProps {
|
||||
onFormInit: DynamicFormFormInit<RawSecurity>;
|
||||
}
|
||||
export const Security = ({ onFormInit }: SecurityConfigProps) => {
|
||||
const {
|
||||
config,
|
||||
setWorkingConfig,
|
||||
setDialogOpen,
|
||||
getEffectiveConfig,
|
||||
removeWorkingConfig,
|
||||
} = useDevice();
|
||||
|
||||
export const Security = () => {
|
||||
const { config, setWorkingConfig, setDialogOpen } = useDevice();
|
||||
const { removeError } = useAppStore();
|
||||
const { t } = useTranslation("deviceConfig");
|
||||
|
||||
const [keyState, setKeyState] = useState<KeyState>(() => ({
|
||||
publicKey: fromByteArray(config?.security?.publicKey ?? new Uint8Array(0)),
|
||||
privateKey: fromByteArray(
|
||||
config?.security?.privateKey ?? new Uint8Array(0),
|
||||
),
|
||||
privateKeyDialogOpen: false,
|
||||
}));
|
||||
const securityConfig = getEffectiveConfig("security");
|
||||
const defaultValues = {
|
||||
...securityConfig,
|
||||
...{
|
||||
privateKey: fromByteArray(
|
||||
securityConfig?.privateKey ?? new Uint8Array(0),
|
||||
),
|
||||
publicKey: fromByteArray(
|
||||
securityConfig?.publicKey ?? new Uint8Array(0),
|
||||
),
|
||||
adminKey: [
|
||||
fromByteArray(
|
||||
securityConfig?.adminKey?.at(0) ?? new Uint8Array(0),
|
||||
),
|
||||
fromByteArray(
|
||||
securityConfig?.adminKey?.at(1) ?? new Uint8Array(0),
|
||||
),
|
||||
fromByteArray(
|
||||
securityConfig?.adminKey?.at(2) ?? new Uint8Array(0),
|
||||
),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const formMethods = useForm<RawSecurity>({
|
||||
mode: "onChange",
|
||||
defaultValues: defaultValues as DefaultValues<RawSecurity>,
|
||||
resolver: createZodResolver(RawSecuritySchema),
|
||||
shouldFocusError: false,
|
||||
resetOptions: { keepDefaultValues: true },
|
||||
});
|
||||
const { setValue, formState } = formMethods;
|
||||
|
||||
useEffect(() => {
|
||||
onFormInit?.(formMethods);
|
||||
}, [onFormInit, formMethods]);
|
||||
|
||||
const [privateKeyDialogOpen, setPrivateKeyDialogOpen] = useState<boolean>(
|
||||
false,
|
||||
);
|
||||
const [managedModeDialogOpen, setManagedModeDialogOpen] = useState<boolean>(
|
||||
false,
|
||||
);
|
||||
|
||||
const onSubmit = (data: RawSecurity) => {
|
||||
if (!formState.isReady) return;
|
||||
|
||||
const payload: ParsedSecurity = {
|
||||
...data,
|
||||
privateKey: toByteArray(keyState.privateKey),
|
||||
publicKey: toByteArray(keyState.publicKey),
|
||||
privateKey: toByteArray(data.privateKey),
|
||||
publicKey: toByteArray(data.publicKey),
|
||||
adminKey: [
|
||||
toByteArray(data.adminKey.at(0) ?? ""),
|
||||
toByteArray(data.adminKey.at(1) ?? ""),
|
||||
@@ -45,6 +94,11 @@ export const Security = () => {
|
||||
],
|
||||
};
|
||||
|
||||
if (deepCompareConfig(config.security, payload, true)) {
|
||||
removeWorkingConfig("security");
|
||||
return;
|
||||
}
|
||||
|
||||
setWorkingConfig(
|
||||
create(Protobuf.Config.ConfigSchema, {
|
||||
payloadVariant: {
|
||||
@@ -54,18 +108,10 @@ export const Security = () => {
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const pkiRegenerate = () => {
|
||||
const privateKey = getX25519PrivateKey();
|
||||
|
||||
updatePublicKey(fromByteArray(privateKey));
|
||||
|
||||
setKeyState((prev) => ({
|
||||
...prev,
|
||||
privateKey: fromByteArray(privateKey),
|
||||
privateKeyDialogOpen: false,
|
||||
}));
|
||||
|
||||
removeError("privateKey");
|
||||
};
|
||||
|
||||
const updatePublicKey = (privateKey: string) => {
|
||||
@@ -73,18 +119,14 @@ export const Security = () => {
|
||||
const publicKey = fromByteArray(
|
||||
getX25519PublicKey(toByteArray(privateKey)),
|
||||
);
|
||||
setKeyState((prev) => ({
|
||||
...prev,
|
||||
privateKey: privateKey,
|
||||
publicKey: publicKey,
|
||||
}));
|
||||
setValue("privateKey", privateKey);
|
||||
setValue("publicKey", publicKey);
|
||||
|
||||
removeError("privateKey");
|
||||
removeError("publicKey");
|
||||
setPrivateKeyDialogOpen(false);
|
||||
} catch (_e) {
|
||||
setKeyState((prev) => ({
|
||||
...prev,
|
||||
privateKey: privateKey,
|
||||
}));
|
||||
setValue("privateKey", privateKey);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,31 +141,9 @@ export const Security = () => {
|
||||
return (
|
||||
<>
|
||||
<DynamicForm<RawSecurity>
|
||||
propMethods={formMethods}
|
||||
onSubmit={onSubmit}
|
||||
validationSchema={RawSecuritySchema}
|
||||
formId="Config_SecurityConfig"
|
||||
defaultValues={{
|
||||
...config.security,
|
||||
...{
|
||||
privateKey: fromByteArray(
|
||||
config?.security?.privateKey ?? new Uint8Array(0),
|
||||
),
|
||||
publicKey: fromByteArray(
|
||||
config?.security?.publicKey ?? new Uint8Array(0),
|
||||
),
|
||||
adminKey: [
|
||||
fromByteArray(
|
||||
config?.security?.adminKey.at(0) ?? new Uint8Array(0),
|
||||
),
|
||||
fromByteArray(
|
||||
config?.security?.adminKey.at(1) ?? new Uint8Array(0),
|
||||
),
|
||||
fromByteArray(
|
||||
config?.security?.adminKey.at(2) ?? new Uint8Array(0),
|
||||
),
|
||||
],
|
||||
},
|
||||
}}
|
||||
fieldGroups={[
|
||||
{
|
||||
label: t("security.title"),
|
||||
@@ -144,11 +164,7 @@ export const Security = () => {
|
||||
actionButtons: [
|
||||
{
|
||||
text: t("button.generate"),
|
||||
onClick: () =>
|
||||
setKeyState((prev) => ({
|
||||
...prev,
|
||||
privateKeyDialogOpen: true,
|
||||
})),
|
||||
onClick: () => setPrivateKeyDialogOpen(true),
|
||||
variant: "success",
|
||||
},
|
||||
{
|
||||
@@ -160,8 +176,6 @@ export const Security = () => {
|
||||
properties: {
|
||||
showCopyButton: true,
|
||||
showPasswordToggle: true,
|
||||
|
||||
value: keyState.privateKey,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -172,7 +186,6 @@ export const Security = () => {
|
||||
description: t("security.publicKey.description"),
|
||||
properties: {
|
||||
showCopyButton: true,
|
||||
value: keyState.publicKey,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -240,6 +253,13 @@ export const Security = () => {
|
||||
name: "isManaged",
|
||||
label: t("security.managed.label"),
|
||||
description: t("security.managed.description"),
|
||||
inputChange: (checked) => {
|
||||
if (checked) {
|
||||
setManagedModeDialogOpen(true);
|
||||
}
|
||||
|
||||
setValue("isManaged", false);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "toggle",
|
||||
@@ -275,14 +295,19 @@ export const Security = () => {
|
||||
title: t("pkiRegenerate.title"),
|
||||
description: t("pkiRegenerate.description"),
|
||||
}}
|
||||
open={keyState.privateKeyDialogOpen}
|
||||
onOpenChange={() =>
|
||||
setKeyState((prev) => ({
|
||||
...prev,
|
||||
privateKeyDialogOpen: false,
|
||||
}))}
|
||||
open={privateKeyDialogOpen}
|
||||
onOpenChange={() => setPrivateKeyDialogOpen((prev) => !prev)}
|
||||
onSubmit={pkiRegenerate}
|
||||
/>
|
||||
|
||||
<ManagedModeDialog
|
||||
open={managedModeDialogOpen}
|
||||
onOpenChange={() => setManagedModeDialogOpen((prev) => !prev)}
|
||||
onSubmit={() => {
|
||||
setValue("isManaged", true);
|
||||
setManagedModeDialogOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user