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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,14 +4,21 @@ import { DefaultPageProps } from "../../../../../types/DefaultPageProps";
import { Margin } from "../../../../elements/Margin"; import { Margin } from "../../../../elements/Margin";
import { PageTitle } from "../../../../elements/PageTitle"; import { PageTitle } from "../../../../elements/PageTitle";
import { route } from "preact-router"; import { route } from "preact-router";
import { sendErrorNotification } from "../../../../elements/ErrorMessage";
import { userPassUserNewURL, userPassUserViewURL } from "../../../pageLinks"; import { userPassUserNewURL, userPassUserViewURL } from "../../../pageLinks";
import i18next from "i18next"; import i18next from "i18next";
export class UserPassUsersList extends Component<DefaultPageProps, { users: string[] }> { export class UserPassUsersList extends Component<DefaultPageProps, { users: string[] }> {
async componentDidMount() { async componentDidMount() {
const baseMount = this.props.matches["baseMount"]; 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() { render() {

View file

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

View file

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

View file

@ -1,14 +1,16 @@
import { Button } from "../elements/Button"; import { Button } from "../elements/Button";
import { Component, JSX } from "preact"; import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps"; import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Form } from "../elements/Form"; import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin"; import { Margin } from "../elements/Margin";
import { MarginInline } from "../elements/MarginInline"; import { MarginInline } from "../elements/MarginInline";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class TokenLoginForm extends Component<DefaultPageProps> { export class TokenLoginForm extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render(): JSX.Element { render(): JSX.Element {
return ( return (
<Form onSubmit={(data) => this.onSubmit(data)}> <Form onSubmit={(data) => this.onSubmit(data)}>
@ -22,6 +24,11 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("log_in_btn")} color="primary" type="submit" /> <Button text={i18next.t("log_in_btn")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -31,6 +38,9 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
const token = data.get("token"); const token = data.get("token");
const previousToken = this.props.settings.token;
this.props.settings.token = token as string; this.props.settings.token = token as string;
try { try {
@ -39,11 +49,18 @@ export class TokenLoginForm extends Component<DefaultPageProps> {
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
document.querySelector("#tokenInput").classList.add("uk-form-danger"); document.querySelector("#tokenInput").classList.add("uk-form-danger");
let errorMessage: string;
if (error.message == "permission denied") { if (error.message == "permission denied") {
setErrorText(i18next.t("log_in_token_login_error")); errorMessage = i18next.t("log_in_token_login_error");
} else { } 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 { Button } from "../elements/Button";
import { Component, JSX } from "preact"; import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps"; import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Form } from "../elements/Form"; import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin"; import { Margin } from "../elements/Margin";
import { MarginInline } from "../elements/MarginInline"; import { MarginInline } from "../elements/MarginInline";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class UsernameLoginForm extends Component<DefaultPageProps> { export class UsernameLoginForm extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render(): JSX.Element { render(): JSX.Element {
return ( return (
<Form onSubmit={(data) => this.onSubmit(data)}> <Form onSubmit={(data) => this.onSubmit(data)}>
@ -32,6 +34,11 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("log_in_btn")} color="primary" type="submit" /> <Button text={i18next.t("log_in_btn")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -40,6 +47,8 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
} }
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
const previousToken = this.props.settings.token;
try { try {
const res = await this.props.api.usernameLogin( const res = await this.props.api.usernameLogin(
data.get("username") as string, data.get("username") as string,
@ -51,7 +60,8 @@ export class UsernameLoginForm extends Component<DefaultPageProps> {
const error = e as Error; const error = e as Error;
document.querySelector("#usernameInput").classList.add("uk-form-danger"); document.querySelector("#usernameInput").classList.add("uk-form-danger");
document.querySelector("#passwordInput").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 { Button } from "../elements/Button";
import { Component, JSX, createRef } from "preact"; import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps"; import { DefaultPageProps } from "../../types/DefaultPageProps";
import { ErrorMessage } from "../elements/ErrorMessage";
import { Margin } from "../elements/Margin";
import { PageTitle } from "../elements/PageTitle"; import { PageTitle } from "../elements/PageTitle";
import { addClipboardNotifications, setErrorText } from "../../pageUtils"; import { addClipboardNotifications } from "../../pageUtils";
import { route } from "preact-router"; import { route } from "preact-router";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import i18next from "i18next"; import i18next from "i18next";
@ -36,9 +38,7 @@ export class Me extends Component<DefaultPageProps, MeState> {
this.state = this.defaultState; this.state = this.defaultState;
} }
componentWillUnmount() { errorMessageRef = createRef<ErrorMessage>();
this.setState(this.defaultState);
}
async componentDidMount() { async componentDidMount() {
let canSealVault = false; 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 { render(): JSX.Element {
return ( return (
this.state.loaded && ( this.state.loaded && (
<> <>
<PageTitle title={i18next.t("me_page_title")} /> <PageTitle title={i18next.t("me_page_title")} />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<ul class="uk-nav"> <ul class="uk-nav">
<li> <li>
<a <a
@ -84,7 +91,7 @@ export class Me extends Component<DefaultPageProps, MeState> {
route("/"); route("/");
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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> <li>
<a <a
onClick={async () => { onClick={async () => {
await this.props.api.sealVault(); try {
route("/unseal", true); 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")} {i18next.t("me_seal_vault_btn")}

View file

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

View file

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

View file

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

View file

@ -1,37 +1,50 @@
import { Button } from "../../elements/Button"; import { Button } from "../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage } from "../../elements/ErrorMessage";
import { Form } from "../../elements/Form"; import { Form } from "../../elements/Form";
import { Margin } from "../../elements/Margin"; import { Margin } from "../../elements/Margin";
import { PageTitle } from "../../elements/PageTitle"; import { PageTitle } from "../../elements/PageTitle";
import { policyViewURL } from "../pageLinks"; import { policyViewURL } from "../pageLinks";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class PolicyNew extends Component<DefaultPageProps> { 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() { render() {
return ( return (
<> <>
<PageTitle title={i18next.t("policy_new_title")} /> <PageTitle title={i18next.t("policy_new_title")} />
<div> <div>
<Form <Form onSubmit={async (formData) => await this.onFormSubmit(formData)}>
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);
}
}}
>
<Margin> <Margin>
<input <input
class="uk-input uk-form-width-medium" class="uk-input uk-form-width-medium"
@ -40,7 +53,11 @@ export class PolicyNew extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</Form> </Form>
</div> </div>

View file

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

View file

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

View file

@ -1,14 +1,17 @@
import { Button } from "../../elements/Button"; import { Button } from "../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { ErrorMessage } from "../../elements/ErrorMessage";
import { Form } from "../../elements/Form"; import { Form } from "../../elements/Form";
import { Margin } from "../../elements/Margin";
import { MarginInline } from "../../elements/MarginInline"; import { MarginInline } from "../../elements/MarginInline";
import { PageTitle } from "../../elements/PageTitle"; import { PageTitle } from "../../elements/PageTitle";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class DeleteSecretsEngine extends Component<DefaultPageProps> { export class DeleteSecretsEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
return ( return (
<> <>
@ -22,7 +25,9 @@ export class DeleteSecretsEngine extends Component<DefaultPageProps> {
> >
<p>{i18next.t("delete_secrets_engine_message")}</p> <p>{i18next.t("delete_secrets_engine_message")}</p>
<p class="uk-text-danger" id="errorText" /> <Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_delete")} color="danger" type="submit" /> <Button text={i18next.t("common_delete")} color="danger" type="submit" />
@ -38,7 +43,7 @@ export class DeleteSecretsEngine extends Component<DefaultPageProps> {
route("/secrets"); route("/secrets");
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { kvListURL, kvViewURL } from "../../pageLinks"; import { kvListURL, kvViewURL } from "../../pageLinks";
import { route } from "preact-router"; import { route } from "preact-router";
import i18next from "i18next"; import i18next from "i18next";
export class KeyValueDelete extends Component<DefaultPageProps> { export class KeyValueDelete extends Component<DefaultPageProps> {
render() { errorMessageRef = createRef<ErrorMessage>();
async onDelete() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/"); const secretPath = this.props.matches["secretPath"].split("/");
const item = this.props.matches["item"]; const item = this.props.matches["item"];
@ -20,6 +24,20 @@ export class KeyValueDelete extends Component<DefaultPageProps> {
? kvListURL(baseMount, secretPath) ? kvListURL(baseMount, secretPath)
: kvViewURL(baseMount, secretPath, item, "null"); : 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 ( return (
<> <>
<SecretTitleElement <SecretTitleElement
@ -31,13 +49,15 @@ export class KeyValueDelete extends Component<DefaultPageProps> {
/> />
<div> <div>
<h5>{i18next.t("kv_delete_text")}</h5> <h5>{i18next.t("kv_delete_text")}</h5>
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button <Button
text={i18next.t("common_delete")} text={i18next.t("common_delete")}
color="danger" color="danger"
onClick={async () => { onClick={async () => this.onDelete()}
await this.props.api.deleteSecret(baseMount, secretPath, item, version);
route(buttonRoute);
}}
/> />
</div> </div>
</> </>

View file

@ -2,7 +2,9 @@ import { Button } from "../../../elements/Button";
import { CodeEditor } from "../../../elements/CodeEditor"; import { CodeEditor } from "../../../elements/CodeEditor";
import { Component, JSX, createRef } from "preact"; import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../../elements/ErrorMessage";
import { InputWithTitle } from "../../../elements/InputWithTitle"; import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { import {
SupportedLanguages, SupportedLanguages,
@ -10,7 +12,6 @@ import {
parseData, parseData,
toPrismCode, toPrismCode,
} from "../../../../utils/dataInterchange"; } from "../../../../utils/dataInterchange";
import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap } from "../../../../utils"; import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next"; import i18next from "i18next";
@ -41,30 +42,18 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
}; };
} }
async editorSave(): Promise<void> { errorMessageRef = createRef<ErrorMessage>();
if (!this.state.dataLoaded) return;
const editorContent = this.state.code;
try { async componentDidMount() {
parseData(editorContent, this.state.syntax); this.setState({ syntax: this.props.settings.kvEditorDefaultLanguage });
} catch { if (!this.state.dataLoaded) {
setErrorText(i18next.t("kv_sec_edit_invalid_data_err")); try {
return; 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() { async loadData() {
@ -80,10 +69,28 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
}); });
} }
async componentDidMount() { async editorSave(): Promise<void> {
this.setState({ syntax: this.props.settings.kvEditorDefaultLanguage }); if (!this.state.dataLoaded) return;
if (!this.state.dataLoaded) { const editorContent = this.state.code;
await this.loadData();
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> </select>
</InputWithTitle> </InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<CodeEditor <CodeEditor
language={toPrismCode(this.state.syntax)} language={toPrismCode(this.state.syntax)}
tabSize={this.props.settings.kvEditorIndent} tabSize={this.props.settings.kvEditorIndent}
code={this.getStringKVData(this.state.kvData)} 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()} /> <Button text={i18next.t("common_edit")} color="primary" onClick={() => this.editorSave()} />
</div> </div>
); );

View file

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

View file

@ -1,15 +1,41 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { kvViewURL } from "../../pageLinks"; import { kvViewURL } from "../../pageLinks";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class KeyValueNew extends Component<DefaultPageProps> { 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() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretPath = (this.props.matches["secretPath"] || "").split("/"); const secretPath = (this.props.matches["secretPath"] || "").split("/");
@ -36,35 +62,15 @@ export class KeyValueNew extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</Form> </Form>
</div> </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 { kvViewURL } from "../../pageLinks";
import { objectToMap } from "../../../../utils"; import { objectToMap } from "../../../../utils";
import { route } from "preact-router"; import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
export class KeyValueVersions extends Component<DefaultPageProps, { versions: string[] }> { export class KeyValueVersions extends Component<DefaultPageProps, { versions: string[] }> {
async componentDidMount() { async componentDidMount() {
@ -11,11 +12,16 @@ export class KeyValueVersions extends Component<DefaultPageProps, { versions: st
const secretPath = this.props.matches["secretPath"].split("/"); const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"]; 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() { render() {

View file

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

View file

@ -1,16 +1,18 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle"; import { PageTitle } from "../../../elements/PageTitle";
import { kvListURL } from "../../pageLinks"; import { kvListURL } from "../../pageLinks";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class NewKVEngine extends Component<DefaultPageProps> { export class NewKVEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
return ( return (
<> <>
@ -35,7 +37,11 @@ export class NewKVEngine extends Component<DefaultPageProps> {
</option> </option>
</select> </select>
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -59,7 +65,7 @@ export class NewKVEngine extends Component<DefaultPageProps> {
route(kvListURL(name, [])); route(kvListURL(name, []));
} catch (e) { } catch (e) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle"; import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { totpListURL } from "../../pageLinks"; import { totpListURL } from "../../pageLinks";
import i18next from "i18next"; import i18next from "i18next";
export class NewTOTPEngine extends Component<DefaultPageProps> { export class NewTOTPEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
return ( return (
<> <>
@ -25,7 +27,11 @@ export class NewTOTPEngine extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -46,7 +52,7 @@ export class NewTOTPEngine extends Component<DefaultPageProps> {
route(totpListURL(name)); route(totpListURL(name));
} catch (e) { } catch (e) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { PageTitle } from "../../../elements/PageTitle"; import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class NewTransitEngine extends Component<DefaultPageProps> { export class NewTransitEngine extends Component<DefaultPageProps> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
return ( return (
<> <>
@ -24,7 +26,11 @@ export class NewTransitEngine extends Component<DefaultPageProps> {
required required
/> />
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -45,7 +51,7 @@ export class NewTransitEngine extends Component<DefaultPageProps> {
route("/secrets/transit/list/" + name + "/"); route("/secrets/transit/list/" + name + "/");
} catch (e) { } catch (e) {
const error = e as Error; 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 { DefaultPageProps } from "../../../types/DefaultPageProps";
import { MountType } from "../../../api/types/mount"; import { MountType } from "../../../api/types/mount";
import { PageTitle } from "../../elements/PageTitle"; import { PageTitle } from "../../elements/PageTitle";
import { sendErrorNotification } from "../../elements/ErrorMessage";
import { sortedObjectMap } from "../../../utils"; import { sortedObjectMap } from "../../../utils";
import i18next from "i18next"; import i18next from "i18next";
@ -57,14 +58,19 @@ type SecretsState = {
export class Secrets extends Component<DefaultPageProps, SecretsState> { export class Secrets extends Component<DefaultPageProps, SecretsState> {
async componentDidMount() { async componentDidMount() {
const mountsCapabilities = await this.props.api.getCapsPath("/sys/mounts"); try {
const mounts = await this.props.api.getMounts(); const mountsCapabilities = await this.props.api.getCapsPath("/sys/mounts");
// sort it by secretPath so it's in alphabetical order consistantly. const mounts = await this.props.api.getMounts();
const mountsMap = sortedObjectMap(mounts); // sort it by secretPath so it's in alphabetical order consistantly.
this.setState({ const mountsMap = sortedObjectMap(mounts);
capabilities: mountsCapabilities, this.setState({
mountsMap: mountsMap as Map<string, MountType>, capabilities: mountsCapabilities,
}); mountsMap: mountsMap as Map<string, MountType>,
});
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
} }
render() { render() {

View file

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

View file

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

View file

@ -1,13 +1,13 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component, JSX, createRef } from "preact"; import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { QRScanner } from "../../../elements/QRScanner"; import { QRScanner } from "../../../elements/QRScanner";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
function replaceAll(str: string, replace: string, replaceWith: string): string { function replaceAll(str: string, replace: string, replaceWith: string): string {
@ -31,6 +31,7 @@ export class TOTPNewForm extends Component<
}; };
} }
errorMessageRef = createRef<ErrorMessage>();
uriInputRef = createRef<HTMLInputElement>(); uriInputRef = createRef<HTMLInputElement>();
async onSubmit(data: FormData): Promise<void> { async onSubmit(data: FormData): Promise<void> {
@ -46,7 +47,7 @@ export class TOTPNewForm extends Component<
route("/secrets/totp/list/" + this.props.baseMount); route("/secrets/totp/list/" + this.props.baseMount);
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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> </Margin>
{/* TODO: please redo this to be more like Unseal page qr mode */}
{this.state.qrMode && ( {this.state.qrMode && (
<QRScanner <QRScanner
onScan={(uri) => { onScan={(uri) => {
@ -109,7 +112,9 @@ export class TOTPNewForm extends Component<
/> />
</MarginInline> </MarginInline>
<p id="errorText" class="uk-text-danger" /> <Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <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 { Component, JSX, createRef } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox"; import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle"; import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
@ -9,7 +10,6 @@ import { MarginInline } from "../../../elements/MarginInline";
import { NewTOTPResp } from "../../../../api/types/totp"; import { NewTOTPResp } from "../../../../api/types/totp";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { totpListURL } from "../../pageLinks"; import { totpListURL } from "../../pageLinks";
import i18next from "i18next"; import i18next from "i18next";
@ -17,6 +17,7 @@ export class TOTPNewGeneratedForm extends Component<
{ baseMount: string } & DefaultPageProps, { baseMount: string } & DefaultPageProps,
{ exportedData: NewTOTPResp } { exportedData: NewTOTPResp }
> { > {
errorMessageRef = createRef<ErrorMessage>();
uriInputRef = createRef<HTMLInputElement>(); uriInputRef = createRef<HTMLInputElement>();
render(): JSX.Element { render(): JSX.Element {
@ -93,7 +94,9 @@ export class TOTPNewGeneratedForm extends Component<
</InputWithTitle> </InputWithTitle>
</Margin> </Margin>
<p id="errorText" class="uk-text-danger" /> <Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
@ -140,7 +143,7 @@ export class TOTPNewGeneratedForm extends Component<
} }
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { FileUploadInput } from "../../../elements/FileUploadInput"; import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle"; import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils"; import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: string }> { export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: string }> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; 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")}> <InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" /> <input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle> </InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_decrypt")} color="primary" type="submit" /> <Button text={i18next.t("transit_decrypt")} color="primary" type="submit" />
</Form> </Form>
</> </>
@ -74,6 +80,7 @@ export class TransitDecrypt extends Component<DefaultPageProps, { plaintext: str
const ciphertext_file = data.get("ciphertext_file") as File; const ciphertext_file = data.get("ciphertext_file") as File;
if (ciphertext_file.size > 0) { if (ciphertext_file.size > 0) {
// TODO: please stop using atob
ciphertext = atob( ciphertext = atob(
(await fileToBase64(ciphertext_file)).replace("data:text/plain;base64,", ""), (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 }); this.setState({ plaintext: plaintext });
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { FileUploadInput } from "../../../elements/FileUploadInput"; import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle"; import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils"; import { fileToBase64 } from "../../../../htmlUtils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: string }> { export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: string }> {
errorMessageRef = createRef<ErrorMessage>();
render() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; 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")}> <InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" /> <input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle> </InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_encrypt")} color="primary" type="submit" /> <Button text={i18next.t("transit_encrypt")} color="primary" type="submit" />
</Form> </Form>
</> </>
@ -87,7 +93,7 @@ export class TransitEncrypt extends Component<DefaultPageProps, { ciphertext: st
this.setState({ ciphertext: res.ciphertext }); this.setState({ ciphertext: res.ciphertext });
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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 { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks"; import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks";
import { route } from "preact-router"; import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next"; import i18next from "i18next";
type TransitViewListState = { type TransitViewListState = {
@ -26,25 +27,21 @@ export class TransitViewListItem extends Component<
timer: unknown; timer: unknown;
getTransitKeys(): void { async getTransitKeys() {
void this.props.api const transitKeys = await this.props.api.getTransitKeys(this.props.baseMount);
.getTransitKeys(this.props.baseMount) this.setState({
.then((keys) => { contentLoaded: true,
this.setState({ transitKeysList: transitKeys,
contentLoaded: true, });
transitKeysList: keys,
});
})
.catch((_) => {
this.setState({
contentLoaded: true,
transitKeysList: [],
});
});
} }
componentDidMount(): void { async componentDidMount() {
this.getTransitKeys(); try {
await this.getTransitKeys();
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
} }
render(): JSX.Element { render(): JSX.Element {
@ -79,8 +76,13 @@ export class TransitList extends Component<DefaultPageProps, { caps: Capabilitie
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount; const mountsPath = "/sys/mounts/" + baseMount;
const caps = await this.props.api.getCapabilitiesPath([mountsPath, baseMount]); try {
this.setState({ caps }); const caps = await this.props.api.getCapabilitiesPath([mountsPath, baseMount]);
this.setState({ caps });
} catch (e: unknown) {
const error = e as Error;
sendErrorNotification(error.message);
}
} }
render() { render() {

View file

@ -1,21 +1,21 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline"; import { MarginInline } from "../../../elements/MarginInline";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { route } from "preact-router"; import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { transitViewSecretURL } from "../../pageLinks"; import { transitViewSecretURL } from "../../pageLinks";
import i18next from "i18next"; import i18next from "i18next";
export class TransitNew extends Component<DefaultPageProps> { export class TransitNew extends Component<DefaultPageProps> {
constructor() { errorMessageRef = createRef<ErrorMessage>();
super();
}
render() { render() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
return ( return (
<> <>
<SecretTitleElement <SecretTitleElement
@ -57,7 +57,11 @@ export class TransitNew extends Component<DefaultPageProps> {
))} ))}
</select> </select>
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<MarginInline> <MarginInline>
<Button text={i18next.t("common_create")} color="primary" type="submit" /> <Button text={i18next.t("common_create")} color="primary" type="submit" />
</MarginInline> </MarginInline>
@ -80,7 +84,7 @@ export class TransitNew extends Component<DefaultPageProps> {
route(transitViewSecretURL(baseMount, name)); route(transitViewSecretURL(baseMount, name));
} catch (e) { } catch (e) {
const error = e as Error; 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 { Button } from "../../../elements/Button";
import { Component } from "preact"; import { Component, createRef } from "preact";
import { CopyableBox } from "../../../elements/CopyableBox"; import { CopyableBox } from "../../../elements/CopyableBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { ErrorMessage, sendErrorNotification } from "../../../elements/ErrorMessage";
import { Form } from "../../../elements/Form"; import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin"; import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { TransitKeyType } from "../../../../api/types/transit"; import { TransitKeyType } from "../../../../api/types/transit";
import { objectToMap } from "../../../../utils"; import { objectToMap } from "../../../../utils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next"; import i18next from "i18next";
type versionOption = { version: string; label: string }; type versionOption = { version: string; label: string };
@ -18,12 +18,19 @@ type TransitRewrapState = {
}; };
export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapState> { export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapState> {
errorMessageRef = createRef<ErrorMessage>();
async componentDidMount() { async componentDidMount() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; 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() { render() {
@ -83,7 +90,11 @@ export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapStat
placeholder={i18next.t("transit_rewrap_input_placeholder")} placeholder={i18next.t("transit_rewrap_input_placeholder")}
/> />
</Margin> </Margin>
<p class="uk-text-danger" id="errorText" />
<Margin>
<ErrorMessage ref={this.errorMessageRef} />
</Margin>
<Button text={i18next.t("transit_rewrap")} color="primary" type="submit" /> <Button text={i18next.t("transit_rewrap")} color="primary" type="submit" />
</Form> </Form>
</> </>
@ -115,7 +126,7 @@ export class TransitRewrap extends Component<DefaultPageProps, TransitRewrapStat
this.setState({ ciphertext: res.ciphertext }); this.setState({ ciphertext: res.ciphertext });
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; 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 { Tile } from "../../../elements/Tile";
import { TransitKeyType } from "../../../../api/types/transit"; import { TransitKeyType } from "../../../../api/types/transit";
import { route } from "preact-router"; import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import { import {
transitDecryptSecretURL, transitDecryptSecretURL,
transitEncryptSecretURL, transitEncryptSecretURL,
@ -16,8 +17,14 @@ export class TransitView extends Component<DefaultPageProps, { transitKey: Trans
async componentDidMount() { async componentDidMount() {
const baseMount = this.props.matches["baseMount"]; const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"]; 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() { render() {

View file

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

View file

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

View file

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