212 lines
5.7 KiB
TypeScript
212 lines
5.7 KiB
TypeScript
import { Button } from "../../../elements/Button";
|
|
import { Component, JSX, createRef } from "preact";
|
|
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
|
|
import { DoesNotExistError } from "../../../../types/internalErrors";
|
|
import { Margin } from "../../../elements/Margin";
|
|
import { SecretTitleElement } from "../SecretTitleElement";
|
|
import { TextInput } from "../../../elements/forms/TextInput";
|
|
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
|
|
import { route } from "preact-router";
|
|
import { sendErrorNotification } from "../../../elements/ErrorMessage";
|
|
import i18next from "i18next";
|
|
|
|
export type KVKeysListProps = DefaultPageProps & {
|
|
baseMount: string;
|
|
secretPath: string[];
|
|
};
|
|
|
|
type KVKeysListState = {
|
|
dataLoaded: boolean;
|
|
keys: string[];
|
|
searchQuery: string;
|
|
};
|
|
|
|
function SecretsList(baseMount: string, secretPath: string[], secrets: string[]): JSX.Element[] {
|
|
return secrets.map((secret) => (
|
|
<li>
|
|
<a
|
|
onClick={async () => {
|
|
if (secret.endsWith("/")) {
|
|
route(kvListURL(baseMount, [...secretPath, secret.replace("/", "")]));
|
|
} else {
|
|
route(kvViewURL(baseMount, secretPath, secret));
|
|
}
|
|
}}
|
|
>
|
|
{secret}
|
|
</a>
|
|
</li>
|
|
));
|
|
}
|
|
|
|
export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
|
|
constructor() {
|
|
super();
|
|
this.state = {
|
|
dataLoaded: false,
|
|
keys: [],
|
|
searchQuery: "",
|
|
};
|
|
}
|
|
|
|
async componentDidMount() {
|
|
try {
|
|
await this.loadData();
|
|
} catch (e: unknown) {
|
|
const error = e as Error;
|
|
sendErrorNotification(error.message);
|
|
}
|
|
}
|
|
|
|
async loadData(): Promise<void> {
|
|
try {
|
|
const keys = await this.props.api.getSecrets(this.props.baseMount, this.props.secretPath);
|
|
this.setState({
|
|
dataLoaded: true,
|
|
keys: keys,
|
|
});
|
|
return;
|
|
} catch (e: unknown) {
|
|
const error = e as Error;
|
|
if (error == DoesNotExistError) {
|
|
// getSecrets also 404's on no keys so dont go all the way back.
|
|
if (this.props.secretPath.length != 0) {
|
|
window.history.back();
|
|
return;
|
|
}
|
|
} else {
|
|
throw error;
|
|
}
|
|
|
|
this.setState({
|
|
dataLoaded: true,
|
|
keys: null,
|
|
});
|
|
}
|
|
}
|
|
|
|
componentDidUpdate(prevProps: KVKeysListProps): void {
|
|
if (
|
|
prevProps.baseMount !== this.props.baseMount ||
|
|
prevProps.secretPath !== this.props.secretPath
|
|
) {
|
|
this.setState({
|
|
dataLoaded: false,
|
|
});
|
|
void this.loadData();
|
|
}
|
|
}
|
|
|
|
searchBarRef = createRef<HTMLInputElement>();
|
|
|
|
render(): JSX.Element {
|
|
if (!this.state.dataLoaded) {
|
|
return <p>{i18next.t("content_loading")}</p>;
|
|
}
|
|
|
|
if (this.state.keys == null) {
|
|
return <p>{i18next.t("kv_view_none_here_text")}</p>;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Margin>
|
|
<TextInput
|
|
inputRef={this.searchBarRef}
|
|
name="path"
|
|
placeholder={i18next.t("kv_view_search_input_text")}
|
|
onInput={async () => {
|
|
this.setState({
|
|
searchQuery: this.searchBarRef.current.value,
|
|
});
|
|
}}
|
|
/>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<ul class="uk-nav">
|
|
{...((): JSX.Element[] => {
|
|
let secrets: string[] = this.state.keys;
|
|
if (this.state.searchQuery.length > 0) {
|
|
secrets = secrets.filter((secret) => secret.includes(this.state.searchQuery));
|
|
}
|
|
return SecretsList(this.props.baseMount, this.props.secretPath, secrets);
|
|
})()}
|
|
</ul>
|
|
</Margin>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
type KeyValueListState = {
|
|
pathCaps: string[];
|
|
mountCaps: string[];
|
|
mountType: string;
|
|
};
|
|
|
|
export class KeyValueList extends Component<DefaultPageProps, KeyValueListState> {
|
|
async componentDidMount() {
|
|
const baseMount = this.props.matches["baseMount"];
|
|
const secretPath = this.props.matches["secretPath"].split("/").filter((e) => e.length > 0);
|
|
|
|
const mountsPath = "/sys/mounts/" + baseMount;
|
|
const currentPath = baseMount + secretPath.join();
|
|
|
|
try {
|
|
const caps = await this.props.api.getCapabilitiesPath([mountsPath, currentPath]);
|
|
const mount = await this.props.api.getMount(baseMount);
|
|
|
|
this.setState({
|
|
mountCaps: caps[mountsPath],
|
|
pathCaps: caps[currentPath],
|
|
mountType: mount.type,
|
|
});
|
|
} catch (e: unknown) {
|
|
const error = e as Error;
|
|
sendErrorNotification(error.message);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
if (!this.state.pathCaps) return;
|
|
|
|
const baseMount = this.props.matches["baseMount"];
|
|
const secretPath = this.props.matches["secretPath"].split("/");
|
|
|
|
return (
|
|
<>
|
|
<SecretTitleElement
|
|
type="kv"
|
|
baseMount={baseMount}
|
|
secretPath={secretPath}
|
|
item={this.props.matches["item"]}
|
|
/>
|
|
<p>
|
|
{this.state.pathCaps.includes("create") && (
|
|
<Button
|
|
text={i18next.t("kv_view_new_btn")}
|
|
color="primary"
|
|
route={kvNewURL(baseMount, secretPath)}
|
|
/>
|
|
)}
|
|
{secretPath.length == 0 && this.state.mountCaps.includes("delete") && (
|
|
<Button
|
|
text={i18next.t("kv_view_delete_btn")}
|
|
color="danger"
|
|
route={delSecretsEngineURL(baseMount)}
|
|
/>
|
|
)}
|
|
</p>
|
|
{this.state.mountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
|
|
<KVKeysList
|
|
settings={this.props.settings}
|
|
api={this.props.api}
|
|
baseMount={baseMount}
|
|
secretPath={secretPath}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
}
|