From d82698d550343b38f53c2822e0baa7e819125450 Mon Sep 17 00:00:00 2001 From: ChaotiCryptidz Date: Wed, 19 Jan 2022 16:53:37 +0000 Subject: [PATCH] add ability to change key/value view syntax --- src/settings/Settings.ts | 30 +++++ src/translations/en.js | 14 ++- .../pages/Secrets/KeyValue/KeyValueEdit.tsx | 65 ++-------- .../pages/Secrets/KeyValue/KeyValueView.tsx | 50 ++++++-- .../pages/Settings/KeyValueEditorSettings.tsx | 54 --------- src/ui/pages/Settings/KeyValueSettings.tsx | 114 ++++++++++++++++++ src/ui/pages/Settings/Settings.tsx | 4 +- src/ui/pages/Unseal.tsx | 2 +- src/utils/dataInterchange.ts | 38 ++++++ 9 files changed, 247 insertions(+), 124 deletions(-) delete mode 100644 src/ui/pages/Settings/KeyValueEditorSettings.tsx create mode 100644 src/ui/pages/Settings/KeyValueSettings.tsx create mode 100644 src/utils/dataInterchange.ts diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 138ad21..b49e8bc 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -65,6 +65,16 @@ export class Settings { this.alertChange("theme"); } + get kvAlwaysCodeView(): boolean { + const value = this.storage.getItem("kvAlwaysCodeView") || false; + return value == "true"; + } + + set kvAlwaysCodeView(value: boolean) { + this.storage.setItem("kvAlwaysCodeView", String(value)); + this.alertChange("kvAlwaysCodeView"); + } + get kvEditorDefaultLanguage(): string { return this.storage.getItem("kvEditorDefaultLanguage") || "yaml"; } @@ -84,4 +94,24 @@ export class Settings { this.storage.setItem("kvEditorIndent", String(value)); this.alertChange("kvEditorIndent"); } + + get kvViewDefaultLanguage(): string { + return this.storage.getItem("kvViewDefaultLanguage") || "yaml"; + } + + set kvViewDefaultLanguage(value: string) { + this.storage.setItem("kvViewDefaultLanguage", value); + this.alertChange("kvViewDefaultLanguage"); + } + + get kvViewIndent(): number { + const value = this.storage.getItem("kvViewIndent"); + if (value) return parseInt(value); + return 2; + } + + set kvViewIndent(value: number) { + this.storage.setItem("kvViewIndent", String(value)); + this.alertChange("kvViewIndent"); + } } diff --git a/src/translations/en.js b/src/translations/en.js index 07b47ed..f922832 100644 --- a/src/translations/en.js +++ b/src/translations/en.js @@ -81,10 +81,13 @@ module.exports = { settings_general_page_direction_ltr: "Left to Right", settings_general_page_direction_rtl: "Right to Left", - // Key/Value Editor Settings - settings_kveditor_title: "Key/Value Editor", - settings_kveditor_default_language: "Default Data Interchange Format", - settings_kveditor_default_indent: "Default Indent", + // Key/Value Settings + settings_kv_title: "Key/Value", + settings_kv_default_view_language: "Default View Syntax", + settings_kv_default_view_indent: "Default View Indent", + settings_kv_default_editor_language: "Default Editor Syntax", + settings_kv_default_editor_indent: "Default Editor Indent", + settings_kv_always_view_in_code_mode: "Always view in code mode", // Set Vault URL Page set_vault_url_title: "Set Vault URL", @@ -163,12 +166,13 @@ module.exports = { kv_secret_delete_all_btn: "Delete All Versions", kv_secret_delete_version_btn: "Delete Version {{ version }}", kv_secret_versions_btn: "Versions", + kv_secret_syntax: "Syntax", // Key Value Secret Editor Page kv_sec_edit_title: "K/V Edit", kv_sec_edit_syntax: "Syntax", kv_sec_edit_loading: "Loading Editor..", - kv_sec_edit_invalid_json_err: "Invalid JSON", + kv_sec_edit_invalid_data_err: "Invalid Data", kv_sec_edit_suffix: " (edit)", // Key Value Secret Versions Page diff --git a/src/ui/pages/Secrets/KeyValue/KeyValueEdit.tsx b/src/ui/pages/Secrets/KeyValue/KeyValueEdit.tsx index cfb9613..4b95a89 100644 --- a/src/ui/pages/Secrets/KeyValue/KeyValueEdit.tsx +++ b/src/ui/pages/Secrets/KeyValue/KeyValueEdit.tsx @@ -4,48 +4,15 @@ import { Component, JSX, createRef } from "preact"; import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { InputWithTitle } from "../../../elements/InputWithTitle"; import { SecretTitleElement } from "../SecretTitleElement"; +import { + SupportedLanguages, + dumpData, + parseData, + toPrismCode, +} from "../../../../utils/dataInterchange"; import { setErrorText } from "../../../../pageUtils"; import { sortedObjectMap } from "../../../../utils"; -import JSON5 from "json5"; import i18next from "i18next"; -import yaml from "js-yaml"; - -export const SupportedEditorLanguages = [ - { name: "json", readable: "JSON" }, - { name: "json5", readable: "JSON5" }, - { name: "yaml", readable: "YAML" }, -]; - -function parseData(data: string, syntax = "json"): Record { - if (syntax == "json") { - return JSON.parse(data) as Record; - } else if (syntax == "json5") { - return JSON5.parse(data); - } else if (syntax == "yaml") { - return yaml.load(data) as Record; - } -} - -function dumpData(data: Record, space = 4, syntax = "json"): string { - if (syntax == "json") { - return JSON.stringify(data, null, space); - } else if (syntax == "json5") { - return JSON5.stringify(data, null, space); - } else if (syntax == "yaml") { - return yaml.dump(data, { indent: space }); - } -} - -export function validateData(str: string, syntax = "json"): boolean { - try { - parseData(str, syntax); - } catch (e) { - const err = e as Error; - setErrorText(err.message); - return false; - } - return true; -} export type KVEditProps = DefaultPageProps & { baseMount: string; @@ -78,8 +45,10 @@ export class KVEditor extends Component { if (!this.state.dataLoaded) return; const editorContent = this.state.code; - if (!validateData(editorContent, this.state.syntax)) { - setErrorText(i18next.t("kv_sec_edit_invalid_json_err")); + try { + parseData(editorContent, this.state.syntax); + } catch { + setErrorText(i18next.t("kv_sec_edit_invalid_data_err")); return; } @@ -133,16 +102,6 @@ export class KVEditor extends Component { return

{i18next.t("content_loading")}

; } - let codeEditorLanguage: string; - - if (this.state.syntax == "json") { - codeEditorLanguage = "json"; - } else if (this.state.syntax == "json5") { - codeEditorLanguage = "json5"; - } else if (this.state.syntax == "yaml") { - codeEditorLanguage = "yaml"; - } - return (
@@ -153,7 +112,7 @@ export class KVEditor extends Component { this.setState({ syntax: this.syntaxSelectRef.current.value }); }} > - {SupportedEditorLanguages.map((lang) => { + {SupportedLanguages.map((lang) => { return (

this.onCodeUpdate(code)} diff --git a/src/ui/pages/Secrets/KeyValue/KeyValueView.tsx b/src/ui/pages/Secrets/KeyValue/KeyValueView.tsx index 9eaf1b4..4838c39 100644 --- a/src/ui/pages/Secrets/KeyValue/KeyValueView.tsx +++ b/src/ui/pages/Secrets/KeyValue/KeyValueView.tsx @@ -1,31 +1,63 @@ import { Button } from "../../../elements/Button"; import { CodeBlock } from "../../../elements/CodeBlock"; -import { Component, JSX } from "preact"; +import { Component, JSX, createRef } from "preact"; import { CopyableInputBox } from "../../../elements/CopyableInputBox"; import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DoesNotExistError } from "../../../../types/internalErrors"; import { Grid, GridSizes } from "../../../elements/Grid"; +import { InputWithTitle } from "../../../elements/InputWithTitle"; import { SecretTitleElement } from "../SecretTitleElement"; +import { SupportedLanguages, dumpData, toPrismCode } from "../../../../utils/dataInterchange"; import { kvDeleteURL, kvEditURL, kvVersionsURL } from "../../pageLinks"; import { sortedObjectMap } from "../../../../utils"; import i18next from "i18next"; -export type KVSecretViewProps = { +export type KVSecretViewProps = DefaultPageProps & { kvData: Record; }; -export class KVSecretVew extends Component { +export class KVSecretVew extends Component { + syntaxSelectRef = createRef(); + render(): JSX.Element { const secretsMap = sortedObjectMap(this.props.kvData); - let isMultiLevelJSON = false; + let isMultiLevel = false; for (const value of secretsMap.values()) { - if (typeof value == "object") isMultiLevelJSON = true; + if (typeof value == "object") isMultiLevel = true; } - if (isMultiLevelJSON) { - const jsonText = JSON.stringify(Object.fromEntries(secretsMap), null, 4); - return ; + if (isMultiLevel || this.props.settings.kvAlwaysCodeView) { + const syntax = this.state.syntax || this.props.settings.kvViewDefaultLanguage; + const secretData = Object.fromEntries(secretsMap); + const codeData = dumpData(secretData, this.props.settings.kvViewIndent, syntax); + return ( + <> + + + + + + ); } else { return ( <> @@ -161,7 +193,7 @@ export class KeyValueView extends Component )}

- {!this.state.isDeleted && } + {!this.state.isDeleted && } {this.state.isDeleted && ( <> diff --git a/src/ui/pages/Settings/KeyValueEditorSettings.tsx b/src/ui/pages/Settings/KeyValueEditorSettings.tsx deleted file mode 100644 index 5836b60..0000000 --- a/src/ui/pages/Settings/KeyValueEditorSettings.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, createRef } from "preact"; -import { DefaultPageProps } from "../../../types/DefaultPageProps"; -import { InputWithTitle } from "../../elements/InputWithTitle"; -import { SupportedEditorLanguages } from "../Secrets/KeyValue/KeyValueEdit"; -import { settingsSavedNotification } from "./Settings"; -import i18next from "i18next"; - -export class KeyValueEditorSettings extends Component { - syntaxSelectRef = createRef(); - indentInputRef = createRef(); - - render() { - return ( -
-

{i18next.t("settings_kveditor_title")}

- - - - - - { - const value = this.indentInputRef.current?.value; - const indent = parseInt(value); - this.props.settings.kvEditorIndent = indent; - settingsSavedNotification(); - }} - /> - -
- ); - } -} diff --git a/src/ui/pages/Settings/KeyValueSettings.tsx b/src/ui/pages/Settings/KeyValueSettings.tsx new file mode 100644 index 0000000..e3dd8af --- /dev/null +++ b/src/ui/pages/Settings/KeyValueSettings.tsx @@ -0,0 +1,114 @@ +import { Component, createRef } from "preact"; +import { DefaultPageProps } from "../../../types/DefaultPageProps"; +import { InputWithTitle } from "../../elements/InputWithTitle"; +import { SupportedLanguages } from "../../../utils/dataInterchange"; +import { settingsSavedNotification } from "./Settings"; +import i18next from "i18next"; + +export class KeyValueSettings extends Component { + editorSyntaxSelectRef = createRef(); + editorIndentInputRef = createRef(); + viewSyntaxSelectRef = createRef(); + viewIndentInputRef = createRef(); + codeModeToggleRef = createRef(); + + render() { + return ( +
+

{i18next.t("settings_kv_title")}

+ + {/* KV View Language */} + + + + + {/* KV View Indent */} + + { + const value = this.viewIndentInputRef.current.value; + const indent = parseInt(value); + this.props.settings.kvViewIndent = indent; + settingsSavedNotification(); + }} + /> + + + {/* KV Editor Language */} + + + + + {/* KV Editor Indent */} + + { + const value = this.editorIndentInputRef.current.value; + const indent = parseInt(value); + this.props.settings.kvEditorIndent = indent; + settingsSavedNotification(); + }} + /> + + + {/* Always view in code mode */} + + { + const value = this.codeModeToggleRef.current.checked; + this.props.settings.kvAlwaysCodeView = value; + settingsSavedNotification(); + }} + /> + +
+ ); + } +} diff --git a/src/ui/pages/Settings/Settings.tsx b/src/ui/pages/Settings/Settings.tsx index b3146fb..4bae108 100644 --- a/src/ui/pages/Settings/Settings.tsx +++ b/src/ui/pages/Settings/Settings.tsx @@ -2,7 +2,7 @@ import { Component } from "preact"; import { DefaultPageProps } from "../../../types/DefaultPageProps"; import { GeneralSettings } from "./GeneralSettings"; import { Grid, GridSizes } from "../../elements/Grid"; -import { KeyValueEditorSettings } from "./KeyValueEditorSettings"; +import { KeyValueSettings } from "./KeyValueSettings"; import { PageTitle } from "../../elements/PageTitle"; import UIkit from "uikit"; import i18next from "i18next"; @@ -21,7 +21,7 @@ export class Settings extends Component { - + ); diff --git a/src/ui/pages/Unseal.tsx b/src/ui/pages/Unseal.tsx index 41e2cc4..fc0e349 100644 --- a/src/ui/pages/Unseal.tsx +++ b/src/ui/pages/Unseal.tsx @@ -91,7 +91,7 @@ export class Unseal extends Component { const timer = setInterval(() => { this.updateStateWithSealStatus(); }, 500) as unknown as number; - this.setState({timer: timer}); + this.setState({ timer: timer }); } render(): JSX.Element { diff --git a/src/utils/dataInterchange.ts b/src/utils/dataInterchange.ts new file mode 100644 index 0000000..d05d288 --- /dev/null +++ b/src/utils/dataInterchange.ts @@ -0,0 +1,38 @@ +import JSON5 from "json5"; +import yaml from "js-yaml"; + +export const SupportedLanguages = [ + { name: "json", readable: "JSON" }, + { name: "json5", readable: "JSON5" }, + { name: "yaml", readable: "YAML" }, +]; + +export const LanguagePrismBlockMap: Record = { + json: "json", + json5: "json5", + yaml: "yaml", +}; + +export function toPrismCode(syntax: string): string { + return LanguagePrismBlockMap[syntax]; +} + +export function parseData(data: string, syntax = "json"): Record { + if (syntax == "json") { + return JSON.parse(data) as Record; + } else if (syntax == "json5") { + return JSON5.parse(data); + } else if (syntax == "yaml") { + return yaml.load(data) as Record; + } +} + +export function dumpData(data: Record, space = 4, syntax = "json"): string { + if (syntax == "json") { + return JSON.stringify(data, null, space); + } else if (syntax == "json5") { + return JSON5.stringify(data, null, space); + } else if (syntax == "yaml") { + return yaml.dump(data, { indent: space }); + } +}