1
0
Fork 0

remove setErrorText and replace it with proper error text & notifs

This commit is contained in:
ChaotiCryptidz 2022-01-21 21:56:09 +00:00
parent 886ca8a240
commit 0695adec71
45 changed files with 695 additions and 333 deletions

View file

@ -60,23 +60,3 @@ export function addClipboardNotifications(clipboard: ClipboardJS, timeout = 1000
);
});
}
export function setErrorText(text: string): void {
const errorTextElement = document.querySelector("#errorText");
if (errorTextElement) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const p = document.querySelector("#errorText") as HTMLParagraphElement;
p.innerText = `Error: ${text}`;
/* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */
}
UIkit.notification({
message: `Error: ${text}`,
status: "danger",
pos: "top-center",
timeout: 2000,
});
}
export function notImplemented(): void {
setErrorText(i18next.t("not_implemented"));
}

View file

@ -0,0 +1,63 @@
import { Component, createRef } from "preact";
import UIkit from "uikit";
import i18next from "i18next";
type ErrorMessageState = {
errorMessage: string;
};
// Use only when a error text element can't be used
// e.g inside componentDidMount
export function sendErrorNotification(errorMessage: string) {
UIkit.notification({
message: `Error: ${errorMessage}`,
status: "danger",
pos: "top-center",
timeout: 2000,
});
}
export function notImplementedNotification(): void {
sendErrorNotification(i18next.t("not_implemented"));
}
export class ErrorMessage extends Component<unknown, ErrorMessageState> {
public setErrorMessage(errorMessage: string) {
this.setState({
errorMessage: `Error: ${errorMessage}`,
});
sendErrorNotification(errorMessage);
// make browser focus on the change.
this.errorMessageRef.current.focus();
}
public clear() {
this.setState({ errorMessage: "" });
}
errorMessageRef = createRef<HTMLParagraphElement>();
render() {
return (
<p
ref={this.errorMessageRef}
{
...[] /* keeping for backwards compatability with seterrorMessage*/
}
{
...[] /* TODO: remove when finished removing all references to seterrorMessage */
}
id="errorMessage"
class="uk-text-danger"
{
...[] /* makes screenreaders read out changes to this element's content*/
}
aria-live="assertive"
>
{this.state.errorMessage || ""}
</p>
);
}
}

View file

@ -3,7 +3,7 @@ import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { Grid, GridSizes } from "../../elements/Grid";
import { PageTitle } from "../../elements/PageTitle";
import { Tile } from "../../elements/Tile";
import { notImplemented } from "../../../pageUtils";
import { notImplementedNotification } from "../../elements/ErrorMessage";
import { route } from "preact-router";
import i18next from "i18next";
@ -23,19 +23,19 @@ export class AccessHomePage extends Component<DefaultPageProps> {
title={i18next.t("access_entities_title")}
description={i18next.t("access_entities_description")}
icon="user"
onclick={async () => notImplemented()}
onclick={async () => notImplementedNotification()}
/>
<Tile
title={i18next.t("access_groups_title")}
description={i18next.t("access_groups_description")}
icon="users"
onclick={async () => notImplemented()}
onclick={async () => notImplementedNotification()}
/>
<Tile
title={i18next.t("access_leases_title")}
description={i18next.t("access_leases_description")}
icon="unlock"
onclick={async () => notImplemented()}
onclick={async () => notImplementedNotification()}
/>
</Grid>
</>

View file

@ -4,7 +4,7 @@ import { Component, JSX } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { PageTitle } from "../../../elements/PageTitle";
import { authViewConfigURL, userPassUserListURL } from "../../pageLinks";
import { notImplemented } from "../../../../pageUtils";
import { notImplementedNotification, sendErrorNotification } from "../../../elements/ErrorMessage";
import { objectToMap } from "../../../../utils";
import { route } from "preact-router";
import i18next from "i18next";
@ -46,7 +46,7 @@ export function AuthListElement(props: AuthListElementProps): JSX.Element {
<Button
text={i18next.t("auth_home_edit_config")}
color="primary"
onClick={notImplemented}
onClick={notImplementedNotification}
/>
</div>
</div>
@ -55,8 +55,13 @@ export function AuthListElement(props: AuthListElementProps): JSX.Element {
export class AuthHome extends Component<DefaultPageProps, { authList: Map<string, AuthMethod> }> {
async componentDidMount() {
const authList = objectToMap(await this.props.api.listAuth()) as Map<string, AuthMethod>;
this.setState({ authList });
try {
const authList = objectToMap(await this.props.api.listAuth()) as Map<string, AuthMethod>;
this.setState({ authList });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
if (!this.state.authList) return;

View file

@ -4,18 +4,27 @@ import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { HeaderAndContent } from "../../../elements/HeaderAndContent";
import { PageTitle } from "../../../elements/PageTitle";
import { objectToMap, toStr } from "../../../../utils";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next";
export class AuthViewConfig extends Component<DefaultPageProps, { authMethod: AuthMethod }> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const authList = objectToMap(await this.props.api.listAuth()) as Map<string, AuthMethod>;
const authMethod = authList.get(baseMount + "/");
this.setState({ authMethod: authMethod });
try {
const authList = objectToMap(await this.props.api.listAuth()) as Map<string, AuthMethod>;
const authMethod = authList.get(baseMount + "/");
this.setState({ authMethod: authMethod });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
if (!this.state.authMethod) return;
const baseMount = this.props.matches["baseMount"];
const authMethod = this.state.authMethod;
return (

View file

@ -1,12 +1,16 @@
import { Button } from "../../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../../elements/ErrorMessage";
import { Margin } from "../../../../elements/Margin";
import { PageTitle } from "../../../../elements/PageTitle";
import { route } from "preact-router";
import { userPassUserListURL } from "../../../pageLinks";
import i18next from "i18next";
export class UserPassUserDelete extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
const baseMount = this.props.matches["baseMount"];
const user = this.props.matches["user"];
@ -16,12 +20,22 @@ export class UserPassUserDelete extends Component<DefaultPageProps> {
<PageTitle title={i18next.t("userpass_user_delete_title")} />
<div>
<h5>{i18next.t("userpass_user_delete_text")}</h5>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button
text={i18next.t("common_delete")}
color="danger"
onClick={async () => {
await this.props.api.deleteUserPassUser(baseMount, user);
route(userPassUserListURL(baseMount));
try {
await this.props.api.deleteUserPassUser(baseMount, user);
route(userPassUserListURL(baseMount));
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}}
/>
</div>

View file

@ -1,6 +1,7 @@
import { Button } from "../../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../../../elements/ErrorMessage";
import { Form } from "../../../../elements/Form";
import { InputWithTitle } from "../../../../elements/InputWithTitle";
import { Margin } from "../../../../elements/Margin";
@ -8,7 +9,6 @@ import { MarginInline } from "../../../../elements/MarginInline";
import { PageTitle } from "../../../../elements/PageTitle";
import { UserType } from "../../../../../api/types/user";
import { route } from "preact-router";
import { setErrorText } from "../../../../../pageUtils";
import { toStr } from "../../../../../utils";
import { userPassUserViewURL } from "../../../pageLinks";
import i18next from "i18next";
@ -16,12 +16,19 @@ import i18next from "i18next";
const removeEmptyStrings = (arr: string[]) => arr.filter((e) => e.length > 0);
export class UserPassUserEdit extends Component<DefaultPageProps, { user_data: UserType }> {
errorMessageRef = createRef<ErrorMessage>();
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const user = this.props.matches["user"];
const user_data = await this.props.api.getUserPassUser(baseMount, user);
this.setState({ user_data });
try {
const user_data = await this.props.api.getUserPassUser(baseMount, user);
this.setState({ user_data });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
@ -111,7 +118,11 @@ export class UserPassUserEdit extends Component<DefaultPageProps, { user_data: U
value={toStr(user_data.token_ttl)}
/>
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_edit")} color="primary" type="submit" />
</MarginInline>
@ -134,7 +145,9 @@ export class UserPassUserEdit extends Component<DefaultPageProps, { user_data: U
token_policies: removeEmptyStrings(String(data.get("policies")).split(",")),
token_ttl: parseInt(data.get("initial_ttl") as string, 10),
};
const password = data.get("password") as string;
if (password.length > 0) {
apiData.password = password;
}
@ -144,7 +157,7 @@ export class UserPassUserEdit extends Component<DefaultPageProps, { user_data: U
route(userPassUserViewURL(baseMount, user));
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,17 +1,19 @@
import { Button } from "../../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../../elements/ErrorMessage";
import { Form } from "../../../../elements/Form";
import { Margin } from "../../../../elements/Margin";
import { MarginInline } from "../../../../elements/MarginInline";
import { PageTitle } from "../../../../elements/PageTitle";
import { UserType } from "../../../../../api/types/user";
import { route } from "preact-router";
import { setErrorText } from "../../../../../pageUtils";
import { userPassUserViewURL } from "../../../pageLinks";
import i18next from "i18next";
export class UserPassUserNew extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -33,7 +35,11 @@ export class UserPassUserNew extends Component<DefaultPageProps> {
placeholder={i18next.t("common_password")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline>
@ -56,7 +62,7 @@ export class UserPassUserNew extends Component<DefaultPageProps> {
route(userPassUserViewURL(baseMount, data.get("username") as string));
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -5,6 +5,7 @@ import { HeaderAndContent } from "../../../../elements/HeaderAndContent";
import { Margin } from "../../../../elements/Margin";
import { PageTitle } from "../../../../elements/PageTitle";
import { UserType } from "../../../../../api/types/user";
import { sendErrorNotification } from "../../../../elements/ErrorMessage";
import { toStr } from "../../../../../utils";
import { userPassUserDeleteURL, userPassUserEditURL } from "../../../pageLinks";
import i18next from "i18next";
@ -14,14 +15,20 @@ export class UserPassUserView extends Component<DefaultPageProps, { user_data: U
const baseMount = this.props.matches["baseMount"];
const user = this.props.matches["user"];
const user_data = await this.props.api.getUserPassUser(baseMount, user);
this.setState({ user_data });
try {
const user_data = await this.props.api.getUserPassUser(baseMount, user);
this.setState({ user_data });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
if (!this.state.user_data) return;
const baseMount = this.props.matches["baseMount"];
const user = this.props.matches["user"];
const user_data = this.state.user_data;
return (

View file

@ -4,14 +4,21 @@ import { DefaultPageProps } from "../../../../../types/DefaultPageProps";
import { Margin } from "../../../../elements/Margin";
import { PageTitle } from "../../../../elements/PageTitle";
import { route } from "preact-router";
import { sendErrorNotification } from "../../../../elements/ErrorMessage";
import { userPassUserNewURL, userPassUserViewURL } from "../../../pageLinks";
import i18next from "i18next";
export class UserPassUsersList extends Component<DefaultPageProps, { users: string[] }> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const users = await this.props.api.listUserPassUsers(baseMount);
this.setState({ users });
try {
const users = await this.props.api.listUserPassUsers(baseMount);
this.setState({ users });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -5,8 +5,9 @@ import { Margin } from "../elements/Margin";
import { PageTitle } from "../elements/PageTitle";
import { Tile } from "../elements/Tile";
import { TokenInfo } from "../../api/types/token";
import { pageChecks, setErrorText } from "../../pageUtils";
import { pageChecks } from "../../pageUtils";
import { route } from "preact-router";
import { sendErrorNotification } from "../elements/ErrorMessage";
import i18next from "i18next";
type HomeState = {
@ -17,18 +18,31 @@ type HomeState = {
export class Home extends Component<DefaultPageProps, HomeState> {
async componentDidMount() {
// Always call pageChecks on /home
if (await pageChecks("/home", this.props.api, this.props.settings)) return;
try {
await this.populateState();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
async populateState() {
// Check if logged in otherise redirect to /login
let selfTokenInfo: TokenInfo;
try {
selfTokenInfo = await this.props.api.lookupSelf();
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
if (error.message == "permission denied") {
this.props.settings.token = "";
route("/login", true);
return;
} else {
throw error;
}
}

View file

@ -19,7 +19,7 @@ export class Login extends Component<DefaultPageProps> {
<a>{i18next.t("log_in_with_username")}</a>
</li>
</ul>
<p id="errorText" class="uk-text-danger" />
<ul class="uk-switcher uk-margin switcher-container">
<li>
<TokenLoginForm {...this.props} />

View file

@ -1,14 +1,16 @@
import { Button } from "../elements/Button";
import { Component, JSX } from "preact";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin";
import { MarginInline } from "../elements/MarginInline";
import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import i18next from "i18next";
export class TokenLoginForm extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render(): JSX.Element {
return (
<Form onSubmit={(data) => this.onSubmit(data)}>
@ -22,6 +24,11 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
required
/>
</Margin>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("log_in_btn")} color="primary" type="submit" />
</MarginInline>
@ -31,6 +38,9 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
async onSubmit(data: FormData): Promise<void> {
const token = data.get("token");
const previousToken = this.props.settings.token;
this.props.settings.token = token as string;
try {
@ -39,11 +49,18 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
} catch (e: unknown) {
const error = e as Error;
document.querySelector("#tokenInput").classList.add("uk-form-danger");
let errorMessage: string;
if (error.message == "permission denied") {
setErrorText(i18next.t("log_in_token_login_error"));
errorMessage = i18next.t("log_in_token_login_error");
} else {
setErrorText(error.message);
errorMessage = error.message;
}
this.errorMessageRef.current.setErrorMessage(errorMessage);
this.props.settings.token = previousToken;
}
}
}

View file

@ -1,14 +1,16 @@
import { Button } from "../elements/Button";
import { Component, JSX } from "preact";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin";
import { MarginInline } from "../elements/MarginInline";
import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import i18next from "i18next";
export class UsernameLoginForm extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render(): JSX.Element {
return (
<Form onSubmit={(data) => this.onSubmit(data)}>
@ -32,6 +34,11 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
required
/>
</Margin>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("log_in_btn")} color="primary" type="submit" />
</MarginInline>
@ -40,6 +47,8 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
}
async onSubmit(data: FormData): Promise<void> {
const previousToken = this.props.settings.token;
try {
const res = await this.props.api.usernameLogin(
data.get("username") as string,
@ -51,7 +60,8 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
const error = e as Error;
document.querySelector("#usernameInput").classList.add("uk-form-danger");
document.querySelector("#passwordInput").classList.add("uk-form-danger");
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
this.props.settings.token = previousToken;
}
}
}

View file

@ -1,8 +1,10 @@
import { Button } from "../elements/Button";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Margin } from "../elements/Margin";
import { PageTitle } from "../elements/PageTitle";
import { addClipboardNotifications, setErrorText } from "../../pageUtils";
import { addClipboardNotifications } from "../../pageUtils";
import { route } from "preact-router";
import ClipboardJS from "clipboard";
import i18next from "i18next";
@ -36,9 +38,7 @@ export class Me extends Component<DefaultPageProps, MeState> {
this.state = this.defaultState;
}
componentWillUnmount() {
this.setState(this.defaultState);
}
errorMessageRef = createRef<ErrorMessage>();
async componentDidMount() {
let canSealVault = false;
@ -55,13 +55,20 @@ export class Me extends Component<DefaultPageProps, MeState> {
});
}
themeSelectRef = createRef<HTMLSelectElement>();
componentWillUnmount() {
this.setState(this.defaultState);
}
render(): JSX.Element {
return (
this.state.loaded && (
<>
<PageTitle title={i18next.t("me_page_title")} />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<ul class="uk-nav">
<li>
<a
@ -84,7 +91,7 @@ export class Me extends Component<DefaultPageProps, MeState> {
route("/");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}}
>
@ -95,8 +102,13 @@ export class Me extends Component<DefaultPageProps, MeState> {
<li>
<a
onClick={async () => {
await this.props.api.sealVault();
route("/unseal", true);
try {
await this.props.api.sealVault();
route("/unseal", true);
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}}
>
{i18next.t("me_seal_vault_btn")}

View file

@ -5,18 +5,24 @@ import { Margin } from "../../elements/Margin";
import { PageTitle } from "../../elements/PageTitle";
import { policyNewURL, policyViewURL } from "../pageLinks";
import { route } from "preact-router";
import { sendErrorNotification } from "../../elements/ErrorMessage";
import i18next from "i18next";
export class PoliciesHome extends Component<DefaultPageProps, { policies: string[] }> {
async componentDidMount() {
let policies = await this.props.api.getPolicies();
policies = policies.sort();
policies = policies.filter(function (policy_name) {
return policy_name !== "root";
});
this.setState({
policies: policies,
});
try {
let policies = await this.props.api.getPolicies();
policies = policies.sort();
policies = policies.filter(function (policy_name) {
return policy_name !== "root";
});
this.setState({
policies: policies,
});
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -1,12 +1,26 @@
import { Button } from "../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage } from "../../elements/ErrorMessage";
import { Margin } from "../../elements/Margin";
import { PageTitle } from "../../elements/PageTitle";
import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next";
export class PolicyDelete extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
async onDelete() {
const policyName = this.props.matches["policyName"];
try {
await this.props.api.deletePolicy(policyName);
route("/policies");
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
render() {
const policyName = this.props.matches["policyName"];
return (
@ -14,18 +28,15 @@ export class PolicyDelete extends Component<DefaultPageProps> {
<PageTitle title={i18next.t("policy_delete_title", { policy: policyName })} />
<div>
<h5>{i18next.t("policy_delete_text")}</h5>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button
text={i18next.t("common_delete")}
color="danger"
onClick={async () => {
try {
await this.props.api.deletePolicy(policyName);
route("/policies");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}}
onClick={async () => await this.onDelete()}
/>
</div>
</>

View file

@ -1,28 +1,30 @@
import { Button } from "../../elements/Button";
import { CodeEditor } from "../../elements/CodeEditor";
import { Component, JSX } from "preact";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../elements/ErrorMessage";
import { Margin } from "../../elements/Margin";
import { MarginInline } from "../../elements/MarginInline";
import { PageTitle } from "../../elements/PageTitle";
import { policyViewURL } from "../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next";
type PolicyEditorProps = DefaultPageProps & {
policyName: string;
};
type PolicyEditorState =
| {
dataLoaded: false;
}
| {
dataLoaded: true;
policyData: string;
code: string;
};
type PolicyEditorStateUnloaded = {
dataLoaded: false;
};
type PolicyEditorStateLoaded = {
dataLoaded: true;
policyData: string;
code: string;
};
type PolicyEditorState = PolicyEditorStateUnloaded | PolicyEditorStateLoaded;
export class PolicyEditor extends Component<PolicyEditorProps, PolicyEditorState> {
constructor() {
@ -32,6 +34,19 @@ export class PolicyEditor extends Component<PolicyEditorProps, PolicyEditorState
};
}
errorMessageRef = createRef<ErrorMessage>();
async componentDidMount() {
if (!this.state.dataLoaded) {
try {
await this.loadData();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
}
async editorSave(): Promise<void> {
if (!this.state.dataLoaded) return;
@ -40,16 +55,10 @@ export class PolicyEditor extends Component<PolicyEditorProps, PolicyEditorState
route(policyViewURL(this.props.policyName));
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
onCodeUpdate(code: string): void {
this.setState({
code: code,
});
}
async loadData(): Promise<void> {
const policyData = await this.props.api.getPolicy(this.props.policyName);
this.setState({
@ -60,12 +69,6 @@ export class PolicyEditor extends Component<PolicyEditorProps, PolicyEditorState
return;
}
componentDidMount(): void {
if (!this.state.dataLoaded) {
void this.loadData();
}
}
render(): JSX.Element {
if (!this.state.dataLoaded) {
return <p>{i18next.t("content_loading")}</p>;
@ -73,13 +76,18 @@ export class PolicyEditor extends Component<PolicyEditorProps, PolicyEditorState
return (
<div>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Margin>
<CodeEditor
language="hcl"
tabSize={2}
code={this.state.policyData}
onUpdate={(code) => this.onCodeUpdate(code)}
onUpdate={(code) => {
this.setState({ code: code });
}}
/>
</Margin>
<MarginInline>
@ -101,11 +109,7 @@ export class PolicyEdit extends Component<DefaultPageProps> {
<>
<PageTitle title={i18next.t("policy_edit_title", { policy: policyName })} />
<div>
<PolicyEditor
settings={this.props.settings}
api={this.props.api}
policyName={policyName}
/>
<PolicyEditor {...this.props} policyName={policyName} />
</div>
</>
);

View file

@ -1,37 +1,50 @@
import { Button } from "../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage } from "../../elements/ErrorMessage";
import { Form } from "../../elements/Form";
import { Margin } from "../../elements/Margin";
import { PageTitle } from "../../elements/PageTitle";
import { policyViewURL } from "../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next";
export class PolicyNew extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
async onFormSubmit(formData: FormData) {
const name = formData.get("name") as string;
let policies: string[] = [];
try {
policies = await this.props.api.getPolicies();
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
return;
}
if (policies.includes(name)) {
this.errorMessageRef.current.setErrorMessage(i18next.t("policy_new_already_exists"));
return;
}
try {
await this.props.api.createOrUpdatePolicy(name, " ");
route(policyViewURL(name));
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
render() {
return (
<>
<PageTitle title={i18next.t("policy_new_title")} />
<div>
<Form
onSubmit={async (formData) => {
const name = formData.get("name") as string;
if ((await this.props.api.getPolicies()).includes(name)) {
setErrorText(i18next.t("policy_new_already_exists"));
return;
}
try {
await this.props.api.createOrUpdatePolicy(name, " ");
route(policyViewURL(name));
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}}
>
<Form onSubmit={async (formData) => await this.onFormSubmit(formData)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
@ -40,7 +53,11 @@ export class PolicyNew extends Component<DefaultPageProps> {
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</Form>
</div>

View file

@ -5,6 +5,7 @@ import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { Margin } from "../../elements/Margin";
import { PageTitle } from "../../elements/PageTitle";
import { policyDeleteURL, policyEditURL } from "../pageLinks";
import { sendErrorNotification } from "../../elements/ErrorMessage";
import i18next from "i18next";
export class PolicyView extends Component<
@ -13,11 +14,16 @@ export class PolicyView extends Component<
> {
async componentDidMount() {
const policyName = this.props.matches["policyName"];
const policy = await this.props.api.getPolicy(policyName);
this.setState({
policy,
policyName,
});
try {
const policy = await this.props.api.getPolicy(policyName);
this.setState({
policy,
policyName,
});
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -61,6 +61,7 @@ export class PasswordGenerator extends Component<DefaultPageProps, PasswordGener
alphabet: passwordOptionsDefault.alphabet,
};
}
passwordLengthSlider = createRef<HTMLInputElement>();
alphabetSelector = createRef<HTMLSelectElement>();

View file

@ -1,14 +1,17 @@
import { Button } from "../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage } from "../../elements/ErrorMessage";
import { Form } from "../../elements/Form";
import { Margin } from "../../elements/Margin";
import { MarginInline } from "../../elements/MarginInline";
import { PageTitle } from "../../elements/PageTitle";
import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next";
export class DeleteSecretsEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -22,7 +25,9 @@ export class DeleteSecretsEngine extends Component<DefaultPageProps> {
>
<p>{i18next.t("delete_secrets_engine_message")}</p>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_delete")} color="danger" type="submit" />
@ -38,7 +43,7 @@ export class DeleteSecretsEngine extends Component<DefaultPageProps> {
route("/secrets");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,13 +1,17 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { kvListURL, kvViewURL } from "../../pageLinks";
import { route } from "preact-router";
import i18next from "i18next";
export class KeyValueDelete extends Component<DefaultPageProps> {
render() {
errorMessageRef = createRef<ErrorMessage>();
async onDelete() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const item = this.props.matches["item"];
@ -20,6 +24,20 @@ export class KeyValueDelete extends Component<DefaultPageProps> {
? kvListURL(baseMount, secretPath)
: kvViewURL(baseMount, secretPath, item, "null");
try {
await this.props.api.deleteSecret(baseMount, secretPath, item, version);
route(buttonRoute);
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
render() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const item = this.props.matches["item"];
return (
<>
<SecretTitleElement
@ -31,13 +49,15 @@ export class KeyValueDelete extends Component<DefaultPageProps> {
/>
<div>
<h5>{i18next.t("kv_delete_text")}</h5>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button
text={i18next.t("common_delete")}
color="danger"
onClick={async () => {
await this.props.api.deleteSecret(baseMount, secretPath, item, version);
route(buttonRoute);
}}
onClick={async () => this.onDelete()}
/>
</div>
</>

View file

@ -2,7 +2,9 @@ import { Button } from "../../../elements/Button";
import { CodeEditor } from "../../../elements/CodeEditor";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../../elements/ErrorMessage";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import {
SupportedLanguages,
@ -10,7 +12,6 @@ import {
parseData,
toPrismCode,
} from "../../../../utils/dataInterchange";
import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next";
@ -41,30 +42,18 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
};
}
async editorSave(): Promise<void> {
if (!this.state.dataLoaded) return;
const editorContent = this.state.code;
errorMessageRef = createRef<ErrorMessage>();
try {
parseData(editorContent, this.state.syntax);
} catch {
setErrorText(i18next.t("kv_sec_edit_invalid_data_err"));
return;
async componentDidMount() {
this.setState({ syntax: this.props.settings.kvEditorDefaultLanguage });
if (!this.state.dataLoaded) {
try {
await this.loadData();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
await this.props.api.createOrUpdateSecret(
this.props.baseMount,
this.props.secretPath.map((e) => e + "/"),
this.props.secretItem,
parseData(editorContent, this.state.syntax),
);
window.history.back();
}
onCodeUpdate(code: string): void {
this.setState({
code: code,
});
}
async loadData() {
@ -80,10 +69,28 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
});
}
async componentDidMount() {
this.setState({ syntax: this.props.settings.kvEditorDefaultLanguage });
if (!this.state.dataLoaded) {
await this.loadData();
async editorSave(): Promise<void> {
if (!this.state.dataLoaded) return;
const editorContent = this.state.code;
try {
parseData(editorContent, this.state.syntax);
} catch {
this.errorMessageRef.current.setErrorMessage(i18next.t("kv_sec_edit_invalid_data_err"));
return;
}
try {
await this.props.api.createOrUpdateSecret(
this.props.baseMount,
this.props.secretPath.map((e) => e + "/"),
this.props.secretItem,
parseData(editorContent, this.state.syntax),
);
window.history.back();
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
@ -123,13 +130,20 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
})}
</select>
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<CodeEditor
language={toPrismCode(this.state.syntax)}
tabSize={this.props.settings.kvEditorIndent}
code={this.getStringKVData(this.state.kvData)}
onUpdate={(code) => this.onCodeUpdate(code)}
onUpdate={(code) => {
this.setState({ code });
}}
/>
<Button text={i18next.t("common_edit")} color="primary" onClick={() => this.editorSave()} />
</div>
);

View file

@ -5,7 +5,7 @@ import { DoesNotExistError } from "../../../../types/internalErrors";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next";
export type KVKeysListProps = DefaultPageProps & {
@ -47,6 +47,15 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
};
}
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);
@ -64,7 +73,7 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
return;
}
} else {
setErrorText(error.message);
throw error;
}
this.setState({
@ -86,10 +95,6 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
}
}
componentDidMount(): void {
void this.loadData();
}
searchBarRef = createRef();
render(): JSX.Element {
@ -143,15 +148,20 @@ export class KeyValueList extends Component<DefaultPageProps, KeyValueListState>
const mountsPath = "/sys/mounts/" + baseMount;
const currentPath = baseMount + secretPath.join();
const caps = await this.props.api.getCapabilitiesPath([mountsPath, currentPath]);
const mount = await this.props.api.getMount(baseMount);
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,
});
this.setState({
mountCaps: caps[mountsPath],
pathCaps: caps[currentPath],
mountType: mount.type,
});
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -1,15 +1,41 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { kvViewURL } from "../../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class KeyValueNew extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
async newKVSecretHandleForm(
formData: FormData,
baseMount: string,
secretPath: string[],
): Promise<void> {
const path = formData.get("path") as string;
let keyData: Record<string, unknown> = {};
try {
const mountInfo = await this.props.api.getMount(baseMount);
if (mountInfo.options.version == "1") {
// Can't have a empty secret on KV V1
keyData = { placeholder_on_kv1: "placeholder_on_kv1" };
}
await this.props.api.createOrUpdateSecret(baseMount, secretPath, path, keyData);
route(kvViewURL(baseMount, secretPath, path));
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
render() {
const baseMount = this.props.matches["baseMount"];
const secretPath = (this.props.matches["secretPath"] || "").split("/");
@ -36,35 +62,15 @@ export class KeyValueNew extends Component<DefaultPageProps> {
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</Form>
</div>
</>
);
}
async newKVSecretHandleForm(
formData: FormData,
baseMount: string,
secretPath: string[],
): Promise<void> {
const path = formData.get("path") as string;
let keyData: Record<string, unknown> = {};
const mountInfo = await this.props.api.getMount(baseMount);
if (mountInfo.options.version == "1") {
// Can't have a empty secret on KV V1
keyData = { placeholder_on_kv1: "placeholder_on_kv1" };
}
try {
await this.props.api.createOrUpdateSecret(baseMount, secretPath, path, keyData);
route(kvViewURL(baseMount, secretPath, path));
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}
}

View file

@ -4,6 +4,7 @@ import { SecretTitleElement } from "../SecretTitleElement";
import { kvViewURL } from "../../pageLinks";
import { objectToMap } from "../../../../utils";
import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
export class KeyValueVersions extends Component<DefaultPageProps, { versions: string[] }> {
async componentDidMount() {
@ -11,11 +12,16 @@ export class KeyValueVersions extends Component<DefaultPageProps, { versions: st
const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"];
const metadata = await this.props.api.getSecretMetadata(baseMount, secretPath, secretItem);
try {
const metadata = await this.props.api.getSecretMetadata(baseMount, secretPath, secretItem);
const versions = Array.from(objectToMap(metadata.versions).keys());
const versions = Array.from(objectToMap(metadata.versions).keys());
this.setState({ versions });
this.setState({ versions });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -9,6 +9,7 @@ import { InputWithTitle } from "../../../elements/InputWithTitle";
import { SecretTitleElement } from "../SecretTitleElement";
import { SupportedLanguages, dumpData, toPrismCode } from "../../../../utils/dataInterchange";
import { kvDeleteURL, kvEditURL, kvVersionsURL } from "../../pageLinks";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next";
@ -54,7 +55,7 @@ export class KVSecretNormalVew extends Component<KVSecretViewDataProps> {
{Array.from(this.props.data).map((data: [string, unknown]) => {
const key = data[0];
const value = data[1] as string;
console.log(this.props.settings.kvHideKeyValues);
return (
<Grid size={GridSizes.NORMAL}>
<CopyableInputBox text={key} copyable />
@ -105,6 +106,15 @@ type KeyValueViewState = {
export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState> {
async componentDidMount() {
try {
await this.getKVViewData();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
async getKVViewData() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"];
@ -129,6 +139,8 @@ export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState>
} catch (e) {
if (e == DoesNotExistError) {
secretInfo = null;
} else {
throw e;
}
}
} else {

View file

@ -1,16 +1,18 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle";
import { kvListURL } from "../../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class NewKVEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -35,7 +37,11 @@ export class NewKVEngine extends Component<DefaultPageProps> {
</option>
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline>
@ -59,7 +65,7 @@ export class NewKVEngine extends Component<DefaultPageProps> {
route(kvListURL(name, []));
} catch (e) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,16 +1,18 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { totpListURL } from "../../pageLinks";
import i18next from "i18next";
export class NewTOTPEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -25,7 +27,11 @@ export class NewTOTPEngine extends Component<DefaultPageProps> {
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline>
@ -46,7 +52,7 @@ export class NewTOTPEngine extends Component<DefaultPageProps> {
route(totpListURL(name));
} catch (e) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,15 +1,17 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class NewTransitEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -24,7 +26,11 @@ export class NewTransitEngine extends Component<DefaultPageProps> {
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline>
@ -45,7 +51,7 @@ export class NewTransitEngine extends Component<DefaultPageProps> {
route("/secrets/transit/list/" + name + "/");
} catch (e) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -3,6 +3,7 @@ import { Component, JSX } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { MountType } from "../../../api/types/mount";
import { PageTitle } from "../../elements/PageTitle";
import { sendErrorNotification } from "../../elements/ErrorMessage";
import { sortedObjectMap } from "../../../utils";
import i18next from "i18next";
@ -57,14 +58,19 @@ type SecretsState = {
export class Secrets extends Component<DefaultPageProps, SecretsState> {
async componentDidMount() {
const mountsCapabilities = await this.props.api.getCapsPath("/sys/mounts");
const mounts = await this.props.api.getMounts();
// sort it by secretPath so it's in alphabetical order consistantly.
const mountsMap = sortedObjectMap(mounts);
this.setState({
capabilities: mountsCapabilities,
mountsMap: mountsMap as Map<string, MountType>,
});
try {
const mountsCapabilities = await this.props.api.getCapsPath("/sys/mounts");
const mounts = await this.props.api.getMounts();
// sort it by secretPath so it's in alphabetical order consistantly.
const mountsMap = sortedObjectMap(mounts);
this.setState({
capabilities: mountsCapabilities,
mountsMap: mountsMap as Map<string, MountType>,
});
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -1,12 +1,16 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router";
import { totpListURL } from "../../pageLinks";
import i18next from "i18next";
export class TOTPDelete extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() {
return (
<>
@ -18,14 +22,24 @@ export class TOTPDelete extends Component<DefaultPageProps> {
/>
<div>
<h5>{i18next.t("totp_delete_text")}</h5>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button
text={i18next.t("common_delete")}
color="danger"
onClick={async () => {
const baseMount = this.props.matches["baseMount"];
const item = this.props.matches["item"];
await this.props.api.deleteTOTP(baseMount, item);
route(totpListURL(baseMount));
try {
await this.props.api.deleteTOTP(baseMount, item);
route(totpListURL(baseMount));
} catch (e: unknown) {
const error = e as Error;
this.errorMessageRef.current.setErrorMessage(error.message);
}
}}
/>
</div>

View file

@ -3,7 +3,6 @@ import { CapabilitiesType } from "../../../../api/types/capabilities";
import { Component, JSX } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Grid, GridSizes } from "../../../elements/Grid";
import { MarginInline } from "../../../elements/MarginInline";
import { SecretTitleElement } from "../SecretTitleElement";
@ -14,7 +13,7 @@ import {
totpNewURL,
} from "../../pageLinks";
import { removeDoubleSlash } from "../../../../utils";
import { setErrorText } from "../../../../pageUtils";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next";
type TOTPGridItemProps = DefaultPageProps & {
@ -87,42 +86,36 @@ export class TOTPList extends Component<DefaultPageProps, TOTPListState> {
refresher: number;
async componentDidMount() {
async doApiFetches() {
const api = this.props.api;
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const caps = await api.getCapabilitiesPath([mountsPath, baseMount]);
let totpItems: TOTPItem[] = [];
try {
const totpKeys = await api.getTOTPKeys(baseMount);
const totpKeys = await api.getTOTPKeys(baseMount);
const totpKeyPermissions = await Promise.all(
Array.from(
totpKeys.map(async (key) => {
const totpCaps = await api.getCapsPath(removeDoubleSlash(baseMount + "/code/" + key));
return { key: key, caps: totpCaps };
}),
),
);
totpItems = Array.from(
totpKeyPermissions.map((keyData) => {
// Filter out all non-readable totp keys.
if (!keyData.caps.includes("read")) return;
return {
totpKey: keyData.key,
canDelete: keyData.caps.includes("delete"),
};
const totpKeyPermissions = await Promise.all(
Array.from(
totpKeys.map(async (key) => {
const totpCaps = await api.getCapsPath(removeDoubleSlash(baseMount + "/code/" + key));
return { key: key, caps: totpCaps };
}),
);
} catch (e: unknown) {
const error = e as Error;
if (error != DoesNotExistError) {
setErrorText(error.message);
}
}
),
);
totpItems = Array.from(
totpKeyPermissions.map((keyData) => {
// Filter out all non-readable totp keys.
if (!keyData.caps.includes("read")) return;
return {
totpKey: keyData.key,
canDelete: keyData.caps.includes("delete"),
};
}),
);
this.setState({
capabilities: caps,
@ -130,6 +123,15 @@ export class TOTPList extends Component<DefaultPageProps, TOTPListState> {
});
}
async componentDidMount() {
try {
await this.doApiFetches();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
if (!this.state.capabilities) return;
const baseMount = this.props.matches["baseMount"];

View file

@ -1,13 +1,13 @@
import { Button } from "../../../elements/Button";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { QRScanner } from "../../../elements/QRScanner";
import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
function replaceAll(str: string, replace: string, replaceWith: string): string {
@ -31,6 +31,7 @@ export class TOTPNewForm extends Component<
};
}
errorMessageRef = createRef<ErrorMessage>();
uriInputRef = createRef<HTMLInputElement>();
async onSubmit(data: FormData): Promise<void> {
@ -46,7 +47,7 @@ export class TOTPNewForm extends Component<
route("/secrets/totp/list/" + this.props.baseMount);
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
@ -86,6 +87,8 @@ export class TOTPNewForm extends Component<
/>
</Margin>
{/* TODO: please redo this to be more like Unseal page qr mode */}
{this.state.qrMode && (
<QRScanner
onScan={(uri) => {
@ -109,7 +112,9 @@ export class TOTPNewForm extends Component<
/>
</MarginInline>
<p id="errorText" class="uk-text-danger" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />

View file

@ -2,6 +2,7 @@ import { Button } from "../../../elements/Button";
import { Component, JSX, createRef } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
@ -9,7 +10,6 @@ import { MarginInline } from "../../../elements/MarginInline";
import { NewTOTPResp } from "../../../../api/types/totp";
import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { totpListURL } from "../../pageLinks";
import i18next from "i18next";
@ -17,6 +17,7 @@ export class TOTPNewGeneratedForm extends Component<
{ baseMount: string } & DefaultPageProps,
{ exportedData: NewTOTPResp }
> {
errorMessageRef = createRef<ErrorMessage>();
uriInputRef = createRef<HTMLInputElement>();
render(): JSX.Element {
@ -93,7 +94,9 @@ export class TOTPNewGeneratedForm extends Component<
</InputWithTitle>
</Margin>
<p id="errorText" class="uk-text-danger" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
@ -140,7 +143,7 @@ export class TOTPNewGeneratedForm extends Component<
}
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,17 +1,19 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: string }> {
errorMessageRef = createRef<ErrorMessage>();
render() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
@ -43,7 +45,11 @@ export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: str
<InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_decrypt")} color="primary" type="submit" />
</Form>
</>
@ -74,6 +80,7 @@ export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: str
const ciphertext_file = data.get("ciphertext_file") as File;
if (ciphertext_file.size > 0) {
// TODO: please stop using atob
ciphertext = atob(
(await fileToBase64(ciphertext_file)).replace("data:text/plain;base64,", ""),
);
@ -92,7 +99,7 @@ export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: str
this.setState({ plaintext: plaintext });
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,17 +1,19 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: string }> {
errorMessageRef = createRef<ErrorMessage>();
render() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
@ -43,7 +45,11 @@ export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: st
<InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_encrypt")} color="primary" type="submit" />
</Form>
</>
@ -87,7 +93,7 @@ export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: st
this.setState({ ciphertext: res.ciphertext });
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -5,6 +5,7 @@ import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks";
import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next";
type TransitViewListState = {
@ -26,25 +27,21 @@ export class TransitViewListItem extends Component<
timer: unknown;
getTransitKeys(): void {
void this.props.api
.getTransitKeys(this.props.baseMount)
.then((keys) => {
this.setState({
contentLoaded: true,
transitKeysList: keys,
});
})
.catch((_) => {
this.setState({
contentLoaded: true,
transitKeysList: [],
});
});
async getTransitKeys() {
const transitKeys = await this.props.api.getTransitKeys(this.props.baseMount);
this.setState({
contentLoaded: true,
transitKeysList: transitKeys,
});
}
componentDidMount(): void {
this.getTransitKeys();
async componentDidMount() {
try {
await this.getTransitKeys();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render(): JSX.Element {
@ -79,8 +76,13 @@ export class TransitList extends Component<DefaultPageProps, { caps: Capabilitie
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const caps = await this.props.api.getCapabilitiesPath([mountsPath, baseMount]);
this.setState({ caps });
try {
const caps = await this.props.api.getCapabilitiesPath([mountsPath, baseMount]);
this.setState({ caps });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -1,21 +1,21 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { transitViewSecretURL } from "../../pageLinks";
import i18next from "i18next";
export class TransitNew extends Component<DefaultPageProps> {
constructor() {
super();
}
errorMessageRef = createRef<ErrorMessage>();
render() {
const baseMount = this.props.matches["baseMount"];
return (
<>
<SecretTitleElement
@ -57,7 +57,11 @@ export class TransitNew extends Component<DefaultPageProps> {
))}
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline>
@ -80,7 +84,7 @@ export class TransitNew extends Component<DefaultPageProps> {
route(transitViewSecretURL(baseMount, name));
} catch (e) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -1,13 +1,13 @@
import { Button } from "../../../elements/Button";
import { Component } from "preact";
import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { TransitKeyType } from "../../../../api/types/transit";
import { objectToMap } from "../../../../utils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
type versionOption = { version: string; label: string };
@ -18,12 +18,19 @@ type TransitRewrapState = {
};
export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapState> {
errorMessageRef = createRef<ErrorMessage>();
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
this.setState({
transitKey: await this.props.api.getTransitKey(baseMount, secretItem),
});
try {
const transitKey = await this.props.api.getTransitKey(baseMount, secretItem);
this.setState({ transitKey });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {
@ -83,7 +90,11 @@ export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapStat
placeholder={i18next.t("transit_rewrap_input_placeholder")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_rewrap")} color="primary" type="submit" />
</Form>
</>
@ -115,7 +126,7 @@ export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapStat
this.setState({ ciphertext: res.ciphertext });
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
}

View file

@ -5,6 +5,7 @@ import { SecretTitleElement } from "../SecretTitleElement";
import { Tile } from "../../../elements/Tile";
import { TransitKeyType } from "../../../../api/types/transit";
import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import {
transitDecryptSecretURL,
transitEncryptSecretURL,
@ -16,8 +17,14 @@ export class TransitView extends Component<DefaultPageProps, { transitKey: Trans
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const transitKey = await this.props.api.getTransitKey(baseMount, secretItem);
this.setState({ transitKey });
try {
const transitKey = await this.props.api.getTransitKey(baseMount, secretItem);
this.setState({ transitKey });
} catch (e) {
const error = e as Error;
sendErrorNotification(error.message);
}
}
render() {

View file

@ -14,9 +14,6 @@ import { route } from "preact-router";
import i18next from "i18next";
export class SetLanguage extends Component<DefaultPageProps> {
constructor() {
super();
}
render() {
return (
<>

View file

@ -22,7 +22,7 @@ export class SetVaultURL extends Component<DefaultPageProps> {
required
/>
</Margin>
<p id="errorText" class="uk-text-danger" />
<Margin>
<Button text={i18next.t("set_vault_url_set_btn")} color="primary" type="submit" />
</Margin>

View file

@ -1,11 +1,12 @@
import { Button } from "../elements/Button";
import { Component, JSX } from "preact";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Margin } from "../elements/Margin";
import { PageTitle } from "../elements/PageTitle";
import { UnsealForm } from "./Unseal_Form";
import { UnsealQR } from "./Unseal_QR";
import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import { toStr } from "../../utils";
import i18next from "i18next";
@ -38,7 +39,7 @@ export class Unseal extends Component<DefaultPageProps, UnsealPageState> {
this.updateStateWithSealStatus();
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
this.errorMessageRef.current.setErrorMessage(error.message);
}
}
@ -66,6 +67,8 @@ export class Unseal extends Component<DefaultPageProps, UnsealPageState> {
this.setState({ timer: timer });
}
errorMessageRef = createRef<ErrorMessage>();
render(): JSX.Element {
return (
<>
@ -77,7 +80,9 @@ export class Unseal extends Component<DefaultPageProps, UnsealPageState> {
max={this.state.keys_needed}
/>
<p id="errorText" class="uk-text-danger uk-margin-top" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<p>
{i18next.t("unseal_keys_progress", {